安装准备

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

服务还是那些,只需要将服务器准备到初始化状态即可,即:

/zhuanlan/container/kubernetes/9/3.html

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

具体规划如下:

主机 IP 系统 配置 初始化安装服务
ops 192.168.2.40 CentOS 7.9 4C/4G/20G cfssl,kubernetes
slb-01 192.168.2.11 CentOS 7.9 4C/2G/20G nginx,keepalived
slb-02 192.168.2.12 CentOS 7.9 4C/2G/20G nginx,keepalived
master-01 192.168.2.21 CentOS 7.9 4C/3G/20G containerd,nerdctl,etcd,apiserver,controller-manager,scheduler,kubectl,kubelet,kube-proxy,calico
master-02 192.168.2.22 CentOS 7.9 4C/3G/20G containerd,nerdctl,etcd,apiserver,controller-manager,scheduler,kubectl,kubelet,kube-proxy,calico
worker-01 192.168.2.31 CentOS 7.9 4C/4G/20G containerd,nerdctl,kubelet,kube-proxy,calico
worker-02 192.168.2.32 CentOS 7.9 4C/4G/20G containerd,nerdctl,kubelet,kube-proxy,calico
worker-03 192.168.2.33 CentOS 7.9 4C/4G/20G containerd,nerdctl,kubelet,kube-proxy,calico
VIP 192.168.2.100 / / keepalived 提供

CoreDNS 和 Dashboard 属于托管在集群内部,不确定节点。

安装证书签发环境 cfssl

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

为此,准备了 ops 这台服务器专门用于做源码编译,证书签发这类独立于集群的工作。

下载cfssl 相关文件,具体不同版本可以去 Github 下载,本文编写的时候最新版本为 1.6.3

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

执行服务器:ops

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

文件说明:

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

创建免密认证

由于OPS 服务器需要经常传输文件到其它服务器,所以其它服务器要对 OPS 配置免密登录。

执行服务器:ops

# 创建公钥和私钥
ssh-keygen

# 上传公钥到其它服务器,需要一台一台的验证
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.2.21
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.2.22
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.2.31
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.2.32
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.2.33

# 测试免密登录
ssh root@192.168.2.21

# 退出登录
exit

签发 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、ops 节点

# 创建证书存储目录,服务目录,安装包目录
mkdir -p /opt/certs/kubernetes
mkdir -p /opt/package
mkdir -p /opt/service
cd /opt/certs/kubernetes

Kubernetes CA

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

执行服务器:ops

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 文件。

执行服务器:ops

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 文件。

执行服务器:ops

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,算是公共配置。

执行服务器:ops

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

CA证书生成结果如图所示:

 

签发 ETCD 证书

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

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

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

执行服务器:ops

