服务器规划 角色 IP地址 主机名 资源配置 组件 关键要求 Master 192.168.56.101 k8smaster 4CPU/4GB/20GB+ kube-apiserver、kube-controller-manager、kube-scheduler、etcd 确保与Worker节点网络互通,时间同步,Swap关闭 Node1 192.168.56.102 k8snode1 4CPU/8GB/40GB+ kubelet、kube-proxy、docker、etcd 容器运行时(Docker)版本与K8s兼容,Cgroup驱动配置为systemd
Node2 192.168.56.103 k8snode2 4CPU/8GB/40GB+ kubelet、kube-proxy、docker、etcd 同上
目录结构
所有节点执行以下命令创建统一目录:
mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs,templates}
目录结构如下:
1 2 3 4 5 6 /opt/kubernetes/ ├── bin/ # 存放二进制文件(kube-apiserver, kube-scheduler 等) ├── cfg/ # 存放配置文件(*.conf, token.csv 等) ├── logs/ # 存放日志文件 ├── ssl/ # 存放证书文件(ca.pem, etcd.pem 等) └── templates/ # 存放模板文件(如 etcd.service 模板)
系统初始化配置(所有节点执行) 初始化脚本sudo ./setup_k8s.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 #!/bin/bash run_cmd () { local cmd="$1 " local success_msg="$2 " local failure_msg="${3:-命令执行失败,请检查。} " echo "正在执行:$cmd " eval "$cmd " 2>/dev/null if [ $? -eq 0 ]; then echo -e "\e[32m成功: $success_msg \e[0m" else echo -e "\e[31m失败: $failure_msg \e[0m" exit 1 fi } mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs,templates}run_cmd "systemctl stop firewalld && systemctl disable firewalld" \ "防火墙已成功关闭并禁用。" run_cmd "setenforce 0" "SELinux 已临时禁用。" "临时禁用 SELinux 失败,请检查权限。" run_cmd "sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config" \ "SELinux 已永久禁用,重启后生效。" "永久禁用 SELinux 失败,请检查权限或配置文件。" run_cmd "swapoff -a" "Swap 已临时关闭。" "临时关闭 Swap 失败,请检查权限。" run_cmd "sed -ri '/swap/s/^/#/' /etc/fstab" \ "Swap 已永久关闭,重启后生效。" "永久关闭 Swap 失败,请检查权限或配置文件。" IP=$(hostname -I | awk '{print $1}' ) case $IP in 192.168.56.101) hostname="k8smaster" ;; 192.168.56.102) hostname="k8snode1" ;; 192.168.56.103) hostname="k8snode2" ;; *) echo "Unknown IP: $IP " ; exit 1;; esac hostnamectl set-hostname "$hostname " cat >> /etc/hosts <<EOF 192.168.56.101 k8smaster 192.168.56.102 k8snode1 192.168.56.103 k8snode2 EOF echo -e "\e[32m成功: 主机解析已添加到 /etc/hosts 文件。\e[0m" run_cmd "cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF" "内核模块配置文件已创建。" "创建内核模块配置文件失败,请检查权限。" run_cmd "sysctl --system" "内核参数已生效。" "应用内核参数失败,请检查配置。" run_cmd "curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo" \ "配置 阿里云 YUM源成功。" "配置 阿里云 YUM源失败。" run_cmd "yum install -y chrony" "Chrony 已安装。" "安装 Chrony 失败,请检查网络或 yum 配置。" run_cmd "systemctl enable chronyd && systemctl start chronyd" "Chrony服务启动成功。" run_cmd "chronyc sources" "时间同步状态验证成功。" echo -e "\n\e[34m开始安装 Docker...\e[0m" run_cmd "yum install -y yum-utils wget vim" "已安装 yum-utils wget。" "安装 yum-utils wget 失败。" run_cmd "yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo" \ "Docker 源已配置成功。" "配置 Docker 源失败。" run_cmd "yum remove -y docker* containerd.io" "旧版本Docker清理完成。" run_cmd "yum install -y docker-ce-19.03.15 docker-ce-cli-19.03.15" \ "Docker 已安装成功。" "安装 Docker 失败,请检查版本是否存在。" run_cmd "mkdir -p /etc/docker" "Docker 配置目录已创建。" "创建目录失败。" run_cmd "cat > /etc/docker/daemon.json <<EOF { \"log-driver\": \"json-file\", \"log-opts\": { \"max-size\": \"100m\", \"max-file\": \"1\" }, \"exec-opts\": [\"native.cgroupdriver=systemd\"], \"registry-mirrors\": [ \"https://x9r52uz5.mirror.aliyuncs.com\", \"https://dockerhub.icu\", \"https://docker.chenby.cn\", \"https://docker.1panel.live\", \"https://docker.awsl9527.cn\", \"https://docker.anyhub.us.kg\", \"https://dhub.kubesre.xyz\" ] } EOF" "Docker 镜像加速配置完成。" "配置镜像加速失败。" run_cmd "systemctl enable docker && systemctl start docker" \ "Docker 服务已启用并启动。" "Docker 服务启动失败。" run_cmd "docker info | grep 'Cgroup Driver'" "验证Cgroup驱动为systemd。" echo -e "\n\e[32m所有配置已完成,建议重启系统以确保更改生效!\e[0m"
证书生成与权限设置 准备cfssl证书生成工具
cfssl是一个开源的证书管理工具,使用json文件生成证书,相比openssl更方便使用。 找任意一台服务器操作,这里用Master节点
。
仅 Master 节点执行此脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 #!/bin/bash ETCD_NODES=("192.168.56.101" "192.168.56.102" "192.168.56.103" ) CERT_DIR="/opt/kubernetes/ssl" mkdir -p $CERT_DIR && cd $CERT_DIR run_cmd () { local cmd="$1 " local success_msg="$2 " local failure_msg="${3:-命令执行失败,请检查。} " echo "正在执行:$cmd " eval "$cmd " 2>/dev/null if [ $? -eq 0 ]; then echo -e "\e[32m成功: $success_msg \e[0m" else echo -e "\e[31m失败: $failure_msg \e[0m" exit 1 fi } run_cmd "curl -s -L https://github.com/cloudflare/cfssl/releases/download/v1.6.3/cfssl_1.6.3_linux_amd64 -o /usr/local/bin/cfssl && chmod +x /usr/local/bin/cfssl" "CFSSL安装成功。" "CFSSL安装失败。" run_cmd "curl -s -L https://github.com/cloudflare/cfssl/releases/download/v1.6.3/cfssljson_1.6.3_linux_amd64 -o /usr/local/bin/cfssljson && chmod +x /usr/local/bin/cfssljson" "CFSSLJSON安装成功。" "CFSSLJSON安装失败。" cat > ca-config.json <<EOF { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "expiry": "87600h", "usages": ["signing", "key encipherment", "server auth", "client auth"] } } } } EOF cat > ca-csr.json <<EOF { "CN": "Kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "Beijing", "O": "Kubernetes", "OU": "CA" } ] } EOF run_cmd "cfssl gencert -initca ca-csr.json | cfssljson -bare ca" "CA证书生成成功。" "CA证书生成失败。" cat > etcd-csr.json <<EOF { "CN": "etcd", "hosts": [ "localhost", "${ETCD_NODES[0]}", "${ETCD_NODES[1]}", "${ETCD_NODES[2]}" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "Beijing", "O": "etcd", "OU": "etcd-cluster" } ] } EOF run_cmd "cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes etcd-csr.json | cfssljson -bare etcd" "etcd证书生成成功。" "etcd证书生成失败。" cat > apiserver-csr.json <<EOF { "CN": "kube-apiserver", "hosts": [ "localhost", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster", "kubernetes.default.svc.cluster.local", "10.96.0.1", "${ETCD_NODES[0]}", "${ETCD_NODES[1]}", "${ETCD_NODES[2]}" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "Beijing", "O": "Kubernetes", "OU": "API Server" } ] } EOF run_cmd "cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes apiserver-csr.json | cfssljson -bare apiserver" "API Server证书生成成功。" "API Server证书生成失败。" cat > admin-csr.json <<EOF { "CN": "admin", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "system:masters", "OU": "Kubernetes The Hard Way" } ] } EOF run_cmd "cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin" "admin证书生成成功。" "admin证书生成失败。" cat > kube-proxy-csr.json << EOF { "CN": "system:kube-proxy", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "BeiJing", "ST": "BeiJing", "O": "k8s", "OU": "System" } ] } EOF run_cmd "cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy" "kube-proxy证书生成成功。" "kube-proxy证书生成失败。" run_cmd "find . -maxdepth 1 -name '*-key.pem' -exec chmod 600 {} \;" "私钥权限设置成功。" "私钥权限设置失败。" run_cmd "find . -maxdepth 1 -name '*.pem' \! -name '*-*' -exec chmod 644 {} \;" "证书权限设置成功。" "证书权限设置失败。" echo -e "\e[32m[SUCCESS] 所有证书已生成到 $CERT_DIR ,有效期:87600小时(约10年)\e[0m"
部署etcd集群(所有节点执行) Etcd是一个分布式键值存储系统,Kubernetes使用Etcd进行数据存储,所以先准备 一个Etcd数据库,为解决Etcd单点故障,应采用集群方式部署,这里使用3台组建集 群,可容忍1台机器故障,当然,你也可以使用5台组建集群,可容忍2台机器故障。
节点名称 IP etcd-1 192.168.56.101 etcd-2 192.168.56.102 etcd-3 192.168.56.103
注:为了节省机器,这里与K8s节点机器复用。也可以独立于k8s集群之外部署,只要 apiserver能连接到就行。
安装etcd二进制文件 从Github下载二进制文件
1 2 3 4 ETCD_VERSION="v3.4.9" wget https://github.com/etcd-io/etcd/releases/download/${ETCD_VERSION} /etcd-${ETCD_VERSION} -linux-amd64.tar.gz tar -zxvf etcd-${ETCD_VERSION} -linux-amd64.tar.gz mv etcd-${ETCD_VERSION} -linux-amd64/{etcd,etcdctl} /usr/local/bin/
将上面Master节点生成的证书文件拷贝到其他etcd节点
1 2 scp -r 192.168.56.101:/opt/kubernetes/ssl/* /opt/kubernetes/ssl/
创建systemd服务文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 [Unit] Description=etcd server Documentation=https://github.com/etcd-io/etcd After=network.target [Service] Type=notify EnvironmentFile=/opt/kubernetes/cfg/etcd.conf ExecStart=/usr/local/bin/etcd \ --cert-file=/opt/kubernetes/ssl/etcd.pem \ --key-file=/opt/kubernetes/ssl/etcd-key.pem \ --peer-cert-file=/opt/kubernetes/ssl/etcd.pem \ --peer-key-file=/opt/kubernetes/ssl/etcd-key.pem \ --trusted-ca-file=/opt/kubernetes/ssl/ca.pem \ --peer-trusted-ca-file=/opt/kubernetes/ssl/ca.pem \ --logger=zap Restart=always RestartSec=10s LimitNOFILE=65536 [Install] WantedBy=multi-user.target ETCD_NAME="etcd-1" ETCD_DATA_DIR=/var/lib/etcd ETCD_INITIAL_CLUSTER_STATE=new ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_LISTEN_PEER_URLS=https://192.168.56.101:2380 ETCD_LISTEN_CLIENT_URLS=https://192.168.56.101:2379,https://localhost:2379 ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.56.101:2380,etcd-2=https://192.168.56.102:2380,etcd-3=https://192.168.56.103:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS=https://192.168.56.101:2380 ETCD_ADVERTISE_CLIENT_URLS=https://192.168.56.101:2379 ETCD_NAME="etcd-2" ETCD_DATA_DIR=/var/lib/etcd ETCD_INITIAL_CLUSTER_STATE=new ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_LISTEN_PEER_URLS=https://192.168.56.102:2380 ETCD_LISTEN_CLIENT_URLS=https://192.168.56.102:2379,https://localhost:2379 ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.56.101:2380,etcd-2=https://192.168.56.102:2380,etcd-3=https://192.168.56.103:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS=https://192.168.56.102:2380 ETCD_ADVERTISE_CLIENT_URLS=https://192.168.56.102:2379 ETCD_NAME="etcd-3" ETCD_DATA_DIR=/var/lib/etcd ETCD_INITIAL_CLUSTER_STATE=new ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_LISTEN_PEER_URLS=https://192.168.56.103:2380 ETCD_LISTEN_CLIENT_URLS=https://192.168.56.103:2379,https://localhost:2379 ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.56.101:2380,etcd-2=https://192.168.56.102:2380,etcd-3=https://192.168.56.103:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS=https://192.168.56.103:2380 ETCD_ADVERTISE_CLIENT_URLS=https://192.168.56.103:2379
ETCD_NAME:节点名称,集群中唯一 ETCD_DATA_DIR:数据目录 ETCD_LISTEN_PEER_URLS:集群通信监听地址 ETCD_LISTEN_CLIENT_URLS:客户端访问监听地址 ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址 ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址 ETCD_INITIAL_CLUSTER:集群节点地址 ETCD_INITIAL_CLUSTER_TOKEN:集群Token ETCD_INITIAL_CLUSTER_STATE:加入集群的当前状态,new是新集群,existing表示加入已有集群 其他节点参考
1 2 3 4 5 6 7 8 9 10 11 ETCD_NAME="etcd-1" ETCD_DATA_DIR="/var/lib/etcd" ETCD_LISTEN_PEER_URLS="https://192.168.56.101:2380" ETCD_LISTEN_CLIENT_URLS="https://192.168.56.101:2379" ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.56.101:2380" ETCD_ADVERTISE_CLIENT_URLS="https://192.168.56.101:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.56.101:2380,etcd-2=https://192.168.56.102:2380,etcd-3=https://192.168.56.103:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new"
启动etcd集群 1 2 3 systemctl daemon-reload systemctl enable etcd systemctl start etcd
验证集群状态 1 2 3 4 5 6 ETCDCTL_API=3 etcdctl \ --endpoints="https://192.168.56.101:2379,https://192.168.56.102:2379,https://192.168.56.103:2379" \ --cacert=/opt/kubernetes/ssl/ca.pem \ --cert=/opt/kubernetes/ssl/etcd.pem \ --key=/opt/kubernetes/ssl/etcd-key.pem \ endpoint health
1 2 3 https://192.168.56.101:2379 is healthy: successfully committed proposal: took = 10.524037ms https://192.168.56.102:2379 is healthy: successfully committed proposal: took = 11.004703ms https://192.168.56.103:2379 is healthy: successfully committed proposal: took = 11.154245ms
如果输出上面信息,就说明集群部署成功。如果有问题第一步先看日志: /var/log/message
或journalctl -u etcd
启用 TLS Bootstrapping 机制 背景
在 Kubernetes 集群中,当 kube-apiserver 启用 TLS 认证后,Node 节点上的 kubelet
和 kube-proxy
必须使用 CA 签发的有效证书才能与 kube-apiserver
进行通信。如果手动为每个 Node 节点签发证书,工作量会非常大,并且会增加集群扩展的复杂度。
为了解决这个问题,Kubernetes 引入了 TLS Bootstrapping 机制。通过该机制,kubelet
可以以一个低权限用户(Bootstrap Token)向 kube-apiserver
申请证书,动态签署并生成客户端证书,从而简化了证书管理流程。
目前,TLS Bootstrapping
主要用于 kubelet
,而 kube-proxy
仍然需要手动颁发统一的证书。

TLS Bootstrapping 工作流程
先行了解,后续才会步步操作
创建 Bootstrap Token 文件 在 Master 节点上创建一个 token.csv
文件,用于存储 Bootstrap Token。 该文件格式如下: 1 <token>,<username>,<user-id>,<group>
<token>
:用于身份验证的随机字符串(通常为 32 个字符)。<username>
:用户名,建议命名为 kubelet-bootstrap
。<user-id>
:用户 ID,可以是一个数字。<group>
:用户所属的组,建议设置为 system:node-bootstrapper
。配置 kube-apiserver
支持 TLS Bootstrapping 在 kube-apiserver
的配置文件中添加以下参数:
1 2 --enable-bootstrap-token-auth=true --token-auth-file=/opt/kubernetes/cfg/token.csv
创建 RBAC 角色绑定 为了让 kubelet
使用 Bootstrap Token 申请证书,需要为其绑定适当的 RBAC 权限。 创建一个 ClusterRoleBinding,将 system:node-bootstrapper
组与 system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
角色绑定: 1 2 3 kubectl create clusterrolebinding kubelet-bootstrap \ --clusterrole=system:node-bootstrapper \ --user=kubelet-bootstrap
启动 kubelet
并自动申请证书
在 Node 节点上配置 kubelet
,使其能够使用 Bootstrap Token 向 kube-apiserver
申请证书。 配置 kubelet
的启动参数,例如: 1 2 --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig
bootstrap.kubeconfig
是一个初始配置文件,包含 Bootstrap Token 和 API Server 地址。当 kubelet
启动时,它会使用 bootstrap.kubeconfig
中的 Token 向 kube-apiserver
申请证书,并将签发的证书保存到 kubelet.kubeconfig
文件中。 批准 CertificateSigningRequest (CSR)
kubelet
申请证书后,会在 Kubernetes 中生成一个 CSR 对象。使用以下命令查看未批准的 CSR: 1 kubectl certificate approve <csr-name>
或者,可以通过自动化工具(如控制器)自动批准所有来自 system:bootstrappers
组的 CSR。 完成证书签发
一旦 CSR 被批准,kube-apiserver
会为 kubelet
签发证书,并将其返回给 kubelet
。 kubelet
将签发的证书保存到指定的 kubeconfig
文件中,后续通信将使用该证书进行认证。部署Master组件 仅 Master 节点执行
1 2 3 4 5 6 7 8 wget https://dl.k8s.io/v1.18.20/kubernetes-server-linux-amd64.tar.gz tar zxf kubernetes-server-linux-amd64.tar.gz cd kubernetes/server/bincp kube-apiserver kube-scheduler kube-controller-manager /opt/kubernetes/bin/cp kubectl /usr/bin/
配置 kubeconfig 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 kubectl config set-cluster kubernetes \ --certificate-authority=/opt/kubernetes/ssl/ca.pem \ --embed-certs=true \ --server=https://192.168.56.101:6443 kubectl config set-credentials admin \ --client-certificate=/opt/kubernetes/ssl/admin.pem \ --client-key=/opt/kubernetes/ssl/admin-key.pem kubectl config set-context kubernetes \ --cluster=kubernetes \ --user=admin kubectl config use-context kubernetes
授权kubelet-bootstrap用户允许请求证书
1 2 3 kubectl create clusterrolebinding kubelet-bootstrap \ --clusterrole=system:node-bootstrapper \ --user=kubelet-bootstrap
部署kube-apiserver 创建 Bootstrap Token 文件 创建配置文件中Bootstrap Token
文件
1 2 3 4 5 6 7 8 9 10 11 BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ' ) cat > /opt/kubernetes/cfg/token.csv <<EOF ${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:node-bootstrapper" EOF cat /opt/kubernetes/cfg/token.csv80dfa93db74de1e423d1506e48ecfaa1,kubelet-bootstrap,10001,"system:node-bootstrapper"
字段说明
BOOTSTRAP_TOKEN
:随机生成的 Bootstrap Token(32 个字符)。kubelet-bootstrap
:用户名。10001
:用户 ID。"system:node-bootstrapper"
:用户所属的组。创建配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 cat > /opt/kubernetes/cfg/kube-apiserver.conf << EOF KUBE_APISERVER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --etcd-servers=https://192.168.56.101:2379,https://192.168.56.102:2379,https://192.168.56.103:2379 \\ --bind-address=192.168.56.101 \\ --secure-port=6443 \\ --advertise-address=192.168.56.101 \\ --allow-privileged=true \\ --service-cluster-ip-range=10.96.0.0/12 \\ --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \\ --authorization-mode=RBAC,Node \\ --enable-bootstrap-token-auth=true \\ --token-auth-file=/opt/kubernetes/cfg/token.csv \\ --service-node-port-range=30000-32767 \\ --kubelet-client-certificate=/opt/kubernetes/ssl/apiserver.pem \\ --kubelet-client-key=/opt/kubernetes/ssl/apiserver-key.pem \\ --tls-cert-file=/opt/kubernetes/ssl/apiserver.pem \\ --tls-private-key-file=/opt/kubernetes/ssl/apiserver-key.pem \\ --client-ca-file=/opt/kubernetes/ssl/ca.pem \\ --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --etcd-cafile=/opt/kubernetes/ssl/ca.pem \\ --etcd-certfile=/opt/kubernetes/ssl/etcd.pem \\ --etcd-keyfile=/opt/kubernetes/ssl/etcd-key.pem \\ --audit-log-maxage=30 \\ --audit-log-maxbackup=3 \\ --audit-log-maxsize=100 \\ --audit-log-path=/opt/kubernetes/logs/k8s-audit.log" EOF
注:上面两个\第一个是转义符,第二个是换行符,使用转义符是为了使用EOF保留换行符。
创建 systemd 服务文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 cat > /etc/systemd/system/kube-apiserver.service << EOF [Unit] Description=Kubernetes API Server Documentation=https://github.com/kubernetes/kubernetes After=etcd.service Wants=etcd.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS Restart=on-failure RestartSec=10s LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
启动服务 1 2 3 systemctl daemon-reload systemctl enable kube-apiserver systemctl start kube-apiserver
参数解释
--logtostderr
控制是否将日志输出到标准错误(stderr)。
默认值为 true
,即日志默认输出到 stderr。 设置为 false
时,日志会写入文件(通过 --log-dir
指定目录)。 --v
设置日志的详细级别(verbosity level)。
数值越高,日志越详细。例如:0
:仅记录关键信息。2
:记录大多数信息,适合调试。4
或更高:记录非常详细的调试信息。 --log-dir
指定日志文件存储的目录路径。
如果未设置,日志不会写入文件,而是输出到标准错误(除非 --logtostderr=true
)。 --etcd-servers
指定 etcd 集群的地址列表(逗号分隔)。
例如:https://192.168.56.101:2379,https://192.168.56.102:2379
。 Kubernetes 使用 etcd 存储集群的所有数据。 --bind-address
指定 kube-apiserver 监听的 IP 地址。
通常设置为节点的本地 IP 地址(如 192.168.56.101
),或者 0.0.0.0
表示监听所有地址。 --secure-port
指定 kube-apiserver 的 HTTPS 安全端口号。
默认值为 6443
,这是 Kubernetes 的标准安全端口。 --advertise-address
指定 kube-apiserver 向其他组件(如 kubelet、controller-manager 等)通告的 IP 地址。
通常与 --bind-address
一致,用于集群内部通信。 --allow-privileged
允许在容器中运行特权模式(privileged mode)。
设置为 true
时,允许 Pod 使用特权模式(例如访问主机设备)。 --service-cluster-ip-range
指定 Kubernetes Service 的虚拟 IP 地址范围。
例如:10.0.0.0/24
表示 Service 的 ClusterIP 将从该范围内分配。 这个范围不能与节点网络或 Pod 网络冲突。 --enable-admission-plugins
启用一组准入控制插件(Admission Controllers)。
这些插件在请求到达 API Server 时执行额外的验证或修改操作。 常见插件包括:NamespaceLifecycle
LimitRanger
ServiceAccount
NodeRestriction
--authorization-mode
指定 API Server 的授权模式。
常见模式包括: 多个模式用逗号分隔,例如:RBAC,Node
。 --enable-bootstrap-token-auth
启用 TLS Bootstrap 机制,允许节点使用临时 Token 自动注册并获取证书。
--token-auth-file
指定包含 Bootstrap Token 的文件路径,用于节点认证。
--service-node-port-range
指定 Service NodePort 类型的端口范围。
--kubelet-client-certificate
, --kubelet-client-key
指定 API Server 访问 kubelet 时使用的客户端证书和密钥文件路径。
--tls-cert-file
, --tls-private-key-file
指定 API Server 的 HTTPS 证书和密钥文件路径。
--client-ca-file
指定客户端 CA 证书文件路径,用于验证客户端证书。
--service-account-key-file
指定 Service Account 的私钥文件路径,用于签发 Service Account Token。
--etcd-cafile
, --etcd-certfile
, --etcd-keyfile
指定连接 etcd 集群时使用的 CA 证书、客户端证书和密钥文件路径。
--audit-log-path
, --audit-log-maxage
, --audit-log-maxbackup
, --audit-log-maxsize
配置审计日志相关参数。
包括:--audit-log-path
:审计日志文件路径。--audit-log-maxage
:保留审计日志的最大天数。--audit-log-maxbackup
:保留的最大审计日志文件数量。--audit-log-maxsize
:单个审计日志文件的最大大小(以 MB 为单位)。 部署kube-controller-manager 创建配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cat > /opt/kubernetes/cfg/kube-controller-manager.conf <<EOF KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --leader-elect=true \\ --master=127.0.0.1:8080 \\ --bind-address=127.0.0.1 \\ --allocate-node-cidrs=true \\ --cluster-cidr=10.244.0.0/16 \\ --service-cluster-ip-range=10.96.0.0/12 \\ --cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \\ --cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --root-ca-file=/opt/kubernetes/ssl/ca.pem \\ --service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --experimental-cluster-signing-duration=87600h0m0s" EOF
–master:通过本地非安全本地端口8080连接apiserver。
–leader-elect:当该组件启动多个时,自动选举(HA)
–cluster-signing-cert-file/–cluster-signing-key-file:自动为kubelet颁发证书 的CA,与apiserver保持一致
systemd管理controller-manager 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 cat > /etc/systemd/system/kube-controller-manager.service <<EOF [Unit] Description=Kubernetes Controller Manager Documentation=https://kubernetes.io/docs/concepts/overview/components/ After=kube-apiserver.service Requires=kube-apiserver.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS Restart=always RestartSec=10s LimitNOFILE=65536 StandardOutput=syslog StandardError=syslog SyslogIdentifier=kube-controller-manager [Install] WantedBy=multi-user.target EOF
启动并设置开机启动 1 2 3 4 5 6 7 systemctl daemon-reload systemctl enable kube-controller-manager systemctl start kube-controller-manager systemctl status kube-controller-manager -l journalctl -u kube-controller-manager | grep -i error
部署kube-scheduler 1.创建配置文件
1 2 3 4 5 6 7 8 cat > /opt/kubernetes/cfg/kube-scheduler.conf <<EOF KUBE_SCHEDULER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --leader-elect \\ --master=127.0.0.1:8080 \\ --bind-address=127.0.0.1" EOF
–master:通过本地非安全本地端口8080连接apiserver。
–leader-elect:当该组件启动多个时,自动选举(HA)
systemd管理scheduler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 cat > /etc/systemd/system/kube-scheduler.service <<EOF [Unit] Description=Kubernetes Scheduler Documentation=https://kubernetes.io/docs/concepts/overview/components/ [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS Restart=always RestartSec=10s LimitNOFILE=65536 MemoryLimit=512M CPUQuota=100% StandardOutput=syslog StandardError=syslog SyslogIdentifier=kube-scheduler [Install] WantedBy=multi-user.target EOF
启动并设置开机启动 1 2 3 4 5 6 7 systemctl daemon-reload systemctl enable kube-scheduler systemctl start kube-scheduler systemctl status kube-scheduler -l journalctl -u kube-scheduler | grep -i error
查看集群状态 所有组件都已经启动成功,通过kubectl工具查看当前集群组件状态:
1 2 3 kubectl get componentstatus kubectl get cs
预期输出:
1 2 3 4 5 6 NAME STATUS MESSAGE ERROR controller-manager Healthy ok scheduler Healthy ok etcd-2 Healthy {"health":"true"} etcd-0 Healthy {"health":"true"} etcd-1 Healthy {"health":"true"}
部署node组件 下载并拷贝二进制文件
1 2 3 4 wget https://dl.k8s.io/v1.18.20/kubernetes-server-linux-amd64.tar.gz tar zxf kubernetes-server-linux-amd64.tar.gz cd kubernetes/server/bincp kubectl kubelet kube-proxy /opt/kubernetes/bin/
添加环境变量
1 2 echo 'export PATH=$PATH:/opt/kubernetes/bin' | sudo tee -a /etc/profilesource /etc/profile
部署kubelet 创建kubelet配置文件
1 2 3 4 5 6 7 8 9 10 11 12 cat >/opt/kubernetes/cfg/kubelet.conf <<EOF KUBELET_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --hostname-override=k8snode1 \\ --network-plugin=cni \\ --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \\ --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \\ --config=/opt/kubernetes/cfg/kubelet-config.yml \\ --cert-dir=/opt/kubernetes/ssl \\ --pod-infra-container-image=lizhenliang/pause-amd64:3.0" EOF
记得修改 -hostname-override=k8snode1
–hostname-override:显示名称,集群中唯一
–network-plugin:启用CNI
–kubeconfig:空路径,会自动生成,后面用于连接apiserver
–bootstrap-kubeconfig:首次启动向apiserver申请证书
–config:配置参数文件
–cert-dir:kubelet证书生成目录
–pod-infra-container-image:管理Pod网络容器的镜像
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 cat >/opt/kubernetes/cfg/kubelet-config.yml <<EOF apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration address: 0.0.0.0 port: 10250 readOnlyPort: 10255 cgroupDriver: systemd # 根据Docker配置调整(如systemd/cgroupfs) clusterDNS: - 10.96.0.10 # 默认ClusterDNS为kube-dns服务IP clusterDomain: cluster.local failSwapOn: false authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /opt/kubernetes/ssl/ca.pem authorization: mode: Webhook webhook: cacheAuthorizedTTL: 5m0s cacheUnauthorizedTTL: 30s evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% maxOpenFiles: 100000 maxPods: 110 EOF
执行以下命令会生成bootstrap.kubeconfig文件,移动至/opt/kubernetes/cfg/bootstrap.kubeconfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 KUBE_APISERVER="https://192.168.56.101:6443" TOKEN="80dfa93db74de1e423d1506e48ecfaa1" kubectl config set-cluster kubernetes \ --certificate-authority=/opt/kubernetes/ssl/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=bootstrap.kubeconfig kubectl config set-credentials "kubelet-bootstrap" \ --token=${TOKEN} \ --kubeconfig=bootstrap.kubeconfig kubectl config set-context default \ --cluster=kubernetes \ --user="kubelet-bootstrap" \ --kubeconfig=bootstrap.kubeconfig kubectl config use-context default --kubeconfig=bootstrap.kubeconfig mv bootstrap.kubeconfig /opt/kubernetes/cfg/
systemd管理kubelet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 cat > /etc/systemd/system//kubelet.service <<EOF [Unit] Description=Kubernetes Kubelet After=docker.service Requires=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS Restart=on-failure RestartSec=5 LimitNOFILE=65536 LimitNPROC=infinity LimitCORE=infinity Delegate=yes KillMode=process [Install] WantedBy=multi-user.target EOF
启动并设置开机启动
1 2 3 4 5 6 7 8 9 systemctl daemon-reload systemctl start kubelet systemctl enable kubelet systemctl status kubelet
Master批准kubelet证书申请并加入集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ kubectl get csr NAME AGE SIGNERNAME REQUESTOR CONDITION node-csr-RCpKZeogM1MIxtouElJ1WDXPNDe-raOBpkv5gyHt8h0 30m kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending $ kubectl certificate approve node-csr-RCpKZeogM1MIxtouElJ1WDXPNDe-raOBpkv5gyHt8h0 certificatesigningrequest.certificates.k8s.io/node-csr-RCpKZeogM1MIxtouElJ1WDXPNDe-raOBpkv5gyHt8h0 $ kubectl get node NAME STATUS ROLES AGE VERSION k8snode1 NotReady <none> 16s v1.18.20 k8snode2 NotReady <none> 11s v1.18.20
注:由于网络插件还没有部署,节点会没有准备就绪 NotReady
部署kube-proxy 1.1 kube-proxy.conf
配置文件
1 2 3 4 5 6 cat > /opt/kubernetes/cfg/kube-proxy.conf << EOF KUBE_PROXY_OPTS="--logtostderr=false \ --v=2 \ --log-dir=/opt/kubernetes/logs \ --config=/opt/kubernetes/cfg/kube-proxy-config.yml" EOF
1.2 kube-proxy-config.yml
参数文件
1 2 3 4 5 6 7 8 9 10 cat > /opt/kubernetes/cfg/kube-proxy-config.yml << EOF apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration bindAddress: 0.0.0.0 metricsBindAddress: 0.0.0.0:10249 clientConnection: kubeconfig: /opt/kubernetes/cfg/kube-proxy.kubeconfig hostnameOverride: "k8snode1" # 替换为当前节点的主机名 clusterCIDR: 10.0.0.0/24 EOF
说明 :
apiVersion
:使用最新版本 v1
(原 v1alpha1
已弃用)。bindAddress
:监听所有网络接口。metricsBindAddress
:监控端口。hostnameOverride
:节点名称(需与 kubelet
配置一致)。clusterCIDR
:Pod 网络 CIDR。生成证书和 kubeconfig
文件 2.1 生成 kube-proxy.kubeconfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 KUBE_APISERVER="https://192.168.56.101:6443" kubectl config set-cluster kubernetes \ --certificate-authority=/opt/kubernetes/ssl/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=kube-proxy.kubeconfig kubectl config set-credentials kube-proxy \ --client-certificate=/opt/kubernetes/ssl/kube-proxy.pem \ --client-key=/opt/kubernetes/ssl/kube-proxy-key.pem \ --embed-certs=true \ --kubeconfig=kube-proxy.kubeconfig kubectl config set-context default \ --cluster=kubernetes \ --user=kube-proxy \ --kubeconfig=kube-proxy.kubeconfig kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
2.2 拷贝文件到指定路径
1 cp kube-proxy.kubeconfig /opt/kubernetes/cfg/
配置 systemd
服务 3.1 创建 kube-proxy.service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 cat > /usr/lib/systemd/system/kube-proxy.service << EOF [Unit] Description=Kubernetes Proxy After=network.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-proxy.conf ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS Restart=on-failure LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
3.2 启动并设置开机启动
1 2 3 systemctl daemon-reload systemctl start kube-proxy systemctl enable kube-proxy
验证部署 1 2 3 4 5 systemctl status kube-proxy journalctl -u kube-proxy -f --since "5 minutes ago"
部署集群网络 Node节点操作(所有Worker Node执行) 安装CNI插件(容器网络接口)
1 2 3 4 5 6 wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz mkdir -p /opt/cni/bintar zxf cni-plugins-linux-amd64-v0.8.6.tgz -C /opt/cni/bin
作用 :CNI插件是Kubernetes容器网络的基础组件,负责为Pod分配IP、配置网络路由和连通性。 常见的插件如bridge
、flannel
、host-local
等会在此目录中提供二进制工具。 预期结果 :/opt/cni/bin
目录下会包含一系列CNI插件二进制文件(如flanneld
、bridge
等)。若无此步骤,Pod将无法获得IP地址,节点状态会持续为NotReady
。 Master节点操作 部署Flannel网络插件 1 2 3 4 5 wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml kubectl apply -f kube-flannel.yml
作用 :Flannel是一个Overlay网络解决方案,负责为集群中的Pod分配子网并实现跨节点通信。 kube-flannel.yml
定义了Flannel的DaemonSet、ServiceAccount、RBAC规则等资源。预期结果 :
每个Node节点会自动启动一个kube-flannel
Pod(通过DaemonSet部署)。 执行以下命令验证 1 kubectl get pods -n kube-flannel
输出示例:
1 2 3 NAME READY STATUS RESTARTS AGE kube-flannel-ds-lc985 1/1 Running 0 2m28s kube-flannel-ds-vbv67 1/1 Running 0 2m28s
所有节点的STATUS
应从NotReady
变为Ready
(需等待1-2分钟)。 授权API Server访问kubelet 创建 RBAC 配置文件 :创建 apiserver-to-kubelet-rbac.yaml
文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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 - pods/log 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
应用 RBAC 配置文件 :1 kubectl apply -f apiserver-to-kubelet-rbac.yaml
作用 :允许API Server通过kubelet的API访问Pod日志、监控指标等。 若无此授权,执行kubectl logs
或kubectl exec
时会报权限错误。 测试kubernetes集群 在kubernetes集群中创建一个pod,验证是否正常运行:
步骤 1:创建 Deployment
1 kubectl create deployment nginx --image=nginx
作用 : 创建了一个名为 nginx
的 Deployment,使用官方的 nginx
镜像。结果 : Kubernetes 会自动创建一个 ReplicaSet,并调度一个 Pod 运行 Nginx 容器。步骤 2:暴露服务
1 kubectl expose deployment nginx --port=80 --type =NodePort
作用 : 创建了一个 Service,将 nginx
Deployment 的 80 端口暴露为 NodePort
类型的服务。结果 : Kubernetes 会分配一个随机的高范围端口(默认范围是 30000-32767),并将流量转发到 Nginx Pod 的 80 端口。步骤 3:查看 Pods
确认 nginx
Pod 是否正常运行:
1 2 3 4 5 6 7 NAME READY STATUS RESTARTS AGE pod/nginx-f89759699-496vr 1/1 Running 0 3m26s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h service/nginx NodePort 10.97.173.113 <none> 80:31366/TCP 28s
解释:nginx
Service 的类型为 NodePort
。内部 ClusterIP 是 10.100.123.45
。 外部访问端口是 31366
(随机分配的)。 通过 NodePort 访问 : 假设节点 IP 地址是 192.168.56.102/103
,可以通过以下命令任意node节点IP访问:
1 curl http://192.168.56.102:30001
新增 Worker Node 拷贝已部署好的 Node 相关文件到新节点 在 Master 节点上,将以下文件拷贝到新节点(例如:192.168.56.102
或 192.168.56.103
):
1 2 3 4 scp -r /opt/kubernetes root@192.168.56.102:/opt/ scp -r /usr/lib/systemd/system/{kubelet,kube-proxy}.service root@192.168.56.102:/usr/lib/systemd/system/ scp -r /opt/cni/ root@192.168.56.102:/opt/ scp /opt/kubernetes/ssl/ca.pem root@192.168.56.102:/opt/kubernetes/ssl/
删除 kubelet 证书和 kubeconfig 文件 在新节点上删除旧的 kubelet 证书和 kubeconfig 文件:
1 2 rm /opt/kubernetes/cfg/kubelet.kubeconfigrm -f /opt/kubernetes/ssl/kubelet*
注:这几个文件是证书申请审批后自动生成的,每个Node不同,必须删除重新生成。
修改主机名 在新节点上修改 kubelet 和 kube-proxy 的配置文件中的主机名:
编辑文件:
1 2 3 4 5 6 7 vi /opt/kubernetes/cfg/kubelet.conf --hostname-override=k8s-node3 vi /opt/kubernetes/cfg/kube-proxy-config.yml hostnameOverride: k8s-node3
启动服务并设置开机启动 1 2 3 4 5 systemctl daemon-reload systemctl start kubelet systemctl enable kubelet systemctl start kube-proxy systemctl enable kube-proxy
在 Master 上批准新 Node 的 kubelet 证书申请 在 Master 节点上查看证书签名请求(CSR):
1 2 3 4 kubectl get csr kubectl certificate approve <CSR_NAME>
查看Node状态