文章

使用Kubeadm搭建K8S集群

这篇博客记录搭建一个 1Master+2Node 的 K8S 集群的过程。所使用的关键软件的版本:

名称版本
Ubuntu22.04
kubeadmin1.29.0
containerd1.7.13
runc1.1.12

以下所有 bash 命令都默认在 root 用户权限下执行。

1 准备机器

使用物理机

我使用的物理机为 3 台 Dell 的 Vostro 主机,它采用一颗 Intel(R) Core(TM) i7-10700 CPU,主频 2.90GHz,8 核心,16 线程,并且有 16GB 内存,实机照片如下:

Dell Vostro

  1. 安装 Ubuntu 22.04 live server

    只需要安装服务器版本就够了,用不着桌面环境,安装时间可以缩短。

    安装过程中记得选上 OpenSSH 服务,默认是不选的。

  2. 组网

    我使用机器自带的无线网卡组网,安装完系统后,进入 /etc/netplan/ 目录,把原来的配置文件移除(重命名):

    1
    2
    
    mv 00-installer-config.yaml 00-installer-config.yaml.bak
    mv 00-installer-config-wifi.yaml 00-installer-config-wifi.yaml.bak
    

    然后写一个新的配置文件 00-config.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    network:
      version: 2
      wifis:
        wlp4s0:
          dhcp4: false
          access-points:
            MY_SSID: # 填上无线网络名称
              password: MY_PASSWORD # 填上无线网络密码
          addresses:
            - 10.42.0.200/24
          routes:
            - to: default
              via: 10.42.0.1
          nameservers:
            addresses:
              - 10.42.0.1
              - 114.114.114.114
    

    这里我直接分配了固定 IP,方便之后远程连接。

  3. 配置 SSH 认证

    把自己的公钥添加到 /root/.ssh/authorized_keys 文件中,建议后面的操作都远程进行,相比在物理机上,远程操作可以方便地在主机间拷贝命令行。

使用 Hyper-V 虚拟机

虚拟机的好处就是可以复制,我们先创建一个 Ubuntu 22.04 的虚拟机,然后复制两份,这样就有了 3 台机器。

  1. 创建虚拟机并安装 Ubuntu 22.04

    这里不再赘述,安装过程和物理机一样。

  2. 设置硬件配额

    K8S 至少要 2GB 的内存,在 Hyper-V 管理器的虚拟机设置中,把内存最低限制调整到 2GB(如果启用了动态内存)。

  3. 复制虚拟机

    在 Hyper-V 管理器中,选择“导入虚拟机”,找到上面安装的那个虚拟机的文件夹,记得在导入类型页面选择“复制新虚拟机(创建新的唯一 IO)”,就能实现虚拟机的复制了。

    复制的虚拟机和原来的虚拟机具有完全一样的硬件配置,所以不必再修改最低内存限制。

  4. 修改复制虚拟机的 hostname、MAC 地址和 product_uuid

    安装 K8S 要求这三个值都不能相同。

    hostname 在操作系统中修改:

    1
    
    hostnamectl set-hostname xxx # 改为新主机名
    

    在物理机上,MAC 地址存储在网卡中,product_uuid 存储在主板的 BIOS 中。Hyper-V 默认会开启随机 MAC 地址,所以只要启动一遍复制的虚拟机,这两个值就会自动变化。

    product_uuid 必须手动在虚拟机外面修改。关闭虚拟机,然后在宿主机的管理员 PowerShell 中运行下面的脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    $VMname="填写Hyper-V中的虚拟机名"
    $vmx = Get-WmiObject -Namespace root\virtualization\v2 -Class msvm_virtualsystemsettingdata
    #每次操作选择一台虚拟机
    $CurrentSettingsData = $vmx | Where-Object { $_.ElementName -eq $VMname }
    #检查一下老的GUID
    $CurrentSettingsData.BIOSGUID
    #生成新GUID
    $GUID = [System.Guid]::NewGuid()
    #重新写入GUID,注意要带{},这是非常重要的细节
    $CurrentSettingsData.BIOSGUID = "{" + $GUID.Guid.ToUpper() + "}"
    #生成一个ModifySystemSettings的空对象
    $VMMS = Get-WmiObject -Namespace root\virtualization\v2 -Class msvm_virtualsystemmanagementservice
    $ModifySystemSettingsParams = $VMMS.GetMethodParameters('ModifySystemSettings')
    #配置这个属性,需要特殊的转换方式
    $ModifySystemSettingsParams.SystemSettings = $CurrentSettingsData.GetText([System.Management.TextFormat]::CimDtd20)
    #写入
    $VMMS.InvokeMethod('ModifySystemSettings', $ModifySystemSettingsParams, $null)
    
  5. 配置 SSH 认证

    和物理机一样。

2. 配置操作系统环境

从现在开始,所有操作都可以远程进行。

  1. 配置 hosts

    写一个 hosts 文件,把所有机器的 IP 和 hostname 都写上去,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    127.0.0.1 localhost
    10.42.0.200 m0
    10.42.0.210 n0
    10.42.0.211 n1
    
    # The following lines are desirable for IPv6 capable hosts
    ::1     ip6-localhost ip6-loopback
    fe00::0 ip6-localnet
    ff00::0 ip6-mcastprefix
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    

    然后用 scp 命令把这个文件拷贝到所有机器上的 /etc/hosts 文件中,这样以后集群内相互访问就不用写 IP 地址了。

  2. 禁用 swap

    K8S 不允许 swap,所以需要禁用 swap:

    1
    
    nano /etc/fstab # 注释掉 swap 的那一行
    
  3. 配置网络

    我们后面默认安装的 K8S 使用 iptables 作为网络插件,需要启用 br_netfilter 内核模块:

    1
    
    nano /etc/modules-load.d/modules.conf # 添加一行:br_netfilter
    

    然后配置 sysctl

    1
    2
    3
    4
    5
    
    cat > /etc/sysctl.d/k8s.conf << EOF
    net.ipv4.ip_forward = 1
    net.bridge.bridge-nf-call-iptables = 1
    net.bridge.bridge-nf-call-ip6tables = 1
    EOF
    

    我安装的 Ubuntu 22.04 live server 版本没有防火墙,如果有的话要关闭,可以用下面的命令:

    1
    
    ufw disable
    

