安装准备

区别于使用 kubeadm 部署集群时所有核心组件都托管在集群上。二进制安装则采用守护进程的方式直接将各个组件运行在宿主机,生产环境更为推荐。


对于生产环境,不推荐在 Master 节点再安装 kubelet、kube-proxy 以及容器运行时 docker + containerd,使得它同时也是 Worker 节点运行。但是测试环境由于机器有限,为了使得集群看起来节点不那么少,可以也安装上用于测试。


生产环境部署架构图:

二进制安装docker软链接 kubernetes二进制安装_docker

本文部署方案由于机器性能原因,暂时只用 5 个节点,ETCD,Nginx + Keepalived 等组件也安装在 Master 节点。

主机

IP

系统

配置

初始化安装服务

master-01

192.168.200.101

CentOS 7.9

4C/4G/20G

nginx,keepalived,etcd,apiserver,controller-manager,scheduler,kubectl

master-02

192.168.200.102

CentOS 7.9

4C/4G/20G

nginx,keepalived,etcd,apiserver,controller-manager,scheduler,kubectl

master-03

192.168.200.103

CentOS 7.9

4C/4G/20G

nginx,keepalived,etcd,apiserver,controller-manager,scheduler,kubectl

worker-01

192.168.200.104

CentOS 7.9

4C/4G/20G

docker,containerd,kube-proxy,kubelet,coredns

worker-02

192.168.200.105

CentOS 7.9

4C/4G/20G

docker,containerd,kube-proxy,kubelet,coredns


集群相关网络规划:

名称

IP / 网段

说明

SLB VIP

192.168.200.100

keepalived 提供

Pod 网段

172.16.0.0/16

集群规划

Service 网段

10.10.0.0/16

集群规划


ETCD 和 Kubernetes 安装所需安装包:

安装包

版本号

说明

下载地址

etcd

3.5.4

Kubernetes 核心存储,同时作为 CoreDNS 的存储

点击下载

kubernetes

1.25.0

Kubernetes Server 二进制安装包

点击下载

配置证书签发环境

Kubernetes 集群各组件之间的通信都会涉及证书验证,在使用 Kubeadm 安装集群的时候就遇到因为证书有效期问题,各种替换处理。所以在二进制安装签发证书的时候,尽可能证书做到一劳永逸。同时,证书的签发也是整个 Kubernetes 集群二进制安装中最难的部分。

选择任意 Master 节点自建证书签发环境。这里使用 master-01 节点作为证书签发节点,下文中涉及证书签发除非有特别说明,否则都在该节点执行。

下载 cfssl 相关文件,具体不同版本可以去 Github 下载:

https://github.com/cloudflare/cfssl/releases

本文使用的是 1.6.0 版本,三个 Master 节点都安装,其它两个节点用作备份使用:

wget https://github.com/cloudflare/cfssl/releases/download/v1.6.0/cfssl_1.6.0_linux_amd64 -O /usr/bin/cfssl
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.0/cfssljson_1.6.0_linux_amd64 -O /usr/bin/cfssl-json
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.0/cfssl-certinfo_1.6.0_linux_amd64 -O /usr/bin/cfssl-certinfo
chmod 755 /usr/bin/cfssl*

文件说明:

  • cfssl:证书签发工具。
  • cfssl-json:将 json 格式的证书信息转换成文件格式。
  • cfssl-certinfo:验证证书信息。

签发 CA 证书

CA 根证书是签发其他证书的基础,在使用 kubeadm 安装集群查看证书的有效期的时候是能看到有三个 CA 证书:

  • ca
  • etcd-ca
  • front-proxy-ca

它们的有效期都是 10 年。为了做到二进制安装也更贴近官方最佳实践,本文的规划也是如此。

同时,对于证书的签发,有两个非常重要的注意事项:

  1. 证书有效期:二进制安装由于证书签发是有用户自己决定的,所以推荐尽可能签长一点,一劳永逸。
  2. IP 白名单:某些证书是需要绑定 IP 地址的,为了方便后期的节点扩容,可以在 hosts 字段多配置一些 IP 用作备用。


不管是 Master 节点还是 Worker 节点都是需要保存证书的,在 kubeadm 安装的集群中,证书都被保存在了 /etc/kubernetes/pki 下面。

二进制安装环境为了对证书有更好的管理,使用自定义的目录用于存储所需的证书。

在所有 Master 和 Worker 节点都创建证书存储目录:

mkdir -p /ezops/certs
cd /ezops/certs


在 master-01 节点进行证书签发。

Kubernetes CA

创建生成 Kubernetes 集群 CA 证书请求的 Json 文件:

cat > ca-csr.json << EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "GuangDong",
            "L": "ShenZhen",
            "O": "kubernetes",
            "OU": "ops"
        }
    ],
    "ca": {
        "expiry": "876000h"
    }
}
EOF

生成证书:

cfssl gencert -initca ca-csr.json | cfssl-json -bare ca


ETCD CA

创建生成 ETCD 集群 CA 证书请求的 Json 文件:

cat > etcd-ca-csr.json << EOF
{
    "CN": "etcd",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "GuangDong",
            "L": "ShenZhen",
            "O": "etcd",
            "OU": "ops"
        }
    ],
    "ca": {
        "expiry": "876000h"
    }
}
EOF

生成证书:

cfssl gencert -initca etcd-ca-csr.json | cfssl-json -bare etcd-ca


front-proxy CA

创建生成 front-proxy 的 CA 证书请求的 Json 文件:

cat > front-proxy-ca-csr.json << EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "ca": {
        "expiry": "876000h"
    }
}
EOF

生成证书:

cfssl gencert -initca front-proxy-ca-csr.json | cfssl-json -bare front-proxy-ca


在请求 Json 中有几个字段需要特别关注一下:

  • CN:Common Name,所有 csr 文件都必须有字段,对于不同证书,一般拥有不同的含义。
  • 普通 SSL 证书,一般为网站域名。
  • 代码签名证书,一般为申请单位名称。
  • 客户端证书,一般为证书申请者的姓名。
  • 在 Kubernetes 集群中,apiserver 会从证书中提取该字段作为请求的用户名,所以在定义的时候需要注意。
  • O:Organization,apiserver 会从证书中提取该字段作为请求用户所属的组。
  • expiry:证书有效期,876000h 表示 100 年。


创建证书通用配置,该配置可以简化后面生成证书请求的 Json,算是公共配置:

cat > ca-config.json << EOF
{
    "signing": {
        "default": {
            "expiry": "876000h"
        },
        "profiles": {
            "kubernetes": {
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ],
                "expiry": "876000h"
            }
        }
    }
}
EOF


证书生成结果如图所示:

二进制安装docker软链接 kubernetes二进制安装_容器_02

签发 ETCD 证书

ETCD 集群作为 Kubernetes 集群运行的基础,独立于 Kubernetes 集群之外。可以先给它签发证书并进行安装部署。

值得注意的是,ETCD 证书签发是需要绑定 ETCD 集群节点部署机器的 IP 地址的,为了方便后期的扩容迁移,可以配置一些备份 IP。

创建生成证书的请求 Json 文件:

cat > etcd-csr.json << EOF
{
    "CN": "etcd",
    "hosts": [
        "127.0.0.1",
        "192.168.200.100",
        "192.168.200.101",
        "192.168.200.102",
        "192.168.200.103",
        "192.168.200.104",
        "192.168.200.105",
        "192.168.200.106",
        "192.168.200.107",
        "192.168.200.108",
        "192.168.200.109",
        "192.168.200.110"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [{
        "C": "CN",
        "ST": "GuangDong",
        "L": "ShenZhen",
        "O": "etcd",
        "OU": "ops"
    }]
}
EOF

生成证书:

cfssl gencert -ca=etcd-ca.pem -ca-key=etcd-ca-key.pem -config=ca-config.json -profile=kubernetes etcd-csr.json | cfssl-json -bare etcd

这里就会用到证书生成的通用配置 ca-config.json 文件并获取了它的 kubernetes 字段定义的信息。

签发完成后查询证书信息:

cfssl-certinfo -cert etcd.pem

其中 not_after 字段定义了证书的有效期,可以看到是 100 年以后。

当然,也可以使用 openssl 直接看证书有效期:

openssl x509 -in etcd.pem -noout -dates


证书生成结果如图所示:

二进制安装docker软链接 kubernetes二进制安装_kubernetes_03

此时就可以将这些配置证书目录下所有文件都分发到其它 Master 节点的证书目录用作备份和使用。

scp * root@192.168.200.102:/ezops/certs/
scp * root@192.168.200.103:/ezops/certs/

部署 ETCD 集群

从 Kubernetes v1.25 官方 CHANGELOG 中可以看到它已经支持 ETCD v3.5.4 版本了:

https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.25.md

将下载好的安装包上传到所有 Master 节点的 /ezops/packages/ 目录下,并执行下面的安装操作:


将上传好的 ETCD 安装包解压安装:

# 解压安装
cd /ezops/packages/
tar -xzf etcd-v3.5.4-linux-amd64.tar.gz
mv etcd-v3.5.4-linux-amd64 /ezops/service/etcd
cd /ezops/service/etcd/

# 配置目录
mkdir data conf logs docs bin
mv *md Documentation docs/
mv etcd* bin/

# 添加环境变量
cat >> /etc/profile << EOF
# ETCD 环境变量
export ETCD_HOME="/ezops/service/etcd"
export PATH=\$PATH:\$ETCD_HOME/bin
EOF

# 生效
source /etc/profile

# 查看版本
etcd --version


配置主配置文件:

# 本机 IP
IP=$(ip a | grep "192.168.200" | grep -v "100" | awk '{print $2}' | cut -d "/" -f 1)

cat > /ezops/service/etcd/conf/etcd.yml << EOF
name: etcd-$(hostname)
data-dir: /ezops/service/etcd/data
listen-client-urls: https://${IP}:2379,https://127.0.0.1:2379
advertise-client-urls: https://${IP}:2379,https://127.0.0.1:2379
listen-peer-urls: https://${IP}:2380
initial-advertise-peer-urls: https://${IP}:2380
initial-cluster: etcd-master-01=https://192.168.200.101:2380,etcd-master-02=https://192.168.200.102:2380,etcd-master-03=https://192.168.200.103:2380
initial-cluster-token: KubernetesToken
initial-cluster-state: new
client-transport-security:
  cert-file: /ezops/certs/etcd.pem
  key-file: /ezops/certs/etcd-key.pem
  trusted-ca-file: /ezops/certs/etcd-ca.pem
  client-cert-auth: true
peer-transport-security:
  cert-file: /ezops/certs/etcd.pem
  key-file: /ezops/certs/etcd-key.pem
  trusted-ca-file: /ezops/certs/etcd-ca.pem
  client-cert-auth: true
EOF

有几个值得注意的地方:

  • name:节点在集群中的名称,和 initial-cluster 字段中的名称要对应,而且在集群中要唯一。
  • initial-cluster:ETCD 集群节点属于静态发现,所以所有节点都要写上去。
  • IP:注意不同节点监听的 IP 是不同的,根据自己需求改为自己的 IP。
  • 证书:需要配置 ETCD 自己的证书和这个证书对应的 CA 证书。


配置启动文件:

cat > /etc/systemd/system/etcd.service << EOF
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
WorkingDirectory=/ezops/service/etcd
ExecStart=/ezops/service/etcd/bin/etcd --config-file=/ezops/service/etcd/conf/etcd.yml
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF


启动服务:

systemctl daemon-reload
systemctl start etcd
systemctl enable etcd
systemctl status etcd

注意,如果只启动一个 etcd 节点服务是无法启动的,会一直卡住,至少启动两个节点。


配置命令别名,用于简化用户从客户端操作 ETCD:

cat >> /etc/profile << EOF
# ETCD 变量
export ETCDCTL_API=3
alias etcdctl='etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/ezops/certs/etcd-ca.pem --cert=/ezops/certs/etcd.pem --key=/ezops/certs/etcd-key.pem'
EOF

source /etc/profile


查看集群节点状态:

etcdctl endpoint status --cluster -w table | grep -v "127.0.0.1"

如图所示,可以查看到集群中谁是 Leader:

二进制安装docker软链接 kubernetes二进制安装_kubernetes_04

安装 Kubernetes

Kubernetes 本身是 Go 语言开发,所以只需要直接下载安装包解压配置后就能直接使用。

可以去 Github CHANGELOG 中找到对应版本提供的二进制下载地址:

https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.25.md#server-binaries-2

由于 Kubernetes 二进制包中包含了所有 Master 和 Worker 的核心组件,所以在所有 Master 和 Worker 节点都安装。

将下载好的安装包上传到所有节点的 /ezops/packages/ 目录下,并执行下面的安装操作:


解压上传的安装包进行安装:

# 解压安装
cd /ezops/packages/
tar -zxf kubernetes-server-linux-amd64.tar.gz
mkdir -p /ezops/service
mv kubernetes /ezops/service/kubernetes

# 删除无用文件
cd /ezops/service/kubernetes/
rm -rf kubernetes-src.tar.gz LICENSES/
cd server/bin/
rm -f *_tag *tar

# 创建相关目录
mkdir -p /ezops/service/kubernetes/server/{logs,conf}
mkdir -p /ezops/certs
mkdir -p /ezops/service/kubernetes/manifests


配置环境变量:

# 添加环境变量
cat >> /etc/profile << EOF
# Kubernetes
export KUBERNETES_HOME=/ezops/service/kubernetes
export PATH=\$KUBERNETES_HOME/server/bin:\$PATH
EOF

# 配置生效
source /etc/profile

查看配置结果:

kubectl version

签发 apiserver 证书

apiserver 是集群各个组件交互的核心组件,也是需要绑定对应节点 IP 地址的。

创建生成证书的请求 Json 文件:

cd /ezops/certs

# 创建文件
cat > kube-apiserver-csr.json << EOF
{
    "CN": "kube-apiserver",
    "hosts": [
        "127.0.0.1",
        "192.168.200.100",
        "192.168.200.101",
        "192.168.200.102",
        "192.168.200.103",
        "192.168.200.104",
        "192.168.200.105",
        "192.168.200.106",
        "192.168.200.107",
        "192.168.200.108",
        "192.168.200.109",
        "192.168.200.110",
        "10.10.0.1",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [{
        "C": "CN",
        "ST": "GuangDong",
        "L": "ShenZhen",
        "O": "kubernetes",
        "OU": "ops"
    }]
}
EOF

apiserver 证书的 hosts 需要包含:

  • Master 节点和备用 IP。
  • Service 网段的第一个 IP。
  • Kubernetes 自带的一些解析地址。

生成证书:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-apiserver-csr.json | cfssl-json -bare kube-apiserver


证书生成结果如图所示:

二进制安装docker软链接 kubernetes二进制安装_docker_05

签发 apiserver 聚合证书

该证书用于控制第三方组件使用集群的时候的权限管理。

创建生成证书的请求 Json 文件:

cat > front-proxy-client-csr.json << EOF
{
    "CN": "front-proxy-client",
    "key": {
        "algo": "rsa",
        "size": 2048
    }
}
EOF

生成证书,注意聚合证书的 CA 是之前单独生成的:

cfssl gencert -ca=front-proxy-ca.pem -ca-key=front-proxy-ca-key.pem -config=ca-config.json -profile=kubernetes front-proxy-client-csr.json | cfssl-json -bare front-proxy-client

由于没配置 hosts 字段,在生成证书的时候会提示,忽略即可:

2022/10/13 14:20:56 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").


证书生成结果如图所示:

二进制安装docker软链接 kubernetes二进制安装_二进制安装docker软链接_06

签发 controller-manager 证书

创建生成证书的请求 Json 文件:

cat > kube-controller-manager-csr.json << EOF
{
    "CN": "system:kube-controller-manager",
    "hosts": [],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [{
        "C": "CN",
        "ST": "GuangDong",
        "L": "ShenZhen",
        "O": "system:kube-controller-manager",
        "OU": "ops"
    }]
}
EOF

生成证书:

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


该证书涉及到高可用集群的角色等配置,需要证书生成的 Master 节点执行配置:

# set-cluster:设置一个集群项,高可用集群使用 VIP 代理的 API Server 地址
kubectl config set-cluster kubernetes --certificate-authority=/ezops/certs/ca.pem --embed-certs=true --server=https://192.168.200.100:16443 --kubeconfig=/ezops/certs/kube-controller-manager.kubeconfig

# set-credentials 设置一个用户项
kubectl config set-credentials system:kube-controller-manager --client-certificate=/ezops/certs/kube-controller-manager.pem --client-key=/ezops/certs/kube-controller-manager-key.pem --embed-certs=true --kubeconfig=/ezops/certs/kube-controller-manager.kubeconfig

# 设置一个环境项,一个上下文
kubectl config set-context system:kube-controller-manager@kubernetes --cluster=kubernetes --user=system:kube-controller-manager --kubeconfig=/ezops/certs/kube-controller-manager.kubeconfig

# 使用某个环境当做默认环境
kubectl config use-context system:kube-controller-manager@kubernetes --kubeconfig=/ezops/certs/kube-controller-manager.kubeconfig


证书生成结果如图所示:

二进制安装docker软链接 kubernetes二进制安装_kubernetes_07

签发 scheduler 证书

创建生成证书的请求 Json 文件:

cat > kube-scheduler-csr.json << EOF
{
    "CN": "system:kube-scheduler",
    "hosts": [],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [{
        "C": "CN",
        "ST": "GuangDong",
        "L": "ShenZhen",
        "O": "system:kube-scheduler",
        "OU": "ops"
    }]
}
EOF

生成证书:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-scheduler-csr.json | cfssl-json -bare kube-scheduler


该证书涉及到高可用集群的角色等配置,需要证书生成的 Master 节点执行配置:

# set-cluster:设置一个集群项,高可用集群使用 VIP 代理的 API Server 地址
kubectl config set-cluster kubernetes --certificate-authority=/ezops/certs/ca.pem --embed-certs=true --server=https://192.168.200.100:16443 --kubeconfig=/ezops/certs/kube-scheduler.kubeconfig

# set-credentials 设置一个用户项
kubectl config set-credentials system:kube-scheduler --client-certificate=/ezops/certs/kube-scheduler.pem --client-key=/ezops/certs/kube-scheduler-key.pem --embed-certs=true  --kubeconfig=/ezops/certs/kube-scheduler.kubeconfig

# 设置一个环境项,一个上下文
kubectl config set-context system:kube-scheduler@kubernetes --cluster=kubernetes --user=system:kube-scheduler --kubeconfig=/ezops/certs/kube-scheduler.kubeconfig

# 使用某个环境当做默认环境
kubectl config use-context system:kube-scheduler@kubernetes --kubeconfig=/ezops/certs/kube-scheduler.kubeconfig


证书生成结果如图所示:

二进制安装docker软链接 kubernetes二进制安装_容器_08

签发 admin 证书

该证书用于生成管理员权限的 kubeconfig,这里主要是给 kubectl 使用。

cat > admin-csr.json << EOF
{
    "CN": "admin",
    "hosts": [],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [{
        "C": "CN",
        "ST": "GuangDong",
        "L": "ShenZhen",
        "O": "system:masters",
        "OU": "ops"
    }]
}
EOF

apiserver 使用 RBAC 对客户端授权时内部定义了一些 RoleBindings,如 system:masters 绑定 cluster-admin,该角色拥有 apiserver 的所有权限。通过 O 指定了 Group,由于都是被同一个 CA 签名,所以访问 apiserver 是认证通过的,然后根据所属组的角色权限绑定就能获得了 apiserver 的所有权限。

生成证书:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssl-json -bare admin


该证书涉及到高可用集群的角色等配置,需要证书生成的 Master 节点执行配置:

# set-cluster:设置一个集群项,高可用集群使用 VIP 代理的 API Server 地址
kubectl config set-cluster kubernetes --certificate-authority=/ezops/certs/ca.pem --embed-certs=true --server=https://192.168.200.100:16443 --kubeconfig=/ezops/certs/admin.kubeconfig

# set-credentials 设置一个用户项
kubectl config set-credentials kubernetes-admin --client-certificate=/ezops/certs/admin.pem --client-key=/ezops/certs/admin-key.pem --embed-certs=true --kubeconfig=/ezops/certs/admin.kubeconfig

# 设置一个环境项,一个上下文
kubectl config set-context kubernetes-admin@kubernetes --cluster=kubernetes --user=kubernetes-admin --kubeconfig=/ezops/certs/admin.kubeconfig

# 使用某个环境当做默认环境
kubectl config use-context kubernetes-admin@kubernetes --kubeconfig=/ezops/certs/admin.kubeconfig


证书生成结果如图所示:

二进制安装docker软链接 kubernetes二进制安装_二进制安装docker软链接_09

创建 SA Key

生成公钥私钥:

openssl genrsa -out /ezops/certs/sa.key 2048
openssl rsa -in /ezops/certs/sa.key -pubout -out /ezops/certs/sa.pub


将生成的所有证书都分发到其它 Master 节点,用作备份和使用:

scp * root@192.168.200.102:/ezops/certs/
scp * root@192.168.200.103:/ezops/certs/

配置 apiserver

所有 Master 节点配置 apiserver 启动文件:

# 本机 IP
IP=$(ip a | grep "192.168.200" | grep -v "100" | awk '{print $2}' | cut -d "/" -f 1)

cat > /ezops/service/kubernetes/server/conf/kube-apiserver.service << EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/ezops/service/kubernetes/server/bin/kube-apiserver \\
  --v=2 \\
  --logtostderr=false \\
  --log-dir=/ezops/service/kubernetes/server/logs \\
  --allow-privileged=true \\
  --bind-address=0.0.0.0 \\
  --secure-port=6443 \\
  --advertise-address=${IP} \\
  --service-cluster-ip-range=10.10.0.0/16 \\
  --service-node-port-range=30000-50000 \\
  --etcd-servers=https://192.168.200.101:2379,https://192.168.200.102:2379,https://192.168.200.103:2379 \\
  --etcd-cafile=/ezops/certs/etcd-ca.pem \\
  --etcd-certfile=/ezops/certs/etcd.pem \\
  --etcd-keyfile=/ezops/certs/etcd-key.pem \\
  --client-ca-file=/ezops/certs/ca.pem \\
  --tls-cert-file=/ezops/certs/kube-apiserver.pem \\
  --tls-private-key-file=/ezops/certs/kube-apiserver-key.pem \\
  --kubelet-client-certificate=/ezops/certs/kube-apiserver.pem \\
  --kubelet-client-key=/ezops/certs/kube-apiserver-key.pem \\
  --service-account-key-file=/ezops/certs/sa.pub \\
  --service-account-signing-key-file=/ezops/certs/sa.key \\
  --service-account-issuer=https://kubernetes.default.svc.cluster.local \\
  --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname \\
  --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota \\
  --authorization-mode=Node,RBAC \\
  --enable-bootstrap-token-auth=true \\
  --feature-gates=LegacyServiceAccountTokenNoAutoGeneration=false \\
  --enable-aggregator-routing=true \\
  --requestheader-client-ca-file=/ezops/certs/front-proxy-ca.pem \\
  --proxy-client-cert-file=/ezops/certs/front-proxy-client.pem \\
  --proxy-client-key-file=/ezops/certs/front-proxy-client-key.pem \\
  --requestheader-allowed-names=aggregator \\
  --requestheader-group-headers=X-Remote-Group \\
  --requestheader-extra-headers-prefix=X-Remote-Extra- \\
  --requestheader-username-headers=X-Remote-User
Restart=on-failure
RestartSec=10s
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF


启动服务:

# 创建启动文件软连接
ln -s /ezops/service/kubernetes/server/conf/kube-apiserver.service /etc/systemd/system/kube-apiserver.service

# 启动服务
systemctl daemon-reload
systemctl start kube-apiserver
systemctl enable kube-apiserver
systemctl status kube-apiserver

配置 controller-manager

所有 Master 节点配置 controller-manager 启动文件:

cat > /ezops/service/kubernetes/server/conf/kube-controller-manager.service << EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/ezops/service/kubernetes/server/bin/kube-controller-manager \\
  --v=2 \\
  --logtostderr=false \\
  --log-dir=/ezops/service/kubernetes/server/logs \\
  --bind-address=0.0.0.0 \\
  --root-ca-file=/ezops/certs/ca.pem \\
  --cluster-signing-cert-file=/ezops/certs/ca.pem \\
  --cluster-signing-key-file=/ezops/certs/ca-key.pem \\
  --service-account-private-key-file=/ezops/certs/sa.key \\
  --kubeconfig=/ezops/certs/kube-controller-manager.kubeconfig \\
  --feature-gates=LegacyServiceAccountTokenNoAutoGeneration=false \\
  --leader-elect=true \\
  --use-service-account-credentials=true \\
  --node-monitor-grace-period=40s \\
  --node-monitor-period=5s \\
  --pod-eviction-timeout=2m0s \\
  --controllers=*,bootstrapsigner,tokencleaner \\
  --allocate-node-cidrs=true \\
  --cluster-cidr=172.16.0.0/16 \\
  --requestheader-client-ca-file=/ezops/certs/front-proxy-ca.pem \\
  --node-cidr-mask-size=24
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF


启动服务:

# 创建启动文件软连接
ln -s /ezops/service/kubernetes/server/conf/kube-controller-manager.service /etc/systemd/system/kube-controller-manager.service

# 启动服务
systemctl daemon-reload
systemctl start kube-controller-manager
systemctl enable kube-controller-manager
systemctl status kube-controller-manager

配置 scheduler

所有 Master 节点配置 scheduler 启动文件:

cat > /ezops/service/kubernetes/server/conf/kube-scheduler.service << EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/ezops/service/kubernetes/server/bin/kube-scheduler \\
  --v=2 \\
  --logtostderr=false \\
  --leader-elect=true \\
  --log-dir=/ezops/service/kubernetes/server/logs \\
  --authentication-kubeconfig=/ezops/certs/kube-scheduler.kubeconfig \\
  --authorization-kubeconfig=/ezops/certs/kube-scheduler.kubeconfig \\
  --kubeconfig=/ezops/certs/kube-scheduler.kubeconfig

Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF


启动服务:

# 创建启动文件软连接
ln -s /ezops/service/kubernetes/server/conf/kube-scheduler.service /etc/systemd/system/kube-scheduler.service

# 启动服务
systemctl daemon-reload
systemctl start kube-scheduler
systemctl enable kube-scheduler
systemctl status kube-scheduler

配置 kubectl

所有 Master 执行 kubectl 默认证书配置:

cd /ezops/certs/
mkdir ~/.kube
cp admin.kubeconfig ~/.kube/config


配置 kubectl 命令补全,方便后续直接 tab 补全命令:

yum install -y bash-completion

# 加载配置
source /usr/share/bash-completion/bash_completion

# 临时生效
source <(kubectl completion bash)

# 永久生效
echo "source <(kubectl completion bash)" >> ~/.bashrc


查看 Master 集群状态:

kubectl get cs

如图所示:

二进制安装docker软链接 kubernetes二进制安装_kubernetes_10

配置 TLS Bootstrapping

在一个 Kubernetes 集群中,Worker 节点上的组件(kubelet 和 kube-proxy)需要与 Kubernetes 控制平面组件通信,尤其是 kube-apiserver。 为了通信的安全性, 需要使用到节点上的客户端 TLS 证书。

但是客户端很多,又很难有通用的 TSL 证书直接使用,如果每一次加节点都需要重新生成证书,那维护将变得非常麻烦。

为了简化这一过程,从 1.4 版本开始,Kubernetes 引入了一个证书请求和签名 API。

https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/


采用 TLS bootstrapping 生成证书的大致简化流程如下:

  • 管理员通过 apiserver 生成一个 bootstrap token 并将它写入到 kubeconfig 文件中。
  • Kubelet 通过 --bootstrap-kubeconfig 启动参数指定 kubeconfig 文件,然后调用 apiserver 的 API 接口,生成自己所需的服务器和客户端证书。
  • 证书生成后,Kubelet 采用生成的证书和 apiserver 进行通信,并删除本地的 kubeconfig 文件,避免 bootstrap token 泄漏。

想要启动该功能,只需要在 apiserver 中启动参数中添加 --enable-bootstrap-token-auth,并创建一个 Kubelet 访问的 bootstrap token secret 即可。

https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/bootstrap-tokens/


添加配置生成的资源清单:

cat > bootstrap.secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-c8ad9c
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  description: "The default bootstrap token generated by 'kubelet '."
  token-id: c8ad9c
  token-secret: 2e4d610cf3e7426e
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"
  auth-extra-groups:  system:bootstrappers:default-node-token,system:bootstrappers:worker,system:bootstrappers:ingress
 
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubelet-bootstrap
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:node-bootstrapper
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:bootstrappers:default-node-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: node-autoapprove-bootstrap
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:bootstrappers:default-node-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: node-autoapprove-certificate-rotation
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:nodes
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
    verbs:
      - "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kube-apiserver
EOF


创建资源:

kubectl create -f bootstrap.secret.yaml


生成 kubelet-bootstrap.kubeconfig:

kubectl config set-cluster kubernetes --certificate-authority=/ezops/certs/ca.pem --embed-certs=true --server=https://192.168.200.100:16443 --kubeconfig=/ezops/certs/kubelet-bootstrap.kubeconfig

kubectl config set-credentials tls-bootstrap-token-user --token=c8ad9c.2e4d610cf3e7426e --kubeconfig=/ezops/certs/kubelet-bootstrap.kubeconfig

kubectl config set-context tls-bootstrap-token-user@kubernetes --cluster=kubernetes --user=tls-bootstrap-token-user --kubeconfig=/ezops/certs/kubelet-bootstrap.kubeconfig

kubectl config use-context tls-bootstrap-token-user@kubernetes --kubeconfig=/ezops/certs/kubelet-bootstrap.kubeconfig

配置 kubelet

依然在 master-01 节点生成相关配置文件,然后分发到其它节点去。

创建主配置文件:

cat > kubelet.yaml << EOF
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 2m0s
    enabled: true
  x509:
    clientCAFile: /ezops/certs/ca.pem
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
cgroupDriver: systemd
cgroupsPerQOS: true
# CoreDNS 使用 Service 网段的 IP,后面会单独配置
clusterDNS:
- 10.10.10.10
clusterDomain: cluster.local
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
cpuCFSQuota: true
cpuManagerPolicy: none
cpuManagerReconcilePeriod: 10s
enableControllerAttachDetach: true
enableDebuggingHandlers: true
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
  imagefs.available: 15%
  memory.available: 100Mi
  nodefs.available: 10%
  nodefs.inodesFree: 5%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 20s
hairpinMode: promiscuous-bridge
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 20s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
iptablesDropBit: 15
iptablesMasqueradeBit: 14
kubeAPIBurst: 10
kubeAPIQPS: 5
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
registryBurst: 10
registryPullQPS: 5
resolvConf: /etc/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
staticPodPath: /ezops/service/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s
EOF


配置启动文件:

cat > kubelet.service << EOF
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes

[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/ezops/certs/kubelet-bootstrap.kubeconfig --kubeconfig=/ezops/certs/kubelet.kubeconfig"
Environment="KUBELET_SYSTEM_ARGS=--cert-dir=/ezops/certs"
Environment="KUBELET_RINTIME=--container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock"
Environment="KUBELET_CONFIG_ARGS=--config=/ezops/service/kubernetes/server/conf/kubelet.yaml"
Environment="KUBELET_EXTRA_ARGS=--node-labels=node.kubernetes.io/node=''"
ExecStart=/ezops/service/kubernetes/server/bin/kubelet \$KUBELET_KUBECONFIG_ARGS \$KUBELET_CONFIG_ARGS \$KUBELET_SYSTEM_ARGS \$KUBELET_EXTRA_ARGS \$KUBELET_RINTIME
Restart=always
StartLimitInterval=0
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

kubeconfig 会自动在配置的地方生成,所以直写上不用管。


将所有配置分发到其它 Master 节点:

scp * root@192.168.200.102:/ezops/certs/
scp * root@192.168.200.103:/ezops/certs/


将需要的配置分发到 Worker 节点:

# 没免密需要一条一条单独执行
scp kubelet* ca.pem front-proxy-ca.pem root@192.168.200.104:/ezops/certs/
scp kubelet* ca.pem front-proxy-ca.pem root@192.168.200.105:/ezops/certs/


调整所有节点的配置文件结构:

cd /ezops/certs/
mv kubelet.service /ezops/service/kubernetes/server/conf/
mv kubelet.yaml /ezops/service/kubernetes/server/conf/
ln -s /ezops/service/kubernetes/server/conf/kubelet.service /etc/systemd/system/kubelet.service


启动服务:

systemctl daemon-reload
systemctl start kubelet
systemctl enable kubelet
systemctl status kubelet


任意 Master 节点查看节点添加情况:

kubectl get nodes

如图所示:

二进制安装docker软链接 kubernetes二进制安装_容器_11

此时日志中会有一个报错:

Oct 13 18:28:57 master-01 kubelet: E1013 18:28:57.112109 83182 kubelet.go:2373] "Container runtime network not ready" networkReady="NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized"

这是由于还没有安装 CNI 插件,不用管它。同时也可以看到自动签发的 kubelet 证书:

二进制安装docker软链接 kubernetes二进制安装_二进制安装docker软链接_12


由于本文配置了自动允许加入集群,如果没配置还需要手动确认节点加入集群:

# 查看申请
kubectl get csr

# 同意申请
kubectl certificate approve 申请名称

配置 kube-proxy

任意 Master 创建 Service Account 和角色绑定:

kubectl -n kube-system create serviceaccount kube-proxy
kubectl create clusterrolebinding system:kube-proxy --clusterrole system:node-proxier --serviceaccount kube-system:kube-proxy


生成 kube-proxy.kubeconfig:

# 获取 Token
SECRET=$(kubectl -n kube-system get sa/kube-proxy --output=jsonpath='{.secrets[0].name}')
JWT_TOKEN=$(kubectl -n kube-system get secret/$SECRET --output=jsonpath='{.data.token}' | base64 -d)

# 生成 kubeconfig
kubectl config set-cluster kubernetes --certificate-authority=/ezops/certs/ca.pem --embed-certs=true --server=https://192.168.200.100:16443 --kubeconfig=/ezops/certs/kube-proxy.kubeconfig

kubectl config set-credentials kubernetes --token=${JWT_TOKEN} --kubeconfig=/ezops/certs/kube-proxy.kubeconfig

kubectl config set-context kubernetes --cluster=kubernetes --user=kubernetes --kubeconfig=/ezops/certs/kube-proxy.kubeconfig

kubectl config use-context kubernetes --kubeconfig=/ezops/certs/kube-proxy.kubeconfig


创建主配置文件:

cd /ezops/certs

# 创建文件
cat > kube-proxy.yaml << EOF
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
  acceptContentTypes: ""
  burst: 10
  contentType: application/vnd.kubernetes.protobuf
  kubeconfig: /ezops/certs/kube-proxy.kubeconfig
  qps: 5
clusterCIDR: 172.16.0.0/12 
configSyncPeriod: 15m0s
conntrack:
  max: null
  maxPerCore: 32768
  min: 131072
  tcpCloseWaitTimeout: 1h0m0s
  tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
  masqueradeAll: false
  masqueradeBit: 14
  minSyncPeriod: 0s
  syncPeriod: 30s
ipvs:
  masqueradeAll: true
  minSyncPeriod: 5s
  scheduler: "rr"
  syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs"
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
udpIdleTimeout: 250ms
EOF


创建启动文件:

cat > kube-proxy.service << EOF
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/ezops/service/kubernetes/server/bin/kube-proxy \\
  --config=/ezops/service/kubernetes/server/conf/kube-proxy.yaml \\
  --v=2 \\
  --log-dir=/ezops/service/kubernetes/server/logs
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF


发送配置文件到所有其它节点:

scp kube-proxy* root@192.168.200.102:/ezops/certs/
scp kube-proxy* root@192.168.200.103:/ezops/certs/

# 没做免密登录要分开执行
scp kube-proxy* root@192.168.200.104:/ezops/certs/
scp kube-proxy* root@192.168.200.105:/ezops/certs/


调整所有节点的配置文件结构:

cd /ezops/certs/
mv kube-proxy.service /ezops/service/kubernetes/server/conf/
mv kube-proxy.yaml /ezops/service/kubernetes/server/conf/
ln -s /ezops/service/kubernetes/server/conf/kube-proxy.service /etc/systemd/system/kube-proxy.service


启动服务:

systemctl daemon-reload
systemctl start kube-proxy
systemctl enable kube-proxy
systemctl status kube-proxy

安装 Calico

选择任意 Master 节点安装 Calico:

cd /ezops/service/kubernetes/addons
wget https://projectcalico.docs.tigera.io/archive/v3.24/manifests/calico.yaml
kubectl apply -f calico.yaml

外网拉取镜像需要消耗一段时间,我本地花了 10 分钟,这个过程也会导致本地的虚拟机很卡,等它安装完成即可:

二进制安装docker软链接 kubernetes二进制安装_k8s_13

等到初始化完成以后所有节点都会变成 Ready 状态:

二进制安装docker软链接 kubernetes二进制安装_docker_14

安装 CoreDNS

选择任意 Master 节点安装 CoreDNS:

cd /ezops/service/kubernetes/addons
git clone https://github.com/coredns/deployment.git
cd deployment/kubernetes
./deploy.sh -s -i 10.10.10.10 | kubectl apply -f -

注意 Service IP 是之前设置的 10.10.10.10,结果如图:

二进制安装docker软链接 kubernetes二进制安装_k8s_15

验证集群

创建一个用于测试的资源清单:

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
EOF


测试域名解析功能:

kubectl exec busybox -n default -- nslookup kubernetes

如图所示:

二进制安装docker软链接 kubernetes二进制安装_docker_16

到此,二进制基础集群环境搭建完成!