cat > etcd-csr.json << EOF
{
    "CN": "etcd",
    "hosts": [
        "127.0.0.1",
        "192.168.2.21",
        "192.168.2.22",
        "192.168.2.23",
        "192.168.2.24",
        "192.168.2.25"
    ],
    "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 字段定义的信息。

ETCD 证书生成结果如图所示:

 

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

cfssl-certinfo -cert etcd.pem

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

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

openssl x509 -in etcd.pem -noout -dates

将ETCD 相关证书分发到两台 Master 节点:

scp etcd* root@192.168.2.21:/opt/certs/kubernetes/
scp etcd* root@192.168.2.22:/opt/certs/kubernetes/

部署 ETCD 集群

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

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

在所有Master 服务器上面进行 ETCD 的安装。

执行服务器:所有 Master

cd /opt/package

# 下载解压安装
wget https://github.com/etcd-io/etcd/releases/download/v3.5.5/etcd-v3.5.5-linux-amd64.tar.gz
tar -zxf etcd-v3.5.5-linux-amd64.tar.gz
mv etcd-v3.5.5-linux-amd64 /opt/service/etcd

# 创建服务所需目录并整理文件
cd /opt/service/etcd/
mkdir data conf logs docs bin
mv *md Documentation docs/
mv etcd* bin/

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

# 生效
source /etc/profile

# 查看版本
etcd --version

配置主配置文件。

执行服务器:所有 Master

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

cat > /opt/service/etcd/conf/etcd.yml << EOF
name: etcd-$(hostname)
data-dir: /opt/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.2.21:2380,etcd-master-02=https://192.168.2.22:2380
initial-cluster-token: KubernetesToken
initial-cluster-state: new
client-transport-security:
  cert-file: /opt/certs/kubernetes/etcd.pem
  key-file: /opt/certs/kubernetes/etcd-key.pem
  trusted-ca-file: /opt/certs/kubernetes/etcd-ca.pem
  client-cert-auth: true
peer-transport-security:
  cert-file: /opt/certs/kubernetes/etcd.pem
  key-file: /opt/certs/kubernetes/etcd-key.pem
  trusted-ca-file: /opt/certs/kubernetes/etcd-ca.pem
  client-cert-auth: true
EOF

有几个值得注意的地方:

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

配置启动文件。

执行服务器:所有 Master

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=/opt/service/etcd
ExecStart=/opt/service/etcd/bin/etcd --config-file=/opt/service/etcd/conf/etcd.yml
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

启动服务:

systemctl daemon-reload
systemctl enable --now etcd
systemctl status etcd

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

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

执行服务器:所有 Master

cat >> /etc/profile << EOF
# ETCD 变量
export ETCDCTL_API=3
alias etcdctl='etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/opt/certs/kubernetes/etcd-ca.pem --cert=/opt/certs/kubernetes/etcd.pem --key=/opt/certs/kubernetes/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.26.md#server-binaries-2

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

下载安装 Kubernetes。

执行服务器:所有 Master、Worker、ops 节点

# 下载安装包
cd /opt/package/
wget https://dl.k8s.io/v1.26.0/kubernetes-server-linux-amd64.tar.gz

# 解压安装
tar -zxf kubernetes-server-linux-amd64.tar.gz
mv kubernetes /opt/service/

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

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

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

# 配置生效
source /etc/profile

# 查看安装结果
kubectl version

签发 apiserver 证书

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

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

执行服务器:ops

cd /opt/certs/kubernetes

# 创建文件
cat > kube-apiserver-csr.json << EOF
{
    "CN": "kube-apiserver",
    "hosts": [
        "127.0.0.1",
        "192.168.2.100",
        "192.168.2.21",
        "192.168.2.22",
        "192.168.2.23",
        "192.168.2.24",
        "192.168.2.25",
        "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

kube-apiserver 证书生成结果,如图所示:

 

签发 apiserver 聚合证书

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

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

执行服务器:ops

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 字段,在生成证书的时候会提示,忽略即可:

2023/02/22 21:15:13 [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 文件。

执行服务器:ops

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

该证书涉及到高可用集群的角色等配置,需要生成额外的配置,这就是为何 ops 节点也需要安装 Kubernetes 的原因:

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

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

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

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

证书生成结果如图所示:

 

签发 scheduler 证书

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

执行服务器:ops

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

该证书涉及到高可用集群的角色等配置,需要生成额外的配置:

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

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

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

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

证书生成结果如图所示:

 

签发 admin 证书

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

执行服务器:ops

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

该证书涉及到高可用集群的角色等配置,需要生成额外的配置:

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

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

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

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

证书生成结果如图所示:

 

创建 SA Key 和分发证书

生成公钥私钥。

执行服务器:ops

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

生成结果如图所示:

 

将ops 目录下生成的所有证书分发到所有 Master 节点:

scp * root@192.168.2.21:/opt/certs/kubernetes/
scp * root@192.168.2.22:/opt/certs/kubernetes/

配置 apiserver

配置apiserver 启动文件。

执行服务器:所有 Master 节点

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

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

[Service]
ExecStart=/opt/service/kubernetes/server/bin/kube-apiserver \\
  --v=2 \\
  --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.2.21:2379,https://192.168.2.22:2379 \\
  --etcd-cafile=/opt/certs/kubernetes/etcd-ca.pem \\
  --etcd-certfile=/opt/certs/kubernetes/etcd.pem \\
  --etcd-keyfile=/opt/certs/kubernetes/etcd-key.pem \\
  --client-ca-file=/opt/certs/kubernetes/ca.pem \\
  --tls-cert-file=/opt/certs/kubernetes/kube-apiserver.pem \\
  --tls-private-key-file=/opt/certs/kubernetes/kube-apiserver-key.pem \\
  --kubelet-client-certificate=/opt/certs/kubernetes/kube-apiserver.pem \\
  --kubelet-client-key=/opt/certs/kubernetes/kube-apiserver-key.pem \\
  --service-account-key-file=/opt/certs/kubernetes/sa.pub \\
  --service-account-signing-key-file=/opt/certs/kubernetes/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=/opt/certs/kubernetes/front-proxy-ca.pem \\
  --proxy-client-cert-file=/opt/certs/kubernetes/front-proxy-client.pem \\
  --proxy-client-key-file=/opt/certs/kubernetes/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 /opt/service/kubernetes/server/conf/kube-apiserver.service /etc/systemd/system/kube-apiserver.service

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

配置 controller-manager

配置controller-manager 启动文件。

执行服务器:所有 Master 节点

cat > /opt/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=/opt/service/kubernetes/server/bin/kube-controller-manager \\
  --cluster-signing-duration=876000h0m0s \\
  --feature-gates=RotateKubeletServerCertificate=true \\
  --bind-address=0.0.0.0 \\
  --root-ca-file=/opt/certs/kubernetes/ca.pem \\
  --cluster-signing-cert-file=/opt/certs/kubernetes/ca.pem \\
  --cluster-signing-key-file=/opt/certs/kubernetes/ca-key.pem \\
  --service-account-private-key-file=/opt/certs/kubernetes/sa.key \\
  --kubeconfig=/opt/certs/kubernetes/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=/opt/certs/kubernetes/front-proxy-ca.pem \\
  --node-cidr-mask-size=24
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF

启动服务:

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

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

配置 scheduler

配置scheduler 启动文件。

执行服务器:所有 Master 节点

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

[Service]
ExecStart=/opt/service/kubernetes/server/bin/kube-scheduler \\
  --leader-elect=true \\
  --authentication-kubeconfig=/opt/certs/kubernetes/kube-scheduler.kubeconfig \\
  --authorization-kubeconfig=/opt/certs/kubernetes/kube-scheduler.kubeconfig \\
  --kubeconfig=/opt/certs/kubernetes/kube-scheduler.kubeconfig

Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF

启动服务:

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

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

配置 kubectl

执行kubectl 默认证书配置。

执行服务器:所有 Master 节点

cd /opt/certs/kubernetes
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)" >> /etc/profile

查看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/

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

执行服务器:master-01

cat > /opt/certs/kubernetes/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,可以在 master-01 节点生成,也可以在 ops 节点:

kubectl config set-cluster kubernetes --certificate-authority=/opt/certs/kubernetes/ca.pem --embed-certs=true --server=https://192.168.2.100:6443 --kubeconfig=/opt/certs/kubernetes/kubelet-bootstrap.kubeconfig

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

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

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

将生成的配置分发到 master-02 节点和 ops 节点,因为 ops 节点之后需要分发它到其它 Worker 节点:

# 由于 master-01 节点没有做其它节点的免密,需要一台一台的执行发送
scp *bootstrap* root@192.168.2.22:/opt/certs/kubernetes/
scp *bootstrap* root@192.168.2.40:/opt/certs/kubernetes/

配置 kubelet

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

执行服务器:ops

cd /opt/certs/kubernetes

# 生成 kubelet 配置
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: /opt/certs/kubernetes/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: /opt/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=/opt/certs/kubernetes/kubelet-bootstrap.kubeconfig --kubeconfig=/opt/certs/kubernetes/kubelet.kubeconfig"
Environment="KUBELET_SYSTEM_ARGS=--cert-dir=/opt/certs/kubernetes --feature-gates=RotateKubeletServerCertificate=true --rotate-certificates"
Environment="KUBELET_RINTIME=--container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock"
Environment="KUBELET_CONFIG_ARGS=--config=/opt/service/kubernetes/server/conf/kubelet.yaml"
Environment="KUBELET_EXTRA_ARGS=--node-labels=node.kubernetes.io/node=''"
ExecStart=/opt/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 节点,这里因为测试环境节点太少,需要 Master 节点同时作为 Worker 节点使用,所以也要部署 Kubelet:

scp * root@192.168.2.21:/opt/certs/kubernetes/
scp * root@192.168.2.22:/opt/certs/kubernetes/

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

scp kubelet* ca.pem front-proxy-ca.pem root@192.168.2.31:/opt/certs/kubernetes/
scp kubelet* ca.pem front-proxy-ca.pem root@192.168.2.32:/opt/certs/kubernetes/
scp kubelet* ca.pem front-proxy-ca.pem root@192.168.2.33:/opt/certs/kubernetes/

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

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

所有节点启动服务:

systemctl daemon-reload
systemctl enable --now kubelet
systemctl status kubelet

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

kubectl get nodes

如图所示:

 

此时节点都是 NotReady 状态,原因在于缺少 CNI 网络插件。

通过查看证书可以发现,Kubelet 已经是通过接口自动签发证书了:

 

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

# 查看申请
kubectl get csr

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

配置 kube-proxy

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

执行服务器:master-01

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=/opt/certs/kubernetes/ca.pem --embed-certs=true --server=https://192.168.2.100:6443 --kubeconfig=/opt/certs/kubernetes/kube-proxy.kubeconfig

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

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

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

将生成的配置分发到 ops 节点,因为 ops 节点之后需要分发它到其它 Worker 节点:

scp kube-proxy* root@192.168.2.40:/opt/certs/kubernetes/

创建主配置文件。

执行服务器:ops

cd /opt/certs/kubernetes

# 创建文件
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: /opt/certs/kubernetes/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=/opt/service/kubernetes/server/bin/kube-proxy \\
  --config=/opt/service/kubernetes/server/conf/kube-proxy.yaml
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF

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

scp kube-proxy* root@192.168.2.21:/opt/certs/kubernetes/
scp kube-proxy* root@192.168.2.22:/opt/certs/kubernetes/
scp kube-proxy* root@192.168.2.31:/opt/certs/kubernetes/
scp kube-proxy* root@192.168.2.32:/opt/certs/kubernetes/
scp kube-proxy* root@192.168.2.33:/opt/certs/kubernetes/

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

执行服务器:所有 Master 和 Worker 节点

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

启动服务:

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

安装 Calico

执行服务器:master-01

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

拉取镜像可能会非常的慢,半个小时都有可能。如果一直没有成功,可以手动拉取镜像看看:

nerdctl -n k8s.io image pull docker.io/calico/node:v3.25.0
nerdctl -n k8s.io image pull docker.io/calico/cni:v3.25.0
nerdctl -n k8s.io image pull docker.io/calico/kube-controllers:v3.25.0

也可以查看 Pod 输出的信息,看是什么问题:

kubectl -n kube-system describe pod calico-node-xxx

安装完成以后,所有节点都会变成 Ready 状态:

 

安装 CoreDNS

执行服务器:master-01

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

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

 

验证集群解析功能

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

执行服务器:master-01

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

如图所示:

 

安装 Dashboard

在master-01 节点下载资源清单进行安装。

cd /opt/service/kubernetes/addons/

# 由于 raw.githubusercontent.com 访问不到,所以加了个 github proxy 的代理
wget https://ghproxy.com/https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
mv recommended.yaml dashboard.yaml

修改配置文件的 Service 配置:

...
spec:
  ports:
    - port: 443
      targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
  加上 type=NodePort 变成 NodePort 类型的服务
  type: NodePort

应用配置:

kubectl apply -f dashboard.yaml

Dashboard 集成了一个 metrics-scraper 的组件,可以通过 Kubernetes 的 Metrics API 收集一些基础资源的监控信息,并在 web 页面上展示,所以要想在页面上展示监控信息就需要提供 Metrics API,比如安装 Metrics Server。

查看安装结果:

 

可以看到 Dashboard 映射到了 34936 端口,通过任意节点 IP 访问,需要使用 HTTPS:

https://192.168.2.21:34936/#/login

如图所示:

 

创建一个全局访问用户:

cd /opt/service/kubernetes/addons

# 资源清单
cat > dashboard-admin.yaml << EOF
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: admin
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: admin
  namespace: kubernetes-dashboard
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin
  namespace: kubernetes-dashboard
EOF

# 应用
kubectl apply -f dashboard-admin.yaml

# 获取 token
kubectl get secret -n kubernetes-dashboard | grep admin-token

# 生成 base64 字符串
kubectl get secret <获取到的admin-token> -o jsonpath={.data.token} -n kubernetes-dashboard | base64 -d

然后用上面的 base64 解码后的字符串作为 token 登录 Dashboard 即可,注意要复制全:

 

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