安装准备
区别于使用 kubeadm 部署集群时所有核心组件都托管在集群上。二进制安装则采用守护进程的方式直接将各个组件运行在宿主机,生产环境更为推荐。
对于生产环境,不推荐在 Master 节点再安装 kubelet、kube-proxy 以及容器运行时 docker + containerd,使得它同时也是 Worker 节点运行。但是测试环境由于机器有限,为了使得集群看起来节点不那么少,可以也安装上用于测试。
生产环境部署架构图:
本文部署方案由于机器性能原因,暂时只用 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 下载:
本文使用的是 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 年。为了做到二进制安装也更贴近官方最佳实践,本文的规划也是如此。
同时,对于证书的签发,有两个非常重要的注意事项:
- 证书有效期:二进制安装由于证书签发是有用户自己决定的,所以推荐尽可能签长一点,一劳永逸。
- 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
证书生成结果如图所示:
签发 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
证书生成结果如图所示:
此时就可以将这些配置证书目录下所有文件都分发到其它 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:
安装 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
证书生成结果如图所示:
签发 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").
证书生成结果如图所示:
签发 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
证书生成结果如图所示:
签发 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
证书生成结果如图所示:
签发 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
证书生成结果如图所示:
创建 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
如图所示:
配置 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
如图所示:
此时日志中会有一个报错:
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 证书:
由于本文配置了自动允许加入集群,如果没配置还需要手动确认节点加入集群:
# 查看申请
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 分钟,这个过程也会导致本地的虚拟机很卡,等它安装完成即可:
等到初始化完成以后所有节点都会变成 Ready 状态:
安装 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
,结果如图:
验证集群
创建一个用于测试的资源清单:
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
如图所示:
到此,二进制基础集群环境搭建完成!