在Linux上搭建K8s集群

寒江蓑笠翁大约 15 分钟技术日志k8scontainerd

在Linux上搭建K8s集群

最近捣鼓了下用虚拟机搭建k8s集群,坑还是挺多的。


最近在学习k8s,不得不说这玩意运行起来还是相当的麻烦,这里记录一下,以免后面忘了。事先准备好三台ubuntu22.04虚拟机,一台用作control plane,两台用作worker node。

前置准备

在开始安装k8s之前,需要做一些前置的准备。

关闭firewalld

k8s有着自己的网络策略配置功能,关闭friewalld是为了避免起冲突。

# 查看状态
$ ufw status
# 禁用
$ ufw disable

禁用selinux

selinux是linux的一个安全子系统,很多服务器未为了避免麻烦都会把它关了,ubuntu在装机的时候不会自带这玩意,但如果你装了的话可以按照下面的步骤关闭。

# 临时关闭
$ setenforce 0
# 永久关闭
$ vim /etc/selinux/config
SELINUX=disabled

关闭swap

kubelet运行时明确不支持swap,也就是交换内存,一部分原因是想让程序在内存耗尽以后正常OOM而不是一直靠swap苟着从而造成不必要的损失。如果未关闭swap直接启动的话,kubelet在启动时会显示如下信息告诉你应该关闭swap,否则不让你启动。

"command failed" err="failed to run Kubelet: running with swap on is not supported, please disable swap! or set --fail-swap-on flag to false. /proc/swaps contained: [Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority /swapfile

首先执行命令关闭交换分区

$ swapoff -a

然后修改fstab文件

$ vim /etc/fstab

注释掉如下行

# /swapfile                                 none            swap    sw              0       0

执行如下命令查看swap分区情况,如果关闭了的话就不会有任何显示

swapon -show

配置网络

转发 IPv4 并让 iptables 看到桥接流量

$ cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

$ sudo modprobe overlay
$ sudo modprobe br_netfilter

# 设置所需的 sysctl 参数,参数在重新启动后保持不变
$ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

# 应用 sysctl 参数而不重新启动
$ sudo sysctl --system

通过运行以下指令确认 br_netfilteroverlay 模块被加载:

$ lsmod | grep br_netfilter
$ lsmod | grep overlay

通过运行以下指令确认 net.bridge.bridge-nf-call-iptablesnet.bridge.bridge-nf-call-ip6tablesnet.ipv4.ip_forward 系统变量在你的 sysctl 配置中被设置为 1

$ sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward

CRI

Container Runtime Interface(CRI),即容器运行时接口,要想使用K8s的话,需要系统提供CRI,目前实现了CRI的软件的有

  • containerd,推荐用这个,比较轻量。
  • docker engine,并没有实现CRI但是可以通过其它方法桥接,不过一般安装了docker engine的系统都会有containerd,因为containerd就是docker的一部分,所以还是建议用containerd。
  • CRI-O
  • MCR

containerd

下面会用containerd来做演示,其实containerd安装过程就是docker安装过程,先设置docker官方的apt仓库

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get updat

最后就只安装containerd.ioopen in new window,不用安装dcoker-ce和docker-cli。

sudo apt-get install containerd.io

或者你也可以直接下载containerd的二进制文件,它也是用go写的。在安装好后,需要配置systemd cgroup驱动,在containerd配置文件中

/etc/containerd/config.toml

修改如下的配置项

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true

提示

使用如下命令可以重置containerd配置

$ containerd config default > /etc/containerd/config.toml

从软件包安装的话可能会默认禁用CRI,在配置文件中可能会看到这么一行,将其去掉就行。

disabled_plugins = ["CRI"]

修改完后重启containerd

sudo systemctl restart containerd

安装

配置下k8s的阿里云apt源

$ echo "deb https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main" >> /etc/apt/sources.list

更新证书

$ curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add

再更新源

$ sudo apt update

最后安装kubeadmkubectlkubelet,这三个最好软件版本保持一致。

$ sudo apt-get install kubeadm kubelet kubectl

完成后确认版本

$ kubelet --version
Kubernetes v1.28.2
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"28", GitVersion:"v1.28.2", GitCommit:"89a4ea3e1e4ddd7f7572286090359983e0387b2f", GitTreeState:"clean", BuildDate:"2023-09-13T09:34:32Z", GoVersion:"go1.20.8", Compiler:"gc", Platform:"linux/amd64"}
$ kubectl version
Client Version: v1.28.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
The connection to the server localhost:8080 was refused - did you specify the right host or port?

确认版本一致后,看看k8s的镜像,后续必须pull这些镜像,因为这是k8s集群运行的必要组件。

$ kubeadm config images list
registry.k8s.io/kube-apiserver:v1.28.2
registry.k8s.io/kube-controller-manager:v1.28.2
registry.k8s.io/kube-scheduler:v1.28.2
registry.k8s.io/kube-proxy:v1.28.2
registry.k8s.io/pause:3.9
registry.k8s.io/etcd:3.5.9-0
registry.k8s.io/coredns/coredns:v1.10.1

到目前为止,系统上会有下面这几个东西

  • kebuadm,用来快速启动和搭建k8s集群的工具,可以省去我们很多操作。
  • kubelet,k8s集群命令行管理工具
  • kubelet,代表着一个节点,是k8s集群的基本单位。
  • crictl,容器运行时管理工具,只不过它是为k8s工作的,正确使用的前提是系统上安装了支持CRI的软件并正确指定了endpoint。
  • ctr,ctr是containerd的命令管理工具,containerd实现了CRI。

cri endpoint

ctrctl虽然是容器运行时管理工具,但是它并没有具体的实现,只是定义了一组接口规范。要想正常工作还得依赖具体的实现了CRI的软件,之前已经安装好了containerd,所以运行前要先指定crictl的runtime-endpoint,也就是containerd的sock地址。

通过查看配置文件etc/containerd/config.toml可以得知

[grpc]
  address = "/run/containerd/containerd.sock"
  gid = 0
  max_recv_message_size = 16777216
  max_send_message_size = 16777216
  tcp_address = ""
  tcp_tls_ca = ""
  tcp_tls_cert = ""
  tcp_tls_key = ""
  uid = 0

那么endpoint就是

unix:///run/containerd/containerd.sock

所以执行如下命令配置crictl

sudo crictl config runtime-endpoint unix:///run/containerd/containerd.sock

拉镜像

kubeadm支持通过命令预先拉取需要用到的组件镜像,也就是之前list出来的镜像,执行如下命令就可以预先拉取要用到的镜像。

$ kubeadm config images pull

但是不出意外的话,意外就会发生了,上述的镜像仓库是registry.k8s.io,是由谷歌托管的,国内基本上没法访问,甚至于在线获取版本信息都不行

W0927 19:34:16.513175    4571 version.go:104] could not fetch a Kubernetes version from the internet: unable to get URL "https://dl.k8s.io/release/stable-1.txt": Get "https://cdn.dl.k8s.io/release/stable-1.txt": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
W0927 19:34:16.513428    4571 version.go:105] falling back to the local client version: v1.28.2

解决方法就是国内的镜像,阿里云有一个镜像仓库,地址如下

registry.aliyuncs.com/google_containers

网上有很多教程直接在kubeadm init时直接指定了阿里云镜像仓库,这样会导致kubelet没法正常运行,会说找不到组件的镜像,因为kubelet运行的时候只认registry.k8s.io镜像,而通过阿里云镜像仓库拉下来的镜像的前缀是registry.aliyuncs.com/google_containers,所以kubelet自然就没法启动了。所以对应的,拉取完下面的镜像后,应该将其名字改回去。

registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2
registry.aliyuncs.com/google_containers/kube-controller-manager:v1.28.2
registry.aliyuncs.com/google_containers/kube-scheduler:v1.28.2
registry.aliyuncs.com/google_containers/kube-proxy:v1.28.2
registry.aliyuncs.com/google_containers/pause:3.9
registry.aliyuncs.com/google_containers/etcd:3.5.9-0
registry.aliyuncs.com/google_containers/coredns/coredns:v1.10.1

crictl并不能修改镜像名,这是ctr应该干的事情,为了能够查看到k8s的镜像,指定命名空间k8s.io

$ sudo ctr -n k8s.io images ls

一个个改名太麻烦了,所以我写了一个脚本,来自动化完成这个过程。

#!/bin/bash
aliyun="registry.aliyuncs.com/google_containers"
k8sio="registry.k8s.io"
echo "pulling needed k8s images from $aliyun"
kubeadm config images pull --image-repository "$aliyun"
echo "compare local with $k8sio"
# list all kubeadm needs images
for i in $(kubeadm config images list); do
    # get suffxi images name
	imagename=${i##*/}
    # concat new name
	aliimage="$aliyun/$imagename"
	echo "[rename] $aliimage >>>> $i"
    # rename registry to k8s.io
	ctr -n k8s.io i tag "$aliimage" "$i"
	echo "[remove] aliyun image $aliimage"
    # remove aliyun images
	ctr -n k8s.io i rm "$aliimage"
done;

或者也可以

$ curl https://raw.githubusercontent.com/246859/shell/main/k8s/aliyun_images_pull.sh | bash

初始化

接下来使用kubeadm来初始化,这个操作只用在master节点进行。init时有很多参数,开始前可以看看命令帮助。

$ kubeadm init -h
Usage:
  kubeadm init [flags]
  kubeadm init [command]

Available Commands:
  phase       Use this command to invoke single phase of the init workflow

Flags:
      --apiserver-advertise-address string   The IP address the API Server will advertise it's listening on. If not set the default network interface will be used.
      --apiserver-bind-port int32            Port for the API Server to bind to. (default 6443)
      --apiserver-cert-extra-sans strings    Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names.
      --cert-dir string                      The path where to save and store the certificates. (default "/etc/kubernetes/pki")
      --certificate-key string               Key used to encrypt the control-plane certificates in the kubeadm-certs Secret.
      --config string                        Path to a kubeadm configuration file.
      --control-plane-endpoint string        Specify a stable IP address or DNS name for the control plane.
      --cri-socket string                    Path to the CRI socket to connect. If empty kubeadm will try to auto-detect this value; use this option only if you have more than one CRI installed or if you have non-standard CRI socket.
      --dry-run                              Don't apply any changes; just output what would be done.
      --feature-gates string                 A set of key=value pairs that describe feature gates for various features. Options are:
                                             EtcdLearnerMode=true|false (ALPHA - default=false)
                                             PublicKeysECDSA=true|false (ALPHA - default=false)
                                             RootlessControlPlane=true|false (ALPHA - default=false)
                                             UpgradeAddonsBeforeControlPlane=true|false (DEPRECATED - default=false)
...
...

接下来就开始初始化,如果上面的配置都做好了的话,是不会出现问题的。

$ sudo kubeadm init \
--apiserver-advertise-address=192.168.48.138 \
--image-repository=registry.aliyuncs.com/google_containers
上次编辑于:
贡献者: 246859