3 安装 containerd 和 runc

  1. 安装 containerd

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    export VERSION="1.7.13"
    apt install -y libseccomp2 wget
    
    wget https://github.com/containerd/containerd/releases/download/v${VERSION}/cri-containerd-cni-${VERSION}-linux-amd64.tar.gz
    wget https://github.com/containerd/containerd/releases/download/v${VERSION}/cri-containerd-cni-${VERSION}-linux-amd64.tar.gz.sha256sum
    sha256sum --check cri-containerd-cni-${VERSION}-linux-amd64.tar.gz.sha256sum
    
    tar --no-overwrite-dir -C / -xzf cri-containerd-cni-${VERSION}-linux-amd64.tar.gz
    systemctl daemon-reload
    systemctl enable containerd
    
  2. 配置 containerd

    按我这个教程走,到这里的话需要修改 containerd 的一处默认配置。运行下面的命令生成 containerd 的默认配置文件:

    1
    2
    
    mkdir -p /etc/containerd/
    containerd config default > /etc/containerd/config.toml
    

    然后修改一处:

    1
    2
    3
    4
    
    # ...
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
          SystemdCgroup = true # 原本是 false,不改的话容器会间歇性崩溃
    # ...
    
  3. (可选)配置 containerd 使用网络代理

    主要是为了访问镜像仓库,例如 Docker Hub,修改/etc/systemd/system/containerd.service:

    1
    2
    3
    4
    5
    6
    7
    
    [Service] # 在这个段落下添加
    Environment="http_proxy=http://10.42.0.1:7890"
    Environment="https_proxy=http://10.42.0.1:7890"
    Environment="HTTP_PROXY=http://10.42.0.1:7890"
    Environment="HTTPS_PROXY=http://10.42.0.1:7890"
    ExecStartPre=-/sbin/modprobe overlay
    ExecStart=/usr/local/bin/containerd
    
  4. 安装 runc

    1
    2
    
    wget https://github.com/opencontainers/runc/releases/download/v1.1.12/runc.amd64
    install -m 755 runc.amd64 /usr/local/sbin/runc
    
  5. 重启机器

    上面的操作都需要重启机器才能生效:

    1
    
    reboot
    
  6. 验证 containerd 和 runc

    拉取一个 helloworld 镜像运行看看:

    1
    2
    
    ctr image pull docker.io/library/hello-world:latest
    ctr run --rm docker.io/library/hello-world:latest hello-world
    

4 安装 Kubernetes

  1. 安装 CNI 插件

    这个插件用来使能容器间的网络通讯。

    1
    2
    3
    
    wget https://github.com/containernetworking/plugins/releases/download/v1.4.0/cni-plugins-linux-amd64-v1.4.0.tgz
    mkdir -p /opt/cni/bin
    tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.4.0.tgz
    
  2. 下载 kubeadm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    sudo apt-get update
    # apt-transport-https 可能是一个虚拟包(dummy package);如果是的话,你可以跳过安装这个包
    sudo apt-get install -y apt-transport-https ca-certificates curl gpg
    # 如果 `/etc/apt/keyrings` 目录不存在,则应在 curl 命令之前创建它,请阅读下面的注释。
    # sudo mkdir -p -m 755 /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    # 此操作会覆盖 /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.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl
    sudo apt-mark hold kubelet kubeadm kubectl
    

5 组建集群

2~4 的步骤需要在所有机器上执行,都成功之后,设定一个机器为 Master,其它两台为 Node。

Master 节点

  1. 运行 kubeadm init

    成功的话会显示如下的信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    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 172.17.211.1:6443 --token 41as75.eo1kvp6s6osjnevr \
            --discovery-token-ca-cert-hash sha256:e1370957613cd3a32232eabe303f4e7ca06aacd77fb06c84046226928f024cd8
    

    注意最后一行,这是用来加入 Node 的命令。

Node 节点

  1. 运行 kubeadm join

    运行上面打印出来的命令:

    1
    2
    
    kubeadm join 172.17.211.1:6443 --token 41as75.eo1kvp6s6osjnevr \
            --discovery-token-ca-cert-hash sha256:e1370957613cd3a32232eabe303f4e7ca06aacd77fb06c84046226928f024cd8
    

    注意这个命令里的 token 有时限(好像是一个小时?),如果失效了,需要在 Master 生成新的 token,具体怎么做到网上再找资料吧。

完成

如果 kubeadm 运行出错,使用 kubeadm reset 重置集群。

成功后,测试一下组件的集群能不能用。建一个 test.yaml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld
spec:
  replicas: 3
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
        - name: helloworld
          image: docker.io/library/hello-world:latest
          ports:
            - containerPort: 8080

使用 kubectl get pods 查看创建了哪些 Pod,然后使用 kubectl logs <pod_name> 查看 Pod 的输出,最后使用 kubectl delete deployment helloworld 删除这个 Deployment。

本文由作者按照 CC BY 4.0 进行授权