了解如何在裸机上配置K3s,以运行一个Kubernetes集群,其弹性和容错性与管理服务一样多。
本教程是我在2017年发表的《裸机上的Kubernetes 10分钟》的后续教程。原文的重点是让Kubernetes在一些运行Ubuntu的裸机上工作,然后继续部署一个微服务和仪表盘。这是一篇入门文章,是为了帮助新用户试用Kubernetes而写的。它使用了生产就绪的kubeadm,但只使用了一个主节点,这意味着它不能容忍失败。
在这篇文章中,我想在裸机基础设施上为你提供一个HA、生产就绪的集群。我们将使用Equinix Metal作为主机,自动完成枯燥的部分,但其余部分将逐步完成,这样你就可以了解引擎盖下发生了什么。
在结论中,我们将回顾设置情况,并讨论在企业内部或在家庭实验室中使用树莓派斯运行的其他选择。我们还将把kube-vip与其他裸机和自我托管网络的解决方案进行比较。
概念图:由稳定的EIP提供的kubectl访问。IngressController也有一个EIP,为Ingress暴露的服务(如OpenFaaS网关)路由流量。
这篇文章也从kubeadm转向使用K3s(一个CNCF项目),它的控制平面需要更少的资源,并且有一个使用嵌入式etcd的内置HA模式。K3s自2019年以来已普遍可用(GA),并已准备好生产。
教程
我们将使用Terraform在Equinix 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 CLIk3sup
- 通过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_token
和project_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
,用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美元/月。
键入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年的最初文章涉及的内容更多,但它产生的集群是高度可用的,快速的,并准备运行生产工作负载。