vlambda博客
学习文章列表

自己搭建一个k8s环境


背景

Kubernetes 是时下流行的容器编排引擎,因为字母太多,且掐头去尾后剩下 8 个字母,于是被大家亲切的缩写为 k8s。

Kubernetes https://kubernetes.io/

另外所谓“云原生”概念火爆,各大云厂商也纷纷推出了自己的容器服务,例如阿里云的ACK、腾讯云的TKE、微软云的AKS等,无不是基于 Kubernetes 。

阿里云ACK https://www.aliyun.com/product/kubernetes
腾讯云TKE https://cloud.tencent.com/product/tke
微软云AKS https://azure.microsoft.com/services/kubernetes-service/

由于我个人博客搭建在腾讯云上,大概是去年(2021年)11、12 月的时候萌生的“要不把博客搬到 k8s 上?”的想法。云厂商提供的容器服务一般都支持从原有云服务器转换为 Kubernetes 的 worker 节点;不过当然也有一些限制,例如腾讯云价格相对低廉的轻量应用服务器则不支持转换。不过正巧我的博客使用的是云服务器,那就试试腾讯云的 TKE 吧。

结果懒惰使人省钱,从学习到实战的脚步总是异常的缓慢,在此期间腾讯云发生了 2 件大事:

  1. 负载均衡实例升级调价公告
    https://cloud.tencent.com/document/product/214/59398

  2. 腾讯云容器服务于2022年3月21日10:00(北京时间)对托管集群计费通知
    https://cloud.tencent.com/document/product/457/68706

运行在 k8s 里的程序想要暴露到公网上,在云厂商的环境中最常用的就是通过负载均衡实现,腾讯云这一调价直接从原来的每小时 0.02 元涨到了 0.2 元,个人用户应该很少用这么贵的服务。(相较而言,阿里云则提供廉价版的负载均衡,个人用户的福音。)

另外彻底阻止我使用 TKE 的是,我们知道 k8s 集群需要有 master 和 worker 节点分工合作,云厂商会提供托管 master 节点的服务,用户只需要自己照料好 worker 节点即可。腾讯云也不例外,不过原来托管是免费的,即将要开始收费了!而不使用托管,意味着我必须再准备一台云服务器,又是一笔开销……

虽然上云等于花钱,但是贫穷就能阻止我们上云吗?答案是否定的,我们可以自己搭建 k8s 集群!甚至还能从中收获许多,如:

  • 复习了 k8s 网络相关的知识

  • 加深了对 ingress 的了解,ingress 是从容器外部访问容器内相对而言比较方便的手段

  • 折腾的快感和解决问题的喜悦

话虽如此,但强烈建议没有接触过 Kubernetes 的朋友们,先通过官方文档 和 minikube 在本地学习后,再继续深入。

ingress https://kubernetes.io/docs/concepts/services-networking/ingress/
官方文档 https://kubernetes.io/zh/docs/setup/
minikube https://minikube.sigs.k8s.io/docs/

我计划通过几篇文章来介绍:

  • k8s 集群的搭建(本篇)

  • 配置 ingress 通过域名访问容器内的站点

  • 使用 cert-manager 为站点自动生成和更新 HTTPS 证书

  • 使用 k8s 的本地卷 (local volume)

cert-manager https://cert-manager.io/
本地卷 https://kubernetes.io/docs/concepts/storage/volumes/#local

好了,废话说的太多,现在就开始进行 k8s 集群的搭建吧~

环境介绍

本文下面的操作都是在腾讯云里进行的,使用的是 1 台云服务器 CVM 和 1 台轻量应用服务器 TencentCloud Lighthouse 进行。(一般云厂商都会给新用户很大的优惠力度,特别是第一台云服务器和轻量应用服务器,意味着前 3 年可以以很低的成本体验。)

云服务器 CVM https://cloud.tencent.com/product/cvm
轻量应用服务器 TencentCloud Lighthouse https://cloud.tencent.com/product/lighthouse

我使用的具体配置和规划如下:

  • master 节点 bun-master-01,内网 IP 10.0.16.13/22:4C4G 轻量应用服务器(本来是2核的,前几天腾讯云免费升级…),master 节点是控制节点,管理其它的节点,以及运行 k8s 依赖的 etcd 等

  • worker 节点 bun-worker-01,内网 IP 172.17.0.2/20:4C8G 云服务器,未来应用程序就跑在这个节点上

操作系统都使用的是 Debian 11.1,上述机器都在同一地域

Debian http://debian.org/

请注意,不应将这样的配置使用在生产环境,因为这不能保证可用性。不过自己玩儿的话,一台机器也可以(建议至少2C2G),将 master 和 worker 部署在同一机器内,但这么做会增加不稳定的因素,另外都上(hua)云(qian)了,新用户购买轻量应用服务器约等于不要钱,当然配置拉满整上。

当然,虽然你使用的可能不是腾讯云或者 Debian,但这并不是你继续阅读的障碍,因为云基础设施、Linux 命令都是类似的,下文在具体的地方会进行标注。

没有具体标明在哪台机器上进行的,则所有机器都需要进行这些步骤,例如内网配置、Debian 配置、安装 docker、kubeadm 等。

集群网络配置

请查看你使用的云厂商没有类似的限制或配置,以免内网不通导致 master 和 worker 节点无法通信。

内网网段配置

好在腾讯云的 CVM 和 Lighthouse 之间虽然默认不能连通,但位于同一地域的机器可以通过内网互联功能,享受 5Gbps 带宽免费互通(不同地域需要收费),可以参考腾讯云文档进行操作,本文不多赘述。

内网互联 https://cloud.tencent.com/document/product/1207/56847

云服务器防火墙配置

例如,我直接选择放行整个网段,这样未来如果增加机器就不用再来回改配置了:

因为我是全部放行,可以使用 ping 命令查看机器是否连通了,例如在我的 worker 上 ping master:

> ping 10.0.16.13

PING 10.0.16.13 (10.0.16.13) 56(84) bytes of data.
64 bytes from 10.0.16.13: icmp_seq=1 ttl=64 time=0.288 ms
64 bytes from 10.0.16.13: icmp_seq=2 ttl=64 time=0.240 ms
64 bytes from 10.0.16.13: icmp_seq=3 ttl=64 time=0.264 ms

允许访问公网

由于安装 k8s 过程中需要拉取一些镜像或下载一些文件,而这些镜像或文件在腾讯云内网镜像服务中并没有提供,所以至少安装阶段需要访问公网。

Debian 基础配置

如果你使用的是其它 Linux 发行版本,那么通过互联网搜索相关操作,能很快找到对应的命令。

配置机器名称和 hosts

使用统一一致的机器名称利于管理:

hostnamectl set-hostname bun-master-01

配置 hosts 文件让机器名称可以被访问:

cat >> /etc/hosts << EOF
10.0.16.13 bun-master-01
172.17.0.2 bun-worker-01
EOF

关闭防火墙等配置

安装 k8s 前需要将 Linux 的防火墙、安全配置(SELinux)、交换分区(Swap,类似于 Windows 的虚拟内存)关闭。不过这些内容默认情况下都是禁用的,如果你曾经开启过记得关闭。

允许 iptables 检查桥接流量

cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

配置时间同步

确保每台机器时间都是正确的,我使用腾讯云内网提供的NTP 服务

NTP 服务 https://cloud.tencent.com/document/product/213/30392

如果你的云厂商没有提供类似服务,可以采用公网上的 NTP 服务。

安装 ntp:

apt-get install -y ntp

修改 ntp 配置:

vi /etc/ntp.conf

重启 ntp 并查看效果:

service ntp restart
service ntp status
ntpq -p

最后设置 ntp 开机启动:

systemctl enable ntp

安装 docker

注意:所有节点都需要安装 docker。

我们使用官方脚本安装 docker,在此之前先把一些必要的程序安装一下:

apt-get update
apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release

接着使用腾讯云内网镜像来进行下载:

http://mirrors.tencentyun.com 只能在腾讯云内网访问,你需要将它们换成你可用的,例如 http://mirrors.tencent.com。本文优先使用腾讯云内网镜像,因为这样高速,且不会产生任何公网流量和费用。

curl -fsSL http://mirrors.tencentyun.com/docker-ce/linux/debian/gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] http://mirrors.tencentyun.com/docker-ce/linux/debian \
$(lsb_release -cs) stable"
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

接下来开始安装 docker:

apt-get update
apt-get install -y \
docker-ce \
docker-ce-cli \
containerd.io

检查 docker 网络

安装好之后,使用 ip a 命令检查一下 docker 新建的网卡,避免 docker 新建的网卡导致网段冲突,内网机器无法访问。

例如在 bun-master-01 机器上执行 ip a 得到如下返回:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 52:54:00:19:8b:e4 brd ff:ff:ff:ff:ff:ff
altname enp0s5
altname ens5
inet 10.0.16.13/22 brd 10.0.19.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe19:8be4/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:c3:bf:8b:a6 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
cat >> /etc/docker/daemon.json << EOF
{
"bip": "192.168.0.1/16"
}
EOF

然后重启 docker,并重新运行 ip a 检查配置是否生效,以及重新 ping 一下测试连通性:

service docker restart

配置 docker 镜像加速源

https://mirror.ccs.tencentyun.com 只能在腾讯云内网访问,你需要将它们换成你可用的,例如使用阿里云镜像加速


阿里云镜像加速 https://help.aliyun.com/document_detail/60750.html

修改 /etc/docker/daemon.json 文件,新增 registry-mirrors 的配置:

{
"bip": "192.168.0.1/16",
"registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
}

接下来重启 docker 应用配置:

service docker restart

如果一切正常,让我们跑一下 docker 的 hello-world 试试:

docker run hello-world

如果看到 Hello from Docker! 则说明一切顺利,继续进行下一步吧。

docker 启动失败常见问题

如果在重启 docker 时不幸遇到了这样的提示:

Job for docker.service failed because the control process exited with error code.
See "systemctl status docker.service" and "journalctl -xe" for details.

不要慌,请运行 journalctl -xe 查看具体错误。例如我尝试将 docker 的虚拟网卡 IP 修改为了一个非法的值:

Mar 23 21:44:28 master-01.bun.local dockerd[186689]: time="2022-03-23T21:44:28.584331774+08:00" level=info msg="[graphdriver] using prior storage driver: overlay2"
Mar 23 21:44:28 master-01.bun.local dockerd[186689]: time="2022-03-23T21:44:28.589151624+08:00" level=info msg="Loading containers: start."
Mar 23 21:44:28 master-01.bun.local dockerd[186689]: time="2022-03-23T21:44:28.643081791+08:00" level=info msg="stopping event stream following graceful shutdown" error="<nil>" module=libcontainerd namespace=moby
Mar 23 21:44:28 master-01.bun.local dockerd[186689]: failed to start daemon: Error initializing network controller: Error creating default "bridge" network: failed to allocate gateway (192.168.0.0): Address already in use

如果是其它问题,可以在网上进行搜索。

安装 kubeadm, kubelet 以及 kubectl

注意:所有节点都需要安装 kubeadm, kubelet 以及 kubectl。

由于 Google 无法访问,腾讯云的镜像又没有 gpg 文件,只好从阿里云的镜像下载 pgp 文件😓感谢友商

curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg

不过腾讯云的镜像有相关的软件包:

有熟悉 Debian 的小伙伴会注意到,这里指定了 Debian 的 xenial 版本,它其实表示 Debian 的 k8s 最新版。这个 issue 解释了背后的故事。


这个 issue https://github.com/kubernetes/kubernetes/issues/66300

echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] http://mirrors.tencentyun.com/kubernetes/apt/ kubernetes-xenial main" \
| sudo tee /etc/apt/sources.list.d/kubernetes.list

接下来把它们统统安装上,并固定住版本,避免不小心升级造成问题:

apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

部署 master 节点

网段的设置注意不要和现有网段冲突。

使用 kubeadm init 来部署 master 节点,下面的参数中:

  • image-repository 使用阿里云提供的镜像 registry.cn-hangzhou.aliyuncs.com/google_containers,再次感谢友商(腾讯云内网的 ccr.ccs.tencentyun.com 似乎只有用户公开的,且并不是最新的,没法用)

kubeadm init \
--apiserver-advertise-address=10.0.16.13 \
--service-cidr=10.1.0.0/16 \
--pod-network-cidr=10.10.0.0/16 \
--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers

稍等片刻,便可以部署成功:

[init] Using Kubernetes version: v1.23.5
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
...
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.0.16.13:6443 --token 5x8l46.p5716s1ezcgqt3n2 \
--discovery-token-ca-cert-hash sha256:f775a880cff6c4bd250420931726a8bcb9344b0dd335aa9be52a315f65616e16

根据上面 To start using your cluster ... 部分的指引,继续执行它下面的 3 条命令,好让 kubectl 工作:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

接着把最后两行 kubeadm 命令复制下来备用。到此,master 节点就配置完成了。

部署 worker 节点

执行从 master 节点执行成功后返回的 kubeadm 命令(就是刚刚复制的那个),当前机器就会加入到 master 的集群:

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

回到 master 节点,通过 kubectl get nodes 命令查看集群内节点的情况:

NAME            STATUS     ROLES                  AGE     VERSION
bun-master-01 NotReady control-plane,master 4m13s v1.23.5
bun-worker-01 NotReady <none> 51s v1.23.5

可以看到 bun-worker-01 节点已经加入集群,amazing!不过为啥是 NotReady 状态?

别急,还有最后一步,配置 CNI (Container Network Interface) 插件 —— 它为容器管理系统(如 docker、k8s)提供网络功能。

CNI https://github.com/containernetworking/cni

安装 CNI 插件:Calico

Calico 是一款被广泛使用的 k8s CNI 插件,先下载它的配置文件,因为一会儿需要修改里面的内容:

wget https://docs.projectcalico.org/manifests/calico.yaml

在 calico.yaml 文件中找到 CALICO_IPV4POOL_CIDR 的配置,将其设置为执行 kubeadm init 时指定的 pod-network 一样的值:

然后安装 Calico:

kubectl apply -f calico.yaml

安装好后,可以通过 ip a 命令在 master 和 worker 节点上查看到 Calico 创建的 tunl0 网卡。并且再检查一下 master 和 worker 之间是否还能连通,避免因为配置错误之类的问题导致内网不通。

现在再运行 kubectl get nodes 命令,就能看到 master 和 worker 节点已经处于 Ready 状态。

参考

  • Installing kubeadm
    https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm