本文更新于 2022-12-26,Kuberbetes 更新非常快,请注意可能有过时信息

前言

最近公司业务场景要一个高可用的集群节点用于工业环境,而且数据量非常大,刚刚好手上有一台 IDC 淘来的机架服务器,借机复习一下 K8s 的手动部署。

系统资源

虚拟机配置

$ free -h
               total        used        free      shared  buff/cache   available
Mem:            31Gi       901Mi        25Gi       4.0Mi       5.4Gi        30Gi
Swap:             0B          0B          0B
$ cat /proc/cpuinfo | grep -c processor
32

集群的安装

由于只安装一个 Control Panel,我们只需要配置好以下几个组件即可

  • ETCD
  • Kube-ApiServer
  • Kube-Controller-Manager
  • Kube-Scheduler
  • Kube-Proxy
  • Kubelet

基础的配置

安装基础软件

$ apt install bash-completion git net-tools sudo build-essential golang conntrack
$ go version # 这里的 Go 版本需要大于 1.12
go version go1.18.1 linux/amd64

设置NTP时间

$ sudo apt install chrony
$ sudo systemctl status chrony
● chrony.service - chrony, an NTP client/server
   Loaded: loaded (/lib/systemd/system/chrony.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2021-06-27 03:07:41 AKDT; 55s ago
     Docs: man:chronyd(8)
           man:chronyc(1)
           man:chrony.conf(5)
 Main PID: 2028 (chronyd)
    Tasks: 2 (limit: 4915)
   Memory: 1.6M
   CGroup: /system.slice/chrony.service
           ├─2028 /usr/sbin/chronyd -F -1
           └─2029 /usr/sbin/chronyd -F -1

Jun 27 03:07:41 sxueck systemd[1]: Starting chrony, an NTP client/server...
Jun 27 03:07:41 sxueck chronyd[2028]: chronyd version 3.4 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASYNCDNS +SECHASJun 27 03:07:41 sxueck chronyd[2028]: Initial frequency 6.947 ppm
Jun 27 03:07:41 sxueck chronyd[2028]: Loaded seccomp filter
Jun 27 03:07:41 sxueck systemd[1]: Started chrony, an NTP client/server.
Jun 27 03:07:48 sxueck chronyd[2028]: Selected source 183.177.72.201
Jun 27 03:07:49 sxueck chronyd[2028]: Selected source 60.248.114.17
$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^- tock.ntp.infomaniak.ch        1  10   337   841   +491us[ +491us] +/-  110ms
^- time.cloudflare.com           3  10   377    72  -5089us[-5089us] +/-  121ms
^* 139.199.215.251               2  10   377  1017  -2594us[-3319us] +/-   40ms
^- makaki.miuku.net              2  10   333   24m    +78ms[  +77ms] +/-  106ms
$ sudo timedatectl set-timezone Asia/Shanghai

配置 L2 网桥给 CNI 插件使用

$ sudo /sbin/modprobe br_netfilter
$ cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
$ sudo sysctl -p /etc/sysctl.d/k8s.conf

至此,我们可以开始安装集群依赖组件

证书的生成

CA 根证书

K8s 的许多组件都需要依赖证书进行操作,例如 Kube-ApiServer 的认证等,我们这里通过一个自签 CA 证书进行整个集群的签名,但是请注意千万保管好这个证书

在 Kubernetes 集群中,各个组件间通过 TLS 进行通信。组件的证书可以代表其唯一性( Common Name )。搭建 PKI 可以自由签发证书给需要的组件。

安装 cfssl 证书生成工具

$ sudo mkdir /etc/kubernetes/pki -p
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct # 可选步骤
$ go install github.com/cloudflare/cfssl/cmd/cfssl@latest
$ go install github.com/cloudflare/cfssl/cmd/cfssljson@latest
$ cp ~/go/bin/cfssl ~/go/bin/cfssljson /usr/local/bin
$ cfssl version
Version: dev
Runtime: go1.18.1

开始签署 CA 证书,为了图省事,反正都是自签证书,直接配置 Expiry 为 99 年

$ mkdir ~/ssl && cd ~/ssl

# 证书配置文件
$ cat > ca-ssl.json << EOF
{
    "signing": {
        "default": {
            "expiry": "867240h"
        },
        "profiles": {
            "kubernetes": {
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ],
                "expiry": "867240h"
            }
        }
    }
}
EOF

# CA 签名申请
$ cat > ca-csr.json << EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "Guangdong",
            "L": "ShenZhen",
            "O": "k8s",
            "OU": "system"
        }
    ],
    "ca": {
        "expiry": "867240h"
    }
}
EOF

