背景
Kubernetes 是时下流行的容器编排引擎,因为字母太多,且掐头去尾后剩下 8 个字母,于是被大家亲切的缩写为 k8s。
另外所谓“云原生”概念火爆,各大云厂商也纷纷推出了自己的容器服务,例如阿里云的ACK、腾讯云的TKE、微软云的AKS等,无不是基于 Kubernetes 。
由于我个人博客搭建在腾讯云上,大概是去年(2021年)11、12 月的时候萌生的“要不把博客搬到 k8s 上?”的想法。云厂商提供的容器服务一般都支持从原有云服务器转换为 Kubernetes 的 worker 节点;不过当然也有一些限制,例如腾讯云价格相对低廉的轻量应用服务器则不支持转换。不过正巧我的博客使用的是云服务器,那就试试腾讯云的 TKE 吧。
结果懒惰使人省钱,从学习到实战的脚步总是异常的缓慢,在此期间腾讯云发生了 2 件大事:
运行在 k8s 里的程序想要暴露到公网上,在云厂商的环境中最常用的就是通过负载均衡实现,腾讯云这一调价直接从原来的每小时 0.02 元涨到了 0.2 元,个人用户应该很少用这么贵的服务。(相较而言,阿里云则提供廉价版的负载均衡,个人用户的福音。)
另外彻底阻止我使用 TKE 的是,我们知道 k8s 集群需要有 master 和 worker 节点分工合作,云厂商会提供托管 master 节点的服务,用户只需要自己照料好 worker 节点即可。腾讯云也不例外,不过原来托管是免费的,即将要开始收费了!而不使用托管,意味着我必须再准备一台云服务器,又是一笔开销……
虽然上云等于花钱,但是贫穷就能阻止我们上云吗?答案是否定的,我们可以自己搭建 k8s 集群!甚至还能从中收获许多,如:
- 复习了 k8s 网络相关的知识
- 加深了对 ingress 的了解,ingress 是从容器外部访问容器内相对而言比较方便的手段
- 折腾的快感和解决问题的喜悦
话虽如此,但强烈建议没有接触过 Kubernetes 的朋友们,先通过官方文档 和 minikube 在本地学习后,再继续深入。
我计划通过几篇文章来介绍:
- k8s 集群的搭建(本篇)
- 配置 ingress 通过域名访问容器内的站点
- 使用 cert-manager 为站点自动生成和更新 HTTPS 证书
- 使用 k8s 的本地卷 (local volume)
好了,废话说的太多,现在就开始进行 k8s 集群的搭建吧~
环境介绍
本文下面的操作都是在腾讯云里进行的,使用的是 1 台云服务器 CVM 和 1 台轻量应用服务器 TencentCloud Lighthouse 进行。(一般云厂商都会给新用户很大的优惠力度,特别是第一台云服务器和轻量应用服务器,意味着前 3 年可以以很低的成本体验。)
我使用的具体配置和规划如下:
- master 节点
bun-master-01
,内网 IP10.0.16.13/22
:4C4G 轻量应用服务器(本来是2核的,前几天腾讯云免费升级…),master 节点是控制节点,管理其它的节点,以及运行 k8s 依赖的 etcd 等 - worker 节点
bun-worker-01
,内网 IP172.17.0.2/20
:4C8G 云服务器,未来应用程序就跑在这个节点上
操作系统都使用的是 Debian 11.1,上述机器都在同一地域。
请注意,不应将这样的配置使用在生产环境,因为这不能保证可用性。不过自己玩儿的话,一台机器也可以(建议至少2C2G),将 master 和 worker 部署在同一机器内,但这么做会增加不稳定的因素,另外都上(hua)云(qian)了,新用户购买轻量应用服务器约等于不要钱,当然配置拉满整上。
当然,虽然你使用的可能不是腾讯云或者 Debian,但这并不是你继续阅读的障碍,因为云基础设施、Linux 命令都是类似的,下文在具体的地方会进行标注。
没有具体标明在哪台机器上进行的,则所有机器都需要进行这些步骤,例如内网配置、Debian 配置、安装 docker、kubeadm 等。
集群网络配置
请查看你使用的云厂商没有类似的限制或配置,以免内网不通导致 master 和 worker 节点无法通信。
内网网段配置
要组建集群,首先机器之间得能互相访问。最理想的情况是机器都处在同一个网段,但省钱的方案并不能做到这一点,我的两台主机位于完全不同的两个网段,Lighthouse 甚至不支持修改内网 IP 地址。
好在腾讯云的 CVM 和 Lighthouse 之间虽然默认不能连通,但位于同一地域的机器可以通过内网互联功能,享受 5Gbps 带宽免费互通(不同地域需要收费),可以参考腾讯云文档进行操作,本文不多赘述。
云服务器防火墙配置
另外需要注意的是云厂商提供的防火墙的配置(不是系统里的防火墙),需要在每台主机的入站规则中添加上其它主机的 IP 地址,腾讯云的配置入口位于 实例详情 - 安全组(Lighthouse 为防火墙)。
例如,我直接选择放行整个网段,这样未来如果增加机器就不用再来回改配置了:
因为我是全部放行,可以使用 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 服务。
安装 ntp:
apt-get install -y ntp
修改 ntp 配置:
vi /etc/ntp.conf
将腾讯云的 NTP 服务器地址替换掉配置文件中默认的:
重启 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
docker 创建的虚拟网卡 docker0
使用了 172.17.0.1
地址,而我的另一台机器 bun-worker-01
则是 172.17.0.2
。这样导致无法从 bun-master-01
访问 bun-worker-01
。
我们可以修改 docker 的配置文件指定它的虚拟网卡地址,例如我希望 docker 使用 192.168.0.1/16
网段,这样和谁都不冲突:
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
只能在腾讯云内网访问,你需要将它们换成你可用的,例如使用阿里云镜像加速。
修改 /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
docker 要求配置的值是一个真实的 IP 地址,所以不能配置为 192.168.0.0/16
,需要配置为 192.168.0.1/16
。
如果是其它问题,可以在网上进行搜索。
安装 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 解释了背后的故事。
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 节点,下面的参数中:
- 将
apiserver-advertise-address
设置为当前机器的内网 ip 地址 service-cidr
表示 k8s Service 使用的 IP 地址段,设置一个自己喜欢的pod-network
表示 k8s Pod 使用的 IP 地址段,设置一个自己喜欢的,但对于 pod 尽量划分容量大一点的网段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 插件: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
状态。