使用K3s的裸机Kubernetes(附代码)

288 阅读11分钟

了解如何在裸机上配置K3s,以运行一个Kubernetes集群,其弹性和容错性与管理服务一样多。

本教程是我在2017年发表的《裸机上的Kubernetes 10分钟》的后续教程。原文的重点是让Kubernetes在一些运行Ubuntu的裸机上工作,然后继续部署一个微服务和仪表盘。这是一篇入门文章,是为了帮助新用户试用Kubernetes而写的。它使用了生产就绪的kubeadm,但只使用了一个主节点,这意味着它不能容忍失败。

在这篇文章中,我想在裸机基础设施上为你提供一个HA、生产就绪的集群。我们将使用Equinix Metal作为主机,自动完成枯燥的部分,但其余部分将逐步完成,这样你就可以了解引擎盖下发生了什么。

在结论中,我们将回顾设置情况,并讨论在企业内部或在家庭实验室中使用树莓派斯运行的其他选择。我们还将把kube-vip与其他裸机和自我托管网络的解决方案进行比较。

Bare-metal Kubernetes with K3s

概念图:由稳定的EIP提供的kubectl访问。IngressController也有一个EIP,为Ingress暴露的服务(如OpenFaaS网关)路由流量。

这篇文章也从kubeadm转向使用K3s(一个CNCF项目),它的控制平面需要更少的资源,并且有一个使用嵌入式etcd的内置HA模式。K3s自2019年以来已普遍可用(GA),并已准备好生产。

在Twitter上关注我 @alexellisuk

教程

我们将使用TerraformEquinix Metal(又称Packet)服务器上创建节点,使用K3s创建一个HA控制平面,使用kube-vip为API服务器配置一个HA IP地址。

该设置将能够容忍至少一个服务器的故障。因此,与2017年的帖子不同,我们的服务器将形成一个HA集群,并且还将配置一个弹性IP(EIP),以便在一个或多个服务器不可用时,Kubernetes API服务器将保持可访问。

在Kubernetes集群中,云控制器管理器插件有几个职责,包括节点管理、路由和管理服务。在教程结束时,您的集群不仅会有一个HA控制平面,一个稳定的API服务器IP,而且Equinix Metal的(CCM)还将允许您作为类型LoadBalancer暴露服务。每个 IP 地址的费用约为 3.25 美元/月,您可以在仪表板的 IP 地址部分找到更多细节。

技能水平:中级到高级。你应该熟悉Kubernetes、网络和公共云基础设施。

安装前提条件

下载arkade,一个可移植的Kubernetes市场和DevOps CLI的下载器。它将被用来下载我们在教程中需要的工具。当然,如果你愿意的话,也欢迎你用艰苦的方式来做事情。

curl -sLS https://dl.get-arkade.dev | sh

# Install the binary using the command given as output, such as: 
sudo cp arkade-darwin /usr/local/bin/arkade

# Or on Linux:
sudo cp arkade /usr/local/bin/arkade

下载以下CLIS:

  • kubectl - Kubernetes CLI
  • k3sup - 通过SSH工作的k3s安装程序
  • terraform - 基础设施即代码(IaC)工具,我们将用它来创建初始主机
  • kubectx - 检查和切换集群间Kubernetes上下文的快速方法
arkade get kubectl
arkade get k3sup
arkade get terraform
arkade get kubectx

按照步骤将这些二进制文件放入你的PATH中:

# Add (k3sup) to your PATH variable
export PATH=$PATH:$HOME/.arkade/bin/

配置你的节点

我们将使用terraform来配置三个服务器和两个代理。你可以改变这些数字,但三台是K3s使用etcd的内置HA模式所需的最低数量。

你可能会问,为什么整篇博文不只是一个Terraform命令。我可以听到你的Hacker News。这篇文章的重点不是为你做所有的工作,而是帮助你了解需要什么,以及以什么顺序。创建主机是很无聊的,所以我们要把它自动化,为你节省一些时间。

用以下方法保存main.tfvars:

terraform {
  required_providers {
    packet = {
      source = "terraform-providers/packet"
      version = "~> 3.2.1"
    }
  }
  required_version = ">= 0.13"
}

variable "api_token" {
  description = "Equinix Metal API token"
}

variable "project_id" {
  description = "Equinix Metal Project ID"
}

variable "servers" {
  description = "Count of servers"
}

variable "agents" {
  description = "Count of agents"
}

provider "packet" {
  auth_token=var.api_token
}

resource "packet_ssh_key" "key1" {
  name       = "k3sup-1"
  public_key = file("/home/alex/.ssh/id_rsa.pub")
}

resource "packet_device" "k3s-server" {
  count		         = var.servers
  hostname         = "k3s-server-${count.index+1}"
  plan             = "c1.small.x86"
  facilities       = ["ams1"]
  operating_system = "ubuntu_20_10"
  billing_cycle    = "hourly"
  project_id       = var.project_id
  depends_on       = [packet_ssh_key.key1]
}

resource "packet_device" "k3s-agent" {
  count 	         = var.agents
  hostname         = "k3s-agent-${count.index+1}"
  plan             = "c1.small.x86"
  facilities       = ["ams1"]
  operating_system = "ubuntu_20_10"
  billing_cycle    = "hourly"
  project_id       = var.project_id
  depends_on       = [packet_ssh_key.key1]
}

output "server_ips" {
  value = packet_device.k3s-server.*.access_public_ipv4
}

output "agent_ips" {
  value = packet_device.k3s-agent.*.access_public_ipv4
}

然后创建main.tfvars

api_token   =   ""
project_id  =   ""
servers     =   3
agents      =   2

用 Equinix Metal 仪表板上的值编辑api_tokenproject_id 。API 密钥可在您的用户资料中找到。

如果您想改变节点的大小,您可以编辑c1.small.x86 并使用一个不同的值。您可以在您的仪表板上找到这些选项。

现在运行terraform来创建主机:

terraform init
terraform plan -var-file=main.tfvars
terraform apply -var-file=main.tfvars

当主机被创建后,你会得到带有你的IP地址的输出。

agent_ips = [
  "147.75.81.203",
  "147.75.33.3",
]
server_ips = [
  "147.75.32.35",
  "147.75.80.83",
  "147.75.32.99",
]

打开一个终端,为每个主机写入环境变量:

export SERVER1=""
export SERVER2=""
export SERVER3=""

export AGENT1=""
export AGENT2=""

把这个文件保存为hosts.txt ,因为你以后可能还会需要它。

为控制面获得一个IP地址

K3s可以在HA模式下运行,主节点的故障可以被容忍。这对于面向公众的集群来说是不够的,在这种情况下,Kubernetes控制面需要一个稳定的IP地址。

我们需要一个用于6443端口的稳定IP,我们也可以称之为弹性IP或EIP。幸运的是,BGP可以帮助我们。我们三个主节点中的一个将公布其IP地址作为EIP的路由,然后如果它宕机,另一个将开始公布。这意味着我们的客户可以始终依赖一个稳定的地址:https://$EIP:6443 ,以连接到Kubernetes API服务器。

从你的EM仪表板上获得一个IP地址:

  • IPs & Networks

  • 请求IP地址

  • 选择你将用于节点的同一区域。

  • 挑选一个/32 ,用于控制平面的单一IP。

工程师可能需要一些时间来批准这个请求。

一旦你有了IP,就把它设置在一个环境变量中。

export EIP="147.75.100.237"

将这一行添加到hosts.txt ,这样你就有了它,如果你以后需要它。

为每个服务器启用 BGP

在 Equinix Metal 仪表板上单击每个服务器。单击其详细信息页面,然后单击 BGP。找到 IPv4 行上的管理按钮,单击 "启用 BGP"。

此设置允许从该服务器发布 BGP 广告。

配置第一个主服务器

由于各种原因,我们只能在K3s启动后设置kube-vip,所以我们将创建第一个主节点:

k3sup install \
  --ip $SERVER1 \
  --tls-san $EIP \
  --cluster \
  --k3s-channel latest \
  --k3s-extra-args "--no-deploy servicelb --disable-cloud-controller" \
  --merge \
  --local-path $HOME/.kube/config \
  --context=em-ha-k3s
  • --tls-san 是宣传EIP所必需的,这样K3s将为API服务器创建一个有效的证书。
  • --k3s-channel 是指定K3s的最新版本,在这个例子中是1.19,当你运行这个教程的时候,它可能已经改变了,在这种情况下,你可以给1.19 作为通道,或者给一个特定的版本。--k3s-version
  • 注意--cluster 标志,它告诉服务器使用 etcd 为我们以后要加入的服务器创建一个集群。
  • --local-path,--context--merge 都允许我们将K3s的KUBECONFIG合并到我们的本地文件中。
  • --k3s-extra-args ,我们禁用了内置的K3s服务负载平衡器,也禁用了云控制器,因为我们将使用Equinix Metal云控制器管理器来代替。