$ cfssl gencert -initca ca-csr.json| cfssljson -bare ca
2022/08/02 22:41:09 [INFO] generating a new CA key and certificate from CSR
2022/08/02 22:41:09 [INFO] generate received request
2022/08/02 22:41:09 [INFO] received CSR
2022/08/02 22:41:09 [INFO] generating key: rsa-2048
2022/08/02 22:41:10 [INFO] encoded CSR
2022/08/02 22:41:10 [INFO] signed certificate with serial number 224027550201873771575880785170986119805992526820

$ ll ca*pem
-rw------- 1 root root 1.7K Aug  2 22:41 ca-key.pem
-rw-r--r-- 1 root root 1.3K Aug  2 22:41 ca.pem

$ sudo cp ca*pem /etc/kubernetes/pki

Kube-ApiServer 认证证书

Kube-ApiServer 组件的证书一般都会作为集群的入口,例如使用 Kubectl 的时候就是与该证书进行签名匹配,如果出现不在签名范围的请求或者匹配异常则会出现 x509 错误

下面的签名请求配置中,hosts 字段的 IP 为所有 ApiServer 节点地址,这里可以预留几个 IP,以备以后扩容

$ cat > kubernetes-csr.json << EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "O": "Kubernetes",
      "OU": "Kubernetes From Scratch",
      "ST": "BeiJing"
    }
  ]
}
EOF

$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-ssl.json \
  -hostname=${HOSTNAME},121.201.119.45,127.0.0.1,kubernetes.default \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes
2022/12/26 13:12:17 [INFO] generate received request
2022/12/26 13:12:17 [INFO] received CSR
2022/12/26 13:12:17 [INFO] generating key: rsa-2048
2022/12/26 13:12:18 [INFO] encoded CSR
2022/12/26 13:12:18 [INFO] signed certificate with serial number 262321052924478574973572968731187695389058393692

Kubelet 节点证书

该组件作为 Worker 节点的代理组件,运行在每个节点上,其作用定期从 Kube-ApiServer 组件接收新的或者修改的 Pod 规范,并确保 Pod 及其容器在规范标准下运行,因此有多少组件就要生成多少个 Kubelet 认证证书,同时节点通讯失败可以首先怀疑 Kubelet 问题。

这里使用脚本进行生成

$ NODES=$(cat <<-END
    master-1 127.0.0.1
END
)

function genpem() {
    hostname=$1
    ip=$2
    echo $hostname $ip

    cat > "kubelet/${hostname}-csr.json" <<EOF
    {
      "CN": "system:node:${hostname}",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [
        {
          "C": "CN",
          "L": "BeiJing",
          "O": "system:nodes",
          "OU": "Kubernetes From Scratch",
          "ST": "BeiJing"
        }
      ]
    }
EOF

    cfssl gencert \
      -ca=ca.pem \
      -ca-key=ca-key.pem \
      -config=ca-ssl.json \
      -hostname=${hostname},${ip} \
      -profile=kubernetes \
      ${hostname}-csr.json | cfssljson -bare ${hostname}
}

while read -r line; do
    a=($(echo "$line" | tr ' ' '\n'))
    hostname="${a[0]}"
    ip="${a[1]}"
    genpem $hostname $ip
done <<< "$NODES"

执行脚本即可生成

$ bash gen-kubelet.sh
master-1 127.0.0.1
2022/12/26 13:48:47 [INFO] generate received request
2022/12/26 13:48:47 [INFO] received CSR
2022/12/26 13:48:47 [INFO] generating key: rsa-2048
2022/12/26 13:48:47 [INFO] encoded CSR
2022/12/26 13:48:47 [INFO] signed certificate with serial number 161634764867939977984899022143501924846819499448

$ ls -al
...
-rw-r--r-- 1 idc idc 1110 Dec 26 13:48 master-1.csr
-rw------- 1 idc idc 1679 Dec 26 13:48 master-1-key.pem
-rw-rw-r-- 1 idc idc 1489 Dec 26 13:48 master-1.pem

其他证书配置

其他证书的配置大同小异,无非就是 Json 配置后用 cfssl 生产,这里一次性罗列出来

Kube-Controller-Manager 组件

$ cat > kube-controller-manager-csr.json << EOF
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "O": "system:kube-controller-manager",
      "OU": "Kubernetes From Scratch",
      "ST": "BeiJing"
    }
  ]
}
EOF

$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-ssl.json \
  -profile=kubernetes \
  kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

Kube-Proxy 组件

$ cat > kube-proxy-csr.json << EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "O": "system:node-proxier",
      "OU": "Kubernetes From Scratch",
      "ST": "BeiJing"
    }
  ]
}
EOF

$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-ssl.json \
  -profile=kubernetes \
  kube-proxy-csr.json | cfssljson -bare kube-proxy