Kubernetes 部署实战:kubeadm + containerd

使用 kubeadm + containerd 从零部署生产级 Kubernetes 集群的完整记录,含网络插件和核心插件配置。

ops-install-service-kubernetes_by_kubeadmin_new


学习新技术提升自我价值,Kubernetes 是 Google 开源的容器集群管理系统,对于容器运行时、编排、常规服务都抽象设计出标准完整的 API。

简介

跟着官方文档学习使用 Kubeadm 安装 Kubernetes。由于中文文档和英文文档有差别,选择使用英文文档进行学习安装。

由于官方文档较复杂,跟着【K8S 笔记 - 使用 kubeadm 安装 k8s】学习了部分内容,做了部分修改。修改的原因是在搭建学习 kubernets 过程中了解到 kubernetes 已经在高版本移除了 dockershim 且,containerd 运行时是 docker 项目拆分出来的容器运行时,所以直接选择 containerd 作为容器运行时。

其他参考文档:

以下是本文档使用到的服务和对应的信息

序号 服务 版本 描述
1 containerd.io 1.7.22 容器运行时
2 runc 1.1.14 是一个命令行工具端,根据OCI(开放容器组织)的标准来创建和运行容器。
3 flannel 1.0.1 网络插件
5 Calico 3.28.2 网络插件
4 kubeadm 1.28.15 kubernetes 安装工具
5 kubectl 1.28.15 kubernetes 命令行工具
6 kubelet 1.28.15 一个在集群中每个节点(node)上运行的代理。

准备 linux 环境

本文档使用的是 ubuntu-24.04.1-live-server。

  • 为了方便识别,将主机名称修改,修改的信息参考下方的列表信息。
hostnamectl set-hostname k8s-master
hostnamectl set-hostname k8s-slave-1
hostnamectl set-hostname k8s-salve-2

并将IP和hostname信息添加至 hosts

cat >> /etc/hosts << EOF
128.196.66.10 k8s-master
128.196.66.11 k8s-slave-1
128.196.66.12 k8s-slave-2
EOF
  • 将准备完成的虚拟机根据官方要求,将虚拟机硬件配置进行调整

    • CPU 修改为 2 核
    • 内存修改为 2GB
  • 关闭 firewalld
    由于是学习环境,直接关闭防火墙。生产环境建议开启系统防火墙保证安全性,根据官方文档检查所需端口,开启必要的端口。

    systemctl stop firewalld
    systemctl disable firewalld
  • 关闭交换分区
    为了保证 kubelet 正常工作,你 必须 禁用交换分区。 在 Kubernetes 1.22 之前,节点不支持使用交换内存,并且默认情况下, 如果在节点上检测到交换内存配置,kubelet 将无法启动。 在 1.22 以后,可以 逐个节点地启用交换内存支持。但这还在 alpha 阶段。

    # 查看swap分区情况
    swapon
    free -h
    # 关闭所有 swap 分区
    # 临时关闭
    swapoff -a
    # 永久关闭
    vim /etc/fstab # 将 swap 行注释,保存退出。然后重启服务器。
    # sed -i '/swap/s/^/#/g' /etc/fstab
  • 环境详情

    IP 服务器名称 安装服务
    128.196.66.10 k8s-master containerd.io
    kubeadm
    kubectl
    kubelet
    caclico
    128.196.66.11 k8s-slave-1 containerd.io
    kubeadm
    kubectl
    kubelet
    calico
    128.196.66.12 k8s-slave-2 containerd.io
    kubeadm
    kubectl
    kubelet
    calico

    环境准备完成后使用 Xshell (官方家庭版本),进行SSH连接。可使用另一个开源工具 WindTerm 连接,此客户端是由于办公时网络限制且不能安装软件, 通过此软件进行 SSH 连接。

    安装

    安装过程中,若没有指出仅在 slave 端安装,默认所有服务器上都需要安装对应的服务。本文使用的是 root 用户进行安装的,如果是其他用户安装需要 su

    安装容器运行时

    本文档安装的是 containerd 容器运行时,参考文档 【Getting started with containerd】。本文档使用的是 1.7.22。使用 apt 的安装方式进行安装。

    安装前准备

    • 转发 IPv4 并使 iptables 可以桥接流量

      cat <<EOF | tee /etc/modules-load.d/k8s.conf
      overlay
      br_netfilter
      EOF
      sudo modprobe overlay
      sudo modprobe br_netfilter
      # sysctl params required by setup, params persist across reboots
      cat <<EOF | 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
      
      # Apply sysctl params without reboot
      sudo sysctl --system
    • Cgroup 驱动
      本文档使用的是 Cgroup V1 版本,在运行中的 POD 修改 Cgroup 驱动是不明智的,因为重新修改后仍然可能会出现问题。 kubernetes 仅支持相同版本的 cgroup 驱动,因此建议在搭建集群的时候就需要考虑使用 Cgroup V1 还是 V2。 修改配置文件,设置为 SystemdCgroup = true,此操作会在安装配置中修改对应的内容。

    • 移除旧版本

      apt remove -y containerd.io
    • 配置镜像,通过阿里云开源镜像站配置

      # step 1: 安装必要的一些系统工具
      sudo apt update
      sudo apt -y install apt-transport-https ca-certificates curl software-properties-common
      # step 2: 安装GPG证书
      # 国内环境
      # curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
      # Step 3: 写入软件源信息
      # 国内环境
      # sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
      # Step 4: 更新并安装Docker-CE
      sudo apt -y update
    • 安装 containerd.io 并启动

      # 安装 containerd.io,由于此脚本会有docker相关的,仅复制安装了一个内容
      apt -y install containerd # 此包包含 runc
    • 生成配置文件,并将 Cgroup 开启

      # 生成默认的配置文件
      [ ! -d /etc/containerd ] && mkdir -p /etc/containerd
      containerd config default > /etc/containerd/config.toml
      # 修改配置文件中的内容,将 SystemdCgroup 开启,Ubuntu 22.04 默认的 cgroup 版本是 v2。
      sed -i '/SystemdCgroup/s/SystemdCgroup =.*/SystemdCgroup = true/' /etc/containerd/config.toml
      # 修改配置文件中的内容,将 *sandbox_image* 源修改为 阿里云,针对国内环境
      # sed -i 's#registry.k8s.io#registry.cn-hangzhou.aliyuncs.com/google_containers#' /etc/containerd/config.toml
      # 重启服务
      systemctl restart containerd

    安装 kubeadm kubelet kubectl

    • 安装kubernetes 1.28
      # 配置 阿里云 kubernetes 源
      # sudo apt-get update
      # sudo apt-get install -y apt-transport-https ca-certificates curl gpg
      # curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/deb/Release.key |
      #     gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
      # echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/deb/ /" |
      #     tee /etc/apt/sources.list.d/kubernetes.list
      # sudo apt-get update
      # sudo apt-mark hold kubelet kubeadm kubectl
      sudo apt-get update
      # apt-transport-https may be a dummy package; if so, you can skip that package
      sudo apt-get install -y apt-transport-https ca-certificates curl gpg
      curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key 
        sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
      
      # This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list
      echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' |
        sudo tee /etc/apt/sources.list.d/kubernetes.list
      sudo apt-get update
      sudo apt-get install -y kubeadm=1.28.15* kubectl=1.28.15* kubelet=1.28.15*
      sudo apt-mark hold kubelet kubeadm kubectl
      # kubelet 会在安装完成后处于 'auto-restart' 的状态,这是由于在陷入了一个等待 `kubeadm` 指令的死循环。

    安装其他工具(可选)

    初始化 kubeadm(master)

    • 初始化前准备

      # 生成 bootstrapTokens 的 token 值
      ## bootstrapTokens 是由 [a-z0-9](6位)+ '.' + [a-z0-9](16位) 组成的17位字符串
      token_value=$(kubeadm token generate)
      # 创建配置文件,详细如下
      mkdir /app/data/k8s/ -p
      cat > /app/data/k8s/kubeadm.yaml << EOF
      apiVersion: kubeadm.k8s.io/v1beta3
      bootstrapTokens:
      - groups:
        - system:bootstrappers:kubeadm:default-node-token
        token: ${token_value}
        ttl: 24h0m0s
        usages:
        - signing
        - authentication
      kind: InitConfiguration
      localAPIEndpoint:
        advertiseAddress: 128.196.66.10
        bindPort: 6443
      nodeRegistration:
        criSocket: unix:///run/containerd/containerd.sock
        imagePullPolicy: IfNotPresent
        name: k8s-master
        taints:
        - effect: NoSchedule
          key: node-role.kubernetes.io/master
      ---
      apiServer:
        timeoutForControlPlane: 4m0s
      apiVersion: kubeadm.k8s.io/v1beta3
      certificatesDir: /etc/kubernetes/pki
      clusterName: kubernetes
      controllerManager: {}
      dns: {}
      etcd:
        local:
          dataDir: /var/lib/etcd
      kind: ClusterConfiguration
      kubernetesVersion: v1.28.15
      networking:
        dnsDomain: cluster.local
        podSubnet: 10.244.0.0/16
        serviceSubnet: 10.96.0.0/12
      scheduler: {}
      EOF
      # 查看可用镜像
      kubeadm config images list --config /app/data/k8s/kubeadm.yaml
      # 下载镜像
      kubeadm config images pull --config /app/data/k8s/kubeadm.yaml
      # 初始化 master
      kubeadm init --config /app/data/k8s/kubeadm.yaml # 初始过程比较慢
      # 安装完成后 kube-system 命名空间会启动 'coredns' 和 'kube-proxy' pod
    • 输出如下表示初始化成功

      [kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
      [addons] Applied essential addon: CoreDNS
      [addons] Applied essential addon: kube-proxy
      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 128.196.66.10:6443 --token abcdef.0123456789abcdef \
      	--discovery-token-ca-cert-hash sha256:a917905179990bb84d948f6b0e3592dfeae3d4400a10b03de7f5760717328e10 

      最后一行也显示了 slave 加入master 的详细命令。后续 slave 加入master 输入此内容即可。

    • 根据输出的内容操作

      mkdir -p $HOME/.kube
      sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
      sudo chown $(id -u):$(id -g) $HOME/.kube/config
      export KUBECONFIG=/etc/kubernetes/admin.conf >> ~/.bash_profile
      source ~/.bash_profile
    • 查看 node

      # 由于还未安装网络组件,状态都为 NotReady
      # kubectl get node 同样可以sudo
      kubectl get nodes
      # 使用以下命令也可以查看 node 详细信息 
      kubectl describe node k8s-master | grep network 

    slave 加入集群

    # 根据kubeadm master 安装成功后提示的内容进行操作
    kubeadm join 128.196.66.10:6443 --token abcdef.0123456789abcdef \
    	--discovery-token-ca-cert-hash sha256:a917905179990bb84d948f6b0e3592dfeae3d4400a10b03de7f5760717328e10 

    master 查看集群

    # 查看 node 信息
    kubectl get node # kubectl get nodes 相同信息
    # 查看 pods 信息
    kubectl get pods -n kube-system

    coredns 状态为 Pending,这为正常情况,因为网络组件还未安装。 kube-proxy 使用的是默认的 iptables 模式,如果需要使用 ivps 模式请参考 https://docs.tigera.io/calico/3.28/networking/configuring/use-ipvs

    NAME                                 READY   STATUS    RESTARTS   AGE
    coredns-7f74c56694-pq8hb             0/1     Pending   0          2m19s
    coredns-7f74c56694-xrzvv             0/1     Pending   0          2m19s
    etcd-k8s-master                      1/1     Running   0          2m33s
    kube-apiserver-k8s-master            1/1     Running   0          2m32s
    kube-controller-manager-k8s-master   1/1     Running   0          2m32s
    kube-proxy-87wtl                     1/1     Running   0          2m2s
    kube-proxy-klxr8                     1/1     Running   0          109s
    kube-proxy-xn69c                     1/1     Running   0          2m20s
    kube-scheduler-k8s-master            1/1     Running   0          2m32s

    安装网络组件-CNI

    网络是 kubernetes 的核心组成部分。

    参考文档: https://zhuanlan.zhihu.com/p/513231562 https://developer.aliyun.com/article/1245323

    flannel

    由于是学习部署阶段,选择 flannel 作为 k8s 的网络组件。后期考虑单独学习 calico,由于此组件有更多的配置项,可以更容易满足生产条件。

    • 下载配置文件
      # 国内网络可能无法下载,下文会将详细的内容贴出
      wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    • 修改配置文件
      根据注释内容进行修改,主要为添加网卡信息。
      ---
      apiVersion: policy/v1beta3  # 将v1beta1 修改为 v1beta3,不然会有警告,不修改也不影响使用。
      kind: PodSecurityPolicy
      metadata:
        name: psp.flannel.unprivileged
        annotations:
          seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
          seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
          apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
          apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
      spec:
        privileged: false
        volumes:
        - configMap
        - secret
        - emptyDir
        - hostPath
        allowedHostPaths:
        - pathPrefix: "/etc/cni/net.d"
        - pathPrefix: "/etc/kube-flannel"
        - pathPrefix: "/run/flannel"
        readOnlyRootFilesystem: false
        # Users and groups
        runAsUser:
          rule: RunAsAny
        supplementalGroups:
          rule: RunAsAny
        fsGroup:
          rule: RunAsAny
        # Privilege Escalation
        allowPrivilegeEscalation: false
        defaultAllowPrivilegeEscalation: false
        # Capabilities
        allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
        defaultAddCapabilities: []
        requiredDropCapabilities: []
        # Host namespaces
        hostPID: false
        hostIPC: false
        hostNetwork: true
        hostPorts:
        - min: 0
          max: 65535
        # SELinux
        seLinux:
          # SELinux is unused in CaaSP
          rule: 'RunAsAny'
      ---
      kind: ClusterRole
      apiVersion: rbac.authorization.k8s.io/v1
      metadata:
        name: flannel
      rules:
      - apiGroups: ['extensions']
        resources: ['podsecuritypolicies']
        verbs: ['use']
        resourceNames: ['psp.flannel.unprivileged']
      - apiGroups:
        - ""
        resources:
        - pods
        verbs:
        - get
      - apiGroups:
        - ""