现在检查你的kubeconfig文件是否指向正确的集群:

kubectx em-ha-k3s

尝试查询集群中的节点:

[alex@nuc ~]$ kubectl get nodes
NAME           STATUS     ROLES         AGE   VERSION
k3s-server-1   Ready      etcd,master   7s    v1.19.5+k3s2

应用RBAC,使kube-vip能够访问Kubernetes API并配置服务:

kubectl apply -f https://kube-vip.io/manifests/rbac.yaml

现在用SSH登录第一台服务器。每台服务器都需要一个一次性的配置选项:

ssh root@$SERVER1

ctr image pull docker.io/plndr/kube-vip:0.3.0

alias kube-vip="ctr run --rm --net-host docker.io/plndr/kube-vip:0.3.0"
export INTERFACE=lo
export EIP="147.75.100.237"

kube-vip vip /kube-vip manifest daemonset \
  --interface $INTERFACE \
  --vip $EIP \
  --controlplane \
  --services \
  --inCluster \
  --taint \
  --bgp \
  --packet \
  --provider-config /etc/cloud-sa/cloud-sa.json | tee /var/lib/rancher/k3s/server/manifests/vip.yaml

kube-vip的清单将在/var/lib/rancher/k3s/server/manifests/vip.yaml ,任何放在这里的文件都将由K3s运行。

注销服务器

部署 Equinix Metal Cloud Controller Manager

现在部署Equinix Metal 云控制器管理器 (CCM),它将创建上述命令中提到的/etc/cloud-sa/cloud-sa.json 文件。

按照配置,CCM 将从 Equinix Metal API 中获取 IP 地址,用于任何暴露的服务。

按以下方式创建一个秘密:

export API_KEY=""
export PROJECT_ID=""

cat <<EOF > ccm-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: packet-cloud-config
  namespace: kube-system
stringData:
  cloud-sa.json: |
    {
      "apiKey": "$API_KEY",
      "projectID": "$PROJECT_ID"
    }
EOF

现在应用该秘密,然后将修改后的CCM与kube-vip一起使用:

kubectl apply -f ./ccm-secret.yaml
kubectl apply -f https://gist.githubusercontent.com/thebsdbox/c86dd970549638105af8d96439175a59/raw/4abf90fb7929ded3f7a201818efbb6164b7081f0/ccm.yaml

gist 包含 Equinix Metal CCM 的分叉版本。这是由kube-vip的作者开发的,并将被上传到上游,届时URL将重定向到CCM的原始 repo

检查CCM和kube-vip pods是否已经启动。

kubectl get pods -n kube-system -w

kube-vip-ds 和 ,应该是 "正在运行",并且没有错误。packet-cloud-controller-manager

配置额外的主服务器

现在我们已经有了第一台服务器,并且正在宣传它的EIP,我们可以用它来加入我们的其他服务器,以及后来使用EIP的代理。

这很重要,因为如果我们使用其中一台服务器的IP,而该服务器宕机了,代理将不再能够与API服务器通信:

k3sup join \
  --ip $SERVER2 \
  --server \
  --server-ip $EIP \
  --k3s-channel latest
  • --server - 告诉K3s作为一个服务器加入集群
  • --server-ip - 注意,我们在这里指定了EIP,而不是任何一台服务器的IP。

这个命令会写一个kubeconfig文件到你的本地目录,忽略它,继续使用合并到你的kubeconfig文件中的那个。

添加第三台服务器:

k3sup join \
  --ip $SERVER3 \
  --server \
  --server-ip $EIP \
  --k3s-channel latest

添加你的代理

要添加一个代理(或工作者)节点,使用上面的join命令,但删除--server 标志:

agents=("$AGENT1" "$AGENT2")

for i in ${agents[*]} 
do
    k3sup join \
    --ip $i \
    --server \
    --server-ip $EIP \
    --k3s-channel latest
done

转换你的KUBECONFIG以使用EIP

在你自己的电脑上,切换你的KUBECONFIG,使用EIP,而不是k3sup默认为你设置的SERVER1 IP。

编辑~/.kube/config ,用EIP值替换IPEIP值替换IP SERVER1。

在MacOS上:

export EIP="147.75.100.237"
export SERVER1="147.75.32.35"

sed -ie s/$SERVER1/$EIP/g ~/.kube/config

然后检查该文件是否指向EIP:

cat ~/.kube/config |grep $EIP
    server: https://147.75.100.237:6443

尝试使用kubectl ,并将新的 EIP 放置到位:

kubectl get nodes -o wide

部署工作负载

我们之前安装的arkade工具不仅可以安装CLI,而且是一个开源的Kubernetes市场。你可以在你的树莓派、你的Equinix Metal集群、工作中、你的笔记本电脑上使用它,无论你想在哪里使用。它提供了大约40个Kubernetes应用程序,并且是开源的,所以你可以分叉它并添加你自己的应用程序。

kube-vip 与 CCM 协同工作。CCM 将从 Equinix Metal 的 API 为您获得一个新的 IP 地址,然后将其分配给任何处于 Pending 状态的 LoadBalancer 服务。

你能找到的最快速的例子是来自inlets-operator的README

kubectl apply -f https://raw.githubusercontent.com/inlets/inlets-operator/master/contrib/nginx-sample-deployment.yaml
kubectl expose deployment nginx-1 --port=80 --type=LoadBalancer

IP会很快显示出来,在几秒钟之内。

kubectl get svc -o wide -w
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE    SELECTOR
kubernetes   ClusterIP      10.43.0.1     <none>        443/TCP        151m   <none>
nginx-1      LoadBalancer   10.43.71.39   <pending>     80:30822/TCP   3s     app=nginx
nginx-1      LoadBalancer   10.43.71.39   <pending>     80:30822/TCP   5s     app=nginx
nginx-1      LoadBalancer   10.43.71.39   147.75.80.22   80:30822/TCP   5s     app=nginx

NGINX_IP=$(kubectl get svc/nginx-1 -o jsonpath="{.spec.loadBalancerIP}")

curl -s http://$NGINX_IP | grep "<title>"
<title>Welcome to nginx!</title>

kubectl delete svc/nginx-1

要让Kubernetes仪表板启动和运行,只需运行:

arkade install kubernetes-dashboard

输出将告诉你如何获得一个令牌以及如何登录仪表盘。你可以在任何时候通过键入来获取这些信息:

arkade info kubernetes-dashboard

默认情况下,Traefik 1.x与K3s一起安装,你可以禁用或删除它,然后用你喜欢的IngressController安装。

kubectl delete svc/traefik -n kube-system
kubectl delete deploy/traefik -n kube-system

arkade已经为IngressController提供了四个选项。你也可以运行arkade get helm ,然后使用你喜欢的舵手图,如果下面还没有的话:

arkade install ingress-nginx
# Or
arkade install traefik2
# Or
arkade install nginx-inc
# Or
arkade install kong-ingress

添加cert-manager:

arkade install cert-manager

一旦你找到你的IngressController的IP地址,你就可以为你可能创建的任何ingress记录创建一个DNS记录,然后从LetsEncrypt免费获得TLS证书。

要安装OpenFaaS,有一个加密的TLS端点,运行:

export DOMAIN=gateway.example.com
arkade install openfaas
arkade install openfaas-ingress \
  --email webmaster@$DOMAIN \
  --domain $DOMAIN

在这个例子中,你需要使用你的IngressController的IP为gateway.example.com 创建一个DNS A记录。然后你可以继续登录OpenFaaS,或者在浏览器中使用它的URL:https://gateway.example.com

要托管一个Docker注册表,只需运行:

export DOMAIN=registry.example.com
arkade install docker-registry
arkade install docker-registry-ingress \
  --email webmaster@$DOMAIN \
  --domain $DOMAIN

为了让域名工作,你只需按照上面的方法创建DNS记录,registry.example.com ,并将其指向IngressController。

通过使用Ingress,你可以节省成本和IP地址,正如我在介绍中提到的,IP地址的成本约为3.25美元/月。

LinkerdIstio也只是一种命令方式。

键入arkade install --help ,以获得完整的列表。

总结

在本教程中,我们展示了在Equinix Metal的裸机云上使用时,kube-vip如何与BGP整合,以提供一个稳定的IP。

控制平面是HA,可以容忍至少一次故障,因为我们将K3s配置为使用其内置集群模式。内置集群模式使用嵌入式版本的etcd来同步服务器。

6443端口的API服务器被做成了HA,也能容忍故障。这是因为API服务器的IP地址是EIP,由BGP提供。我们更新了kubeconfig文件,我们额外的服务器和代理使用EIP作为集群加入的命令。

云控制器管理器也使用BGP为任何你想用Kubernetes LoadBalancer公开的服务提供一个IP地址。

虽然这篇博文花了很多时间来写,而且比2017年的最初文章涉及的内容更多,但它产生的集群是高度可用的,快速的,并准备运行生产工作负载。