Debian 11 部署 K8s 集群

一、集群规划

ip hostname role version
10.4.7.201 debian201 master v1.28.2
10.4.7.202 debian202 node v1.28.2
10.4.7.203 debian203 node v1.28.2

二、前期准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 检查mac地址和product_uuid(节点唯一)
$ ip link
$ sudo cat /sys/class/dmi/id/product_uuid

# 修改hosts(主机名略)
$ sudo vim /etc/hosts
10.4.7.201 debian201
10.4.7.202 debian202
10.4.7.203 debian203

# 临时开启内核模块(让iptables看到桥接流量)
$ sudo modprobe overlay
$ sudo modprobe br_netfilter

# 让Linux系统在系统引导期间启用内核模块
$ cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

# 查看内核模块是否开启
$ lsmod | grep 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
$ sudo sysctl --system # 在不重启机器的情况下应用配置

# 关闭SWAP
$ sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab # 永久关闭
$ sudo swapoff -a # 当前会话关闭
$ free -m

基于安全性(如在官方文档中承诺的 Secret 只会在内存中读写,不会落盘)、利于保证节点同步一致性等原因,从 1.8 版开始,Kubernetes 就在它的文档中明确声明了它默认不支持 Swap 分区,在未关闭 Swap 分区的机器中,集群将直接无法启动

三、容器运行时

1. Containerd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 1. containerd
$ wget -c https://github.com/containerd/containerd/releases/download/v1.7.6/containerd-1.7.6-linux-amd64.tar.gz
$ sudo tar Cxzvf /usr/local containerd-1.7.6-linux-amd64.tar.gz

$ wget -c https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
$ sudo mkdir -p /usr/local/lib/systemd/system/
$ sudo cp containerd.service /usr/local/lib/systemd/system/
$ sudo chmod 755 /usr/local/lib/systemd/system/containerd.service

$ sudo mkdir -p /etc/containerd/
$ containerd config default | sudo tee /etc/containerd/config.toml
$ sudo vi /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9"
#systemd_cgroup = false
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://ung2thfc.mirror.aliyuncs.com","https://docker.mirrors.ustc.edu.cn"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
endpoint = ["https://gcr.mirrors.ustc.edu.cn"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
endpoint = ["https://gcr.mirrors.ustc.edu.cn/google-containers/"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
endpoint = ["https://quay.mirrors.ustc.edu.cn"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"]
endpoint = ["k8s.m.daocloud.io"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."10.4.7.200:88"]
endpoint = ["http://10.4.7.200:88"]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."10.4.7.200:88".tls]
insecure_skip_verify = true
[plugins."io.containerd.grpc.v1.cri".registry.configs."10.4.7.200:88".auth]
username = "admin"
password = "Harbor12345"

$ sudo systemctl daemon-reload
$ sudo systemctl enable --now containerd

# 2. runc
$ wget -c https://github.com/opencontainers/runc/releases/download/v1.1.9/runc.arm64
$ sudo install -m 755 runc.amd64 /usr/local/sbin/runc

# 3. CNI plugins
$ wget -c https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
$ sudo mkdir -p /opt/cni/bin
$ sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.3.0.tgz

# 4. crictl
$ wget -c https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.28.0/crictl-v1.28.0-linux-amd64.tar.gz
$ sudo tar Czxvf /usr/local/bin crictl-v1.28.0-linux-amd64.tar.gz
$ cat << EOF | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 2
debug: false
pull-image-on-create: false
EOF

pause 版本可通过 sudo kubeadm config images list --kubernetes-version v1.28.2 查看

2. Docker

  • K8s 1.24 版本移除了 Dockershim,因此需要注意容器运行时的选择
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Uninstall old versions
$ for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

# 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/debian/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/debian \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update

# Install the Docker packages
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

$ cat << EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"insecure-registries": ["10.4.7.200:88"],
"registry-mirrors": ["https://ung2thfc.mirror.aliyuncs.com","https://registry.docker-cn.com"]
}
EOF
$ sudo systemctl restart docker

$ sudo usermod -aG docker $USER

$ sudo curl -L "https://github.com/docker/compose/releases/download/v2.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

3. CRI-O

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 安装依赖
$ sudo apt install gnupg2 apt-transport-https -y

# 添加CRI-O仓库和GPG key
$ export OS=Debian_11
$ export VERSION=1.24
$ curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo gpg --dearmor -o /usr/share/keyrings/libcontainers-archive-keyring.gpg
$ curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/Release.key | sudo gpg --dearmor -o /usr/share/keyrings/libcontainers-crio-archive-keyring.gpg
$ echo "deb [signed-by=/usr/share/keyrings/libcontainers-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
$ echo "deb [signed-by=/usr/share/keyrings/libcontainers-crio-archive-keyring.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list

# 更新并刷新存储库和软件包索引
$ sudo apt update

# 安装CRI-O
$ sudo apt install cri-o cri-o-runc cri-tools -y

# 配置
$ sudo vim /etc/crio/crio.conf
[crio.network]
network_dir = "/etc/cni/net.d/"
plugin_dirs = [
"/opt/cni/bin/",
]
$ sudo vim /etc/cni/net.d/100-crio-bridge.conf
"ranges": [ # 使用自定义子网(Pod)(确保和CNI插件的地址匹配)
[{ "subnet": "10.10.0.0/24" }],
[{ "subnet": "1100:200::/24" }]
]
$ sudo systemctl restart crio
$ sudo systemctl enable crio

三、K8s 集群

  • kubeadm:用于引导、安装和启动 Kubernetes 集群的命令行工具,只需要简单的几个命令工具(如 init、join、upgrade、reset 等),便可以快速完成集群的维护管理工作
  • kubelet:每个节点上的代理程序,负责节点上的大部分操作,比如由它来启停容器等,节点上只有它能与 apiserver 进行通信
  • kubectl:Kubernetes 客户端工具,用来操控 Kubernetes 集群,比如查看集群中的节点信息等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1. 注册apt软件源
$ sudo apt install -y apt-transport-https ca-certificates curl gnupg2
$ curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
$ sudo apt update

# 2. 安装k8s
$ sudo apt install -y kubeadm=1.28.2-00 kubelet=1.28.2-00 kubectl=1.28.2-00
$ sudo apt-mark hold kubeadm kubelet kubectl
$ sudo systemctl enable kubelet --now

# 3. master节点初始化集群
$ sudo kubeadm init \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--pod-network-cidr=10.10.0.0/16 \
--apiserver-advertise-address=10.4.7.201 \
--kubernetes-version=v1.28.2 \
--v=5
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 4. node节点加入集群
$ sudo kubeadm join 10.4.7.201:6443 --token o1szra.s2i6veja96njcdq5 \
--discovery-token-ca-cert-hash sha256:bf61c81dbacc0a4909a373c9b388e243ea37ce07142c5c79c60c28c7e39496b4

四、CNI 网络插件

1. calico

1
2
3
4
5
$ curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -O
#$ vi calico.yaml
#- name: CALICO_IPV4POOL_CIDR
# value: "10.10.0.0/16"
$ kubectl apply -f calico.yaml

2. flannel

1
2
3
4
5
6
7
8
9
10
$ curl https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml -O
#$ vi kube-flannel.yml
#net-conf.json: |
# {
# "Network": "10.10.0.0/16",
# "Backend": {
# "Type": "vxlan"
# }
# }
$ kubectl apply -f kube-flannel.yml

五、Helm

1
2
3
$ wget -c https://get.helm.sh/helm-v3.13.0-linux-amd64.tar.gz
$ tar -zxvf helm-v3.13.0-linux-amd64.tar.gz
$ sudo mv linux-amd64/helm /usr/local/bin/helm

六、其他

1. 常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 命令自动补全
$ echo "source <(kubectl completion bash)" >> ~/.bashrc

# 移除master上的污点
$ kubectl taint nodes debian201 node-role.kubernetes.io/control-plane:NoSchedule-

# 调整NodePort范围(默认30000~32767)
$ sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
--service-node-port-range=5000-32767
$ export apiserver_pods=$(kubectl get pods --selector=component=kube-apiserver -n kube-system --output=jsonpath={.items..metadata.name})
$ kubectl delete pod $apiserver_pods -n kube-system
$ kubectl describe pod $apiserver_pods -n kube-system | grep 5000

# 生成node节点加入的token(默认24h)
$ kubeadm token create --print-join-command

2. 容器运行时的发展

  • OCI(Open Container Initiative,开放容器标准)规范由 Docker 倡导和提议,联合 CoreOS 等多家厂商,于 2015 年 6 月 22 日发起。OCI 主要包括三个部分,其中一个就是运行时标准:
    • runtime-spec:容器运行时标准,定义了容器运行的配置,环境和生命周期。即如何运行一个容器,如何管理容器的状态和生命周期,如何使用操作系统的底层特性(namespace,cgroup,pivot_root 等)
    • image-spec:容器镜像标准,定义了镜像的格式,配置(包括应用程序的参数,环境信息等),依赖的元数据格式等,简单来说就是对镜像的静态描述
    • distribution-spec:镜像分发标准,即规定了镜像上传和下载的网络交互过程
  • 容器运行时所做的工作(简单来说就是容器的全生命周期管理)
    • 拉取镜像:如果本地没有镜像,从镜像仓库拉取
    • 镜像解压缩:镜像被解压缩到一个 COW(copy-on-write,写时复制)的文件系统上,所有容器层相互堆叠,最终合并成一个文件系统
    • 为容器准备一个挂载点
    • 设置容器:从镜像中获取元数据信息,并对容器进行设置,保证容器能按照期望的方式正常启动,设置过程中还包括来自用户的输入,比如 CMD,ENTRYPOINT 或程序启动的入参等
    • 设置 namespaces:内核为容器设置相关隔离,如文件系统,进程,网络,IPC 等
    • 设置 cgroups:内核为容器设置资源使用限制,如 CPU,memory 等
    • 通过系统调用启动容器
    • 设置 SELinux/AppArmor,即为容器设置相关安全策略
  • 在 Docker 之前,其实早就有了类似的容器技术,就是 LXC(LinuX Containers,Linux 容器)。2008 年发布的 Linux 2.24 内核首次引入了 Cgroups 技术,而在同一时间,Linux 就发布了 LXC
    • LXC 是一个操作系统级的虚拟化方式,对 Cgroups 和 Namespaces 等 Linux 内核特性进行封装,然后提供一个统一的接口,降低用户使用容器技术的门槛
    • 技术层面确实如此,但设计理念却完全不同:LXC 是封装系统的轻量级虚拟机,而 Docker 则是封装应用
  • Docker 在 v1.10 版本之前是直接利用 LXC 来实现容器隔离的。但到了 2014 年,Docker 公司利用 Go 语言开发了新的底层驱动 libcontainer,从而越过 LXC 直接操控 Namespaces 和 Cgroups。如此一来,Docker 便能直接与系统内核打交道,LXC 也就在 Docker 的崛起中被其淘汰了
    • https://docs.docker.com/engine/faq/#what-does-docker-technology-add-to-just-plain-lxc
    • Docker 依靠统一的内核,从应用的角度出发,每个容器默认只跑一个程序。比如部署 LAMP,那就是 4 个容器,然后再将其组合起来(而不是将环境作为一个整体交付)
    • Docker uses a client-server architecture. The Docker client talks to the Docker daemon, which does the heavy lifting of building, running, and distributing your Docker containers. The Docker client and daemon can run on the same system, or you can connect a Docker client to a remote Docker daemon. The Docker client and daemon communicate using a REST API, over UNIX sockets or a network interface. Another Docker client is Docker Compose, that lets you work with applications consisting of a set of containers.

docker-architecture

  • 2015 年,Docker 又带头搞出了 OCI 标准。为了推动 OCI 标准的落地,Docker 进一步向前演进自身的架构:
    • 先将 libcontainer 独立出来,重构成了 runc 项目,并将其捐献给了 Linux 基金会
      • 由 runc 来负责底层容器的生成和运行,即直接操控 Namespaces 和 Cgroups 等内核特性。官方还特地说明,runc 是一个底层工具,不适合终端用户直接操作,最好配合高层次的容器软件使用(containerd)
    • Docker 继续重构 Docker daemon 子系统,将其中与容器运行时交互的部分抽象为 containerd 项目,并将其捐献给了 CNCF
      • containerd 负责主机上容器的生命周期管理:包括镜像的传输和存储,容器的执行、监管、日志、构建、网络等功能。同样,它不也适合开发人员直接使用,而是嵌入到更大的系统中
      • 实际上,containerd 运行后,内部会创建一个 containerd-shim 进程,与 runc 搭配使用。可以运行一个 Docker 容器后用 ps 看到

docker-containerd-runc

参考