Kubernetes: k8s-安装篇-二进制-1.37

概述

k8s 架构和组件

整体架构

kubernetes(k8s) 是一个容器编排平台,核心目标:

  • 自动部署、自动扩容、服务发现、负载均衡、自愈能力、滚动更新、高可用管理。
kubernetes-cluster-architecture.svg

Kubernetes 集群由一个控制平面和一组用于运行容器化应用的工作机器组成, 这些工作机器称作节点(Node)。每个集群至少需要一个工作节点来运行 Pod。

工作节点托管着组成应用负载的 Pod。控制平面管理集群中的工作节点和 Pod。

控制平面组件

  • kube-apiserver:集群核心入口,负责处理接受请求的工作。以下所有组件必需通过 apiserver 通信
    • kubectl\controller\scheduler\kubelet
  • etcd:键值数据库,保存集群的状态信息。高可用,通常为奇数节点。保存内容包括
    • Pod\Deployment\Node\ConfigMap\Secret\Service\Endpoint
  • kube-scheduler:负责 pod 的调度,决定 pod 运行在哪个 node 节点。调试依据
    • CPU\内存\污点容忍\节点亲和性\Pod亲和性\数据本地性
  • kube-controller-manager:运行各种控制器,保证 Pod 或其他资源到达期望值。常见的控制器
    • Deployment Controller\ReplicaSet Controller\Node Controller\Endpoint Controller\ServeAccount Controller
  • cloud-controller-manager:允许将你的集群连接到云提供商的 API 之上。

节点组件

  • kubelet:管理当前节点的 Pod,对容器进行健康检查及监控。包括
    • 接收PodSpec(从 apiserver 获取)
    • 调用容器运行时。如 containerd\CRI-O
    • 健康检查:livenessProbe\redinessProbe等
    • 上报节点状态:CPU\内存\磁盘
    • 挂载存储:PVC\ConfigMap
  • kube-proxy: 实现 Service 网络转发。工作模式:
    • iptables:通过 iptable NAT 实现转发
    • IPVS: 性能高
    • eBPF (新趋势):Cilium (可替代 kube-proxy)
  • 容器运行时(runtime):负责容器的执行和生命周期。常见的有
    • containerd(官方推荐)
    • CRI-O
    • Docker(已弃用 dockershim) 1.24 版本后移除。

插件(Addons)

k8s 证书体系

Kubernetes 相关组件之间相互访问是基于 Mutual TLS(mTLS) 双向通信,而证书是保障组件间安全通信的关键基础设施。

Kubernetes常见的证书分类

官方参考:

在Kubernetes常见的证书主要分为三类:

类型 作用 谁持有 谁使用它用于验证 是否用于加密通信
CA 证书 签发和信任其他证书 证书颁发机构(自己) 验证服务器/客户端证书是否合法 间接参与
服务器证书 证明"我是真实的服务器" apiserver、etcd、等服务端 被客户端使用,用于验证服务器
客户端证书 证明"我是合法客户端",不是陌生人 kubectl、kubelet、scheduler 等客户端 被服务端使用,用于验证客户端 是,用于双向加密

在Kubernetes中, 不同组件使用的证书角色并不一样 ,不能简单地统一说"都是服务端证书"或者"都是客户端证书"。

本质上要看:这个组件是"主动去访问别人",还是"被别人访问问"。

CA 证书

负责:身份证、证明身份证是真的

它不直接参与业务通信,只负责:“这个人身份是否合法”

k8s CA 证书文件

  • ca.pem、ca-key.pem

作用:签发下面证书

  • apiserver
  • kubelet
  • admin
  • controller-manager
  • scheduler
  • kube-proxy

注意:ca-key.pem 极其重要:绝不能泄漏,因为它能签发所有证书。

服务器证书

服务器需要证明:“我真的是真实的服务器,不是骗子网站”

客户端(浏览器/kubectl) 访问时:先检查这个服务器证书是不是 CA 签发的。如果是,才信任它。

k8s 中的服务器证书,如

  • kube-apiserver.pem、etcd.pem

客户端都会验证服务器证书,验证服务端的真实性。客户端包括

  • kubectl\kubelet\scheduler\controller-manager

服务器证书必须包含:SAN (Subject Alternative Name),如

"hosts": [
    "127.0.0.1",
    "10.96.0.1",
    "10.103.236.201",
    "kubernetes",
    "kubernetes.default",
    "master01"
    ]

否则会报错:

x509: certificate is valid for xxx, not yyy
客户端证书

客户端证书 = 用户身份证

客户端也需要证明:“我真的是合法用户,不是陌生人”

服务端验证:你的身份是不是 CA 签发的。

k8s 中的客户端证书,如

admin.pem
kubelet.pem
scheduler.pem
controller-manager.pem

客户端身份用下面说明

CN + O(Common Name + Organization)

# CN = 用户名
# O = 用户组

# {
  "CN": "admin",
  "O": "system:masters"

}

k8s 会根据: CN + O 做 RBAC 权限控制。

k8s 三个 CA

k8s 的证书体系本质是 分域 PKI(Public Key Intrastructure) 设计 ,默认包含三套 CA:

每个 CA 负责不同的信任域,彼此隔离

CA 类型 文件 作用域 负责签发对象
Cluster CA(主 CA) ca.crt/ca.key k8s 核心组件 apiserver\kubelet\controller\scheduler\admin
etcd CA etcd/ca.crt、etcd/ca.key etcd 集群通信 etcd server/peer/healthcheck/apiserver-etcd-client
front-proxy CA front-proxy-ca.crt/front-proxy-ca.key 聚合层(API Aggregation) front-proxy-client

k8s CA(主 CA) 和相关证书

作用:用于 k8s 控制面 + 节点核心认证体系。

签发证书列表

证书 类型 用途
apiserver.crt Server Cert kube-apiserver HTTPS
apiserver-kubelet-client.crt Client Cert apiserver → kubelet
apiserver-etcd-client.crt Client Cert apiserver → etcd
apiserver-key.pem Private Key apiserver私钥
admin.conf Client Cert kubectl管理员
controller-manager.conf Client Cert controller-manager
scheduler.conf Client Cert scheduler
kubelet-client.crt Client Cert kubelet→apiserver(部分模式)

特点

  • k8s 核心信任根
  • 默认 kubeadm 使用 CA
  • 控制面统一信任域

etcd CA 和相关证书

作用:用于 etcd 集群内部以及 etcd 对外通信

签发证书列表

证书 类型 用途
server.crt Server Cert etcd 服务端
peer.crt Dual Cert etcd 节点间通信(mTLS)
healthcheck-client.crt Client Cert 健康检查
apiserver-etcd-client.crt Client Cert apiserver 访问 etcd

特点

  • 独立信任域(与 Kubernetes CA隔离)
  • etcd 内部强 mTLS
  • 数据库级安全边界

front-proxy CA 和相关证书

作用:用于 k8s API 聚合层

签发证书列表

证书 类型 用途
front-proxy-client.crt Client Cert apiserver(kube-aggregator)→extension-apisrver,prometheus-adapter)
front-proxy-client.key Private Key 私钥
front-proxy-ca.crt CA 该域信任根

使用场景

  • metrics-server
  • custom metrics API
  • aggregated apiserver
  • extension apiserver

特点

  • 用于 API 聚合认证
  • 解决“API 扩展链路信任问题”
  • 独立于主 CA

环境初始化

请将该文档复制一份,然后进行更改安装。

文档中的 IP 地址请统一替换,不要一个一个替换

网段划分

集群安装时会涉及到三个网段:

  • 宿主机网段:就是安装k8s的服务器
  • Pod网段:k8s Pod的网段,相当于容器的IP
  • Service网段:k8s service网段,service用于集群容器通信。

一般 service 网段会设置为10.96.0.0/12

Pod 网段会设置成10.244.0.0/12 或者 172.16.0.1/12

宿主机网段可能是 192.168.0.0/24

需要注意的是这三个网段不能有任何交叉。

比如如果宿主机的IP是10.105.0.x

那么service网段就不能是10.96.0.0/12,因为10.96.0.0/12网段可用IP是:

10.96.0.1 ~ 10.111.255.255

所以10.105是在这个范围之内的,属于网络交叉,此时service网段需要更换,

可以更改为192.168.0.0/16网段(注意如果service网段是192.168开头的子网掩码最好不要是12,最好为16,因为子网掩码是12他的起始IP为192.160.0.1 不是192.168.0.1)。

同样的道理,技术别的网段也不能重复。可以通过 http://tools.jb51.net/aideddesign/ip_net_calc/ 计算。

所以一般的推荐是,直接第一个开头的就不要重复,比如

  • 你的宿主机是 192 开头的,那么你的 service可以是 10.96.0.0/12.
  • 如果你的宿主机是 10 开头的,就直接把 service的 网段改成 192.168.0.0/16
  • 如果你的宿主机是 172 开头的,就直接把 pod 网段改成 192.168.0.0/12

注意搭配,均为 10 网段、172 网段、192 网段的搭配,第一个开头数字不一样就免去了网段冲突的可能性,也可以减去计算的步骤。

安装及优化部分

  • 基本环境配置及优化
  • 安装 Runtime
  • k8s 组件 及 etcd 安装
  • 高可用实现

集群建立部分

  • 生成集群证书
  • Master 节点初始化
  • Node 节点配置
  • CNI 插件安装

Addons 安装

  • Metrics Server
  • Dashboard
  • CoreDNS

收尾工作

  • 集群可用性验证
  • 生产必备配置

机器规划

k8s官网:https://kubernetes.io/docs/setup/

主机名 IP地址 版本 配置
master01.jasper.org~03 10.103.236.201~203 Ubuntu 26.04 master 节点 * 3,2C2G 40G
kubeapi.jasper.org 10.103.236.236 Ubuntu 26.04 keepalived虚拟 IP
node01.jasper.org~02 10.103.236.204~205 Ubuntu 26.04 node 节点 * 2,2C2G 40G

请统一替换这些网段,宿主机网段、K8s Service网段、Pod网段不能重复!

配置信息 备注
Pod网段 172.16.0.0/12
Service网段 192.168.0.0/16

主机信息,服务器IP地址不能设置成dhcp,要配置成静态IP。

VIP(虚拟IP)不要和公司内网IP重复,首先去 ping一下,不通才可用。VIP 需要和主机在同一个局域网内!公有云的话,VIP 为公有云的负载均衡的IP,比如阿里云的 SLB 地址,腾讯云的 ELB 地址,注意公有云的负载均衡都是内网的负载均衡。

环境变量定义(master01)执行

方便后面执行命令,可以先定义相关变量,可选

root@jasper-server:~# cat /etc/os-release
PRETTY_NAME="Ubuntu 26.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="26.04"
VERSION="26.04.1 LTS (Resolute Raccoon)"
VERSION_CODENAME=resolute
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=resolute
LOGO=ubuntu-logo
MASTER_IPS="10.103.236.201 10.103.236.202 10.103.236.203"
NODE_IPS="10.103.236.204 10.103.236.205"

K8S_VERSION=v1.37.0
ETCD_VERSION=v3.7.0

CONTAINERD_VERSION=2.3.5
#CRI_DOCKER_VERSION=0.4.2

POD_CIDR=172.16.0.0/12
SERVICE_CIDR=192.168.0.0/16
#POD_CIDR=10.244.0.0/16
#SERVICE_CIDR=10.96.0.0/16

IFACE=enp2s0
#IFACE=eth0

配置 SSH 名密登录(master1 执行)

Master01 节点免密钥登录其他节点,安装过程中生成配置文件和证书均在 Master01 上操作,集群管理也在 Master01 上操作,阿里云或者 AWS 上需要单独一台 kubectl 服务器。密钥配置如下:

ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
for i in master01.jasper.org master02.jasper.org master03.jasper.org node01.jasper.org node02.jasper.org;do
    ssh-copy-id -i ~/.ssh/id_rsa.pub $i;
done

#for i in $MASTER_IPS  $NODE_IPS;do
    ssh-copy-id -i ~/.ssh/id_rsa.pub $i;
done

主机名和域名解析(所有节点执行)

# 1. 设置主机名(各自节点执行对应命令)
# Master 节点
hostnamectl set-hostname master01.jasper.org && exit

# Node 节点
hostnamectl set-hostname node01.jasper.org && exit

# 2. 配置  /etc/hosts,添加所有节点的 IP 与主机名解析
cat >> /etc/hosts << EOF
10.103.236.201 master01.jasper.org master01
10.103.236.202 master02.jasper.org master02
10.103.236.203 master03.jasper.org master03
10.103.236.236 kubeapi.jasper.org kubeapi # VIP 虚IP不占用机器资源 # 如果不是高可用集群,该 IP 为 Master01 的 IP
10.103.236.204 node01.jasper.org node01
10.103.236.205 node02.jasper.org node02
EOF

注意: /etc/hosts 配置确保节点间可以通过主机名相互解析,这对 k8s 集群内通信至关重要

必备工具(可选)

# CentOS 7 安装yum源如下(作废)
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
yum install -y yum-utils device-mapper-persistent-data lvm2

yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo

# 必备工具安装
yum install wget jq psmisc vim net-tools telnet yum-utils device-mapper-persistent-data lvm2 git -y

关闭swap分区(所有节点执行)

swapoff -a && sysctl -w vm.swappiness=0
sed -ri '/^[^#]*swap/s@^@#@' /etc/fstab

# k8s 要求禁用 swap 以确保调试和资源管理的准确性。开启 swap
# 时 kubelet 默认拒绝启动, k8s-1.22 以后版本可以
# kubectl --fail-swap-on=false 允许开启 swap

同步时间(所有节点执行)

方法1-基于 chrony 实现时间同步(推荐)

Kubernetes 官方不建议使用 systemd-timesyncd,推荐 chrony

# 安装
apt update
apt install  -y chrony
systemctl enable --now  chrony
systemctl status chrony

#配置
cat > /etc/chrony/sources.d/ubuntu-ntp-pools.sources << EOF
server ntp1.aliyun.com iburst minpoll 4 maxpoll 10
server cn.pool.ntp.org iburst minpoll 4 maxpoll 10
server s1b.time.edu.cn iburst minpoll 4 maxpoll 10
EOF

# 把原来 makestep 1.0 3 修改,去掉次数限制,偏差>1s就允许跳
cp /etc/chrony/chrony.conf{,.bak}
sed -i 's/^makestep .*/makestep 1 -1/g' /etc/chrony/chrony.conf

systemctl restart chrony.service
timedatectl set-timezone Asia/Shanghai # 修改时区
chronyc -a makestep # 强制同步下系统时钟
timedatectl set-ntp yes # 启用NTP服务
timedatectl
# 查看时间同步源:
chronyc sources -v
# 查看时间同步源状态:
chronyc sourcestats -v
# 查看同步偏移量,看System time     : 偏移值,越小越好
chronyc tracking

方法2-基于 systemd-timesyncd 实现时间同步

apt update
apt install systemd-timesyncd
# 开机自启
systemctl enable --now systemd-timesyncd

timedatectl status

# ubuntu 默认安装的轻量时间同步 systemd-timesyncd 可以支持时间同步,但不如 chronyd 精度高
systemctl status systemd-timesyncd

# 优化为国内时间同步配置
cp /etc/systemd/timesyncd.conf{,.bak}
cat > /etc/systemd/timesyncd.conf << EOF
[Time]
NTP=ntp.aliyun.com ntp.tencent.com ntp.ntsc.ac.cn
FallbackNTP=cn.pool.ntp.org 0.cn.pool.ntp.org 1.cn.pool.ntp.org
RootDistanceMaxSec=5
PollIntervalMinSec=32
PollIntervalMaxSec=2048
ConnectionRetrySec=30
SaveIntervalSec=60
EOF
systemctl restart systemd-timesyncd
timedatectl timesync-status

方法3-基于 ntpdate 的同时同步

# 安装
rpm -ivh http://mirrors.wlnmp.com/centos/wlnmp-release-centos.noarch.rpm
yum install ntpdate -y

# 所有节点同步时间。时间同步配置如下
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo 'Asia/Shanghai' >/etc/timezone
ntpdate time2.aliyun.com

# 加入到crontab
*/5 * * * * /usr/sbin/ntpdate time2.aliyun.com &>/dev/null

设置时区

# 列出所有时区
timedatectl list-timezones |grep Shanghai
# 设置时区(上海时区 CST+8)
timedatectl set-timezone Asia/Shanghai
chronyc -a makestep # 强制同步下系统时钟
timedatectl set-ntp yes 
# 检查时区
ll /etc/localtime
timedatectl

关闭防火墙(所有节点执行)

所有节点关闭firewalld 、dnsmasq、selinux(CentOS7需要关闭NetworkManager,CentOS8不需要)

# Ubuntu 默认使用 ufw,不是 firewalld
ufw disable
ufw status

# CentOS
systemctl disable --now firewalld 
systemctl disable --now dnsmasq
systemctl disable --now NetworkManager

setenforce 0
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/sysconfig/selinux
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config

limit(所有节点执行)

ulimit -SHn 65535

cat >/etc/security/limits.d/k8s.conf<<EOF
* soft nofile 1000000
* hard nofile 1000000
* soft nproc 1000000
* hard nproc 1000000
* soft memlock unlimited
* hard memlock unlimited

root soft nofile 1000000
root hard nofile 1000000
root soft nproc unlimited
root hard nproc unlimited
root soft memlock unlimited
root hard memlock unlimited
EOF

代理模式 nftables (所有节点执行)

kube‑proxy nftables 模式要求:内核 ≥5.13,nft 命令版本 ≥1.0.0

kube‑proxy,启用 nftables 模式

  • 方式 A:使用 --proxy-mode=nftables 命令行参数(简单)
  • 方式 B:外部 KubeProxyConfiguration yaml 配置文件(推荐生产,参数完整可控)
# 1.安装 nftalbes
apt update
apt install -y nftables
# 启用nftables服务(kube‑proxy本身不需要依赖这个服务开机加载自定义规则,只是提供nft工具)
systemctl enable --now nftables
# 验证
nft --version

#kube‑proxy 会自动维护 table ip kube-proxy,不要在 /etc/nftables.conf 里操作这张表,否则冲突。

# 2.加载必要内核模块
modinfo  br_netfilter
modprobe br_netfilter
lsmod |grep br_netfilter

cat>/etc/modules-load.d/k8s.conf <<'EOF'
br_netfilter
overlay
EOF
#生效
systemctl enable --now systemd-modules-load.service

lsmod | grep -E "br_netfilter|overlay|nf_tables"

# 3. 优化内核参数

cat <<EOF > /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.file-max=10485760
fs.nr_open=10485760
EOF
# 使用内核配置生效
sysctl --system

# 说明
# net.ipv4.ip_forward = 1                  # 启用 IP 转发,这是容器网络通信的基础
# net.bridge.bridge-nf-call-iptables = 1   # 让网桥上的流量经过 iptables规则,用于 service 和网络策略
# net.bridge.bridge-nf-call-ip6tables = 1  # 让网桥上的流量经过 iptables规则,用于 service 和网络策略

ubuntu ipvs(1.35 弃用)

# 1.安装 IPVS 模式所依赖的基础工具,否则 IPVS 模式下的 kube-proxy.service 服务无法启动
apt update && apt install -y ipvsadm ipset # (sysstat conntrack)
#注释:
#ipvsadm: 用于 IPVS 负载均衡模式(kube-proxy的一种模式)。
#ipset,conntrack: 网络过滤和连接跟踪工具,为Service网络提供支持。

# 2.加载必要内核模块
cat>/etc/modules-load.d/k8s.conf <<'EOF'
br_netfilter
overlay
EOF

#生效
systemctl enable --now systemd-modules-load.service

# 3. 优化内核参数

cat <<EOF > /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.file-max=10485760
fs.nr_open=10485760
EOF
# 说明
# net.ipv4.ip_forward = 1                  # 启用 IP 转发,这是容器网络通信的基础
# net.bridge.bridge-nf-call-iptables = 1   # 让网桥上的流量经过 iptables规则,用于 service 和网络策略
# net.bridge.bridge-nf-call-ip6tables = 1  # 让网桥上的流量经过 iptables规则,用于 service 和网络策略

# 使用内核配置生效
sysctl --system

内核升级

所有节点升级系统并重启,此处升级没有升级内核,下节会单独升级内核:

yum update -y --exclude=kernel* && reboot #CentOS7需要升级,CentOS8可以按需升级系统

CentOS7 需要升级内核至4.18+,本次升级的版本为4.19

在master01节点下载内核:(购买架构师课程的可以从百度网盘下载)

cd /root
wget http://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-devel-4.19.12-1.el7.elrepo.x86_64.rpm
wget http://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-4.19.12-1.el7.elrepo.x86_64.rpm

从master01节点传到其他节点:

for i in master02.jasper.org master03.jasper.org node01.jasper.org node02.jasper.org;do scp kernel-ml-4.19.12-1.el7.elrepo.x86_64.rpm kernel-ml-devel-4.19.12-1.el7.elrepo.x86_64.rpm $i:/root/ ; done

所有节点安装内核

cd /root && yum localinstall -y kernel-ml*

所有节点更改内核启动顺序

grub2-set-default  0 && grub2-mkconfig -o /etc/grub2.cfg

grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"

检查默认内核是不是4.19

[root@master02.jasper.org ~]# grubby --default-kernel
/boot/vmlinuz-4.19.12-1.el7.elrepo.x86_64

所有节点重启,然后检查内核是不是4.19

[root@master02.jasper.org ~]# uname -a
Linux master02.jasper.org 4.19.12-1.el7.elrepo.x86_64 #1 SMP Fri Dec 21 11:06:36 EST 2018 x86_64 x86_64 x86_64 GNU/Linux

安装容器运行时(所有节点执行)

安装容器运行时 containerd

ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)
CONTAINERD_VERSION=2.3.5
curl -SL -o containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz

tar tf containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz
tar Cxzvf /usr/local containerd-${CONTAINERD_VERSION}-linux-${ARCH}.tar.gz

#修改 containerd 配置基于 toml(Tom's Obvious Minim1Language)格式:toml.io
mkdir /etc/containerd/
containerd config default >/etc/containerd/config.toml

# 1) 将 sandbox 镜像源设置为阿里云 google_containers 镜像源(国内网络需要)
grep sandbox /etc/containerd/config.toml

sed -i "s#registry.k8s.io/pause#registry.aliyuncs.com/google_containers/pause#g" /etc/containerd/config.toml

# 开启 systemd cgroup
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

# 2) 配置 docker 官方镜像代理(可选)
## registry 块里面的 config_path
sed -rn '/config_path = .*/p' /etc/containerd/config.toml

sed -ri "/\[plugins.'io.containerd.cri.v1.images'.registry/,/\[/s@config_path = .*@config_path = '/etc/containerd/certs.d'@g" /etc/containerd/config.toml


mkdir -p /etc/containerd/certs.d/docker.io
mkdir -p /etc/containerd/certs.d/registry.k8s.io

## docker.io 加速文件
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
  capabilities = ["pull","resolve"]

[host."https://docker.lpanel.live"]
  capabilities = ["pull","resolve"]

[host."https://docker.lms.run"]
  capabilities = ["pull","resolve"]

[host."https://docker.xuanyuan.me"]
  capabilities = ["pull","resolve"]
EOF

#registry.k8s.io(pause、metrics-server)加速
cat > /etc/containerd/certs.d/registry.k8s.io/hosts.toml <<EOF
server = "https://registry-1.k8s.io"
[host."https://k8s.m.daocloud.io"]
  capabilities = ["pull","resolve"]
EOF

# 创建 systemd service(重点加入 LimitNOFILE,解决 limits 失效)
cat > /lib/systemd/system/containerd.service <<EOF
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
#解决ulimit不生效,systemd直接设置上限
LimitNOFILE=1000000
LimitNPROC=1000000
LimitMEMLOCK=infinity
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target
EOF


systemctl daemon-reload && systemctl enable --now containerd
systemctl status containerd
#journalctl -u containerd -f

# 验证进程真实 limit
pidof containerd
cat /proc/$(pidof containerd)/limits

安装 runc

containerd 本身只是容器管理守护进程,runc 是 OCI runtime,用来真正启动容器进程。

Ubuntu 系统安装 containerd 包, 不会自动附带 runc,必须手动安装。

RUNC_VERSION=1.5.0
ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)
wget https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.${ARCH}
install -m 755 runc.${ARCH} /usr/local/bin/runc
runc --version


# 检查 containerd 配置,确认 runc 路径
cp /etc/containerd/config.toml{,.bak}
sed -i 's@BinaryName =.*@BinaryName = "/usr/local/bin/runc"@g' /etc/containerd/config.toml

systemctl restart containerd
systemctl status containerd

安装 crictl(CRI 客户端,k8s 运维必备)

VERSION="v1.37.0"
ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)

wget https://github.com/kubernetes-sigs/cri-tools/releases/download/${VERSION}/crictl-${VERSION}-linux-$ARCH.tar.gz

sudo tar zxvf crictl-${VERSION}-linux-$ARCH.tar.gz -C /usr/local/bin
#rm -f crictl-${VERSION}-linux-$ARCH.tar.gz

# 配置crictl客户端连接
cat > /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF
# 验证
#crictl info

准备 k8s 的二进制程序文件(所有节点执行)

Master 节点的二进制文件

Master01下载kubernetes安装包

K8S_VERSION=v1.37.0
ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)

wget https://dl.k8s.io/${K8S_VERSION}/kubernetes-server-linux-${ARCH}.tar.gz

# 解压文件到 /usr/local/bin 下。
# kubectl,kubelet, kube-apiserver,
# kube-controller-manager,kube-scheduler,kube-proxy
tar tf kubernetes-server-linux-${ARCH}.tar.gz
tar -xvf kubernetes-server-linux-${ARCH}.tar.gz \
    --strip-components=3 \
    -C /usr/local/bin \
    kubernetes/server/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy}

# kubectl 自动补全
apt update && apt install -y bash-completion
echo 'alias k=kubectl' >>~/.bashrc
echo 'source <(kubectl completion bash)' >>~/.bashrc && source ~/.bashrc

将组件发送到其他节点

MasterNodes='master02.jasper.org master03.jasper.org'

for NODE in $MasterNodes; do
    echo $NODE;
    scp /usr/local/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy} $NODE:/usr/local/bin/;
    #scp /usr/local/bin/etcd* $NODE:/usr/local/bin/;
done

版本查看

kubelet --version
etcdctl version

Node 节点的二进制文件

将组件发送到其他节点

WorkNodes='node01.jasper.org node02.jasper.org'

for NODE in $WorkNodes; do
    scp /usr/local/bin/kube{let,-proxy} $NODE:/usr/local/bin/ ;
done

生成证书与密钥(master01 执行)

注释:k8s 集群各组件间使用 TLS 证书进行双向认证和加密通信。所有证书由自建的 CA 签发。

安装证书管理工具

方案1 CFSSL

CFSSL 是 CloudFare 开源的 PKI/TLS 工具,用于生成和签发证书。

因为 k8s 中证书数量特别多,使用 CFSSL 管理证书比 Openssl 要更加方便。

Master01下载生成证书工具

mkdir -p /data/k8s/ && cd /data/k8s

CFSSL_VERSION=1.6.5
ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)

wget -O /usr/local/bin/cfssl \
      https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssl_${CFSSL_VERSION}_linux_${ARCH}
wget -O /usr/local/bin/cfssljson \
     https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssljson_${CFSSL_VERSION}_linux_${ARCH}
wget -O /usr/local/bin/cfssl-certinfo \
     https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssl-certinfo_${CFSSL_VERSION}_linux_${ARCH}

chmod +x /usr/local/bin/cfssl*

生成 k8s 的证书

生成 k8s 的 CA 根证书

所有节点创建kubernetes相关目录

mkdir -p /etc/kubernetes/pki

Master01 生成 kubernetes 证书

mkdir -p /data/k8s
cd /data/k8s

# 1. 创建 CA 证书签名请求 CRS 配置文件. 100年过期
cat > ca-csr.json << EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "Kubernetes",
      "OU": "Kubernetes-manual"
    }
  ],
  "ca": {
    "expiry": "876000h"
  }
}
EOF

# 2. 生成 CA 证书和密钥
cfssl gencert -initca ca-csr.json | cfssljson -bare /etc/kubernetes/pki/ca

# 3. 创建 CA 配置文件,定义签名策略,CA 给别人签发证书时使用该规则,相当于 openssl.cnf。
## 颁发证书有效期 100 年
cat > ca-config.json << EOF
{
  "signing": {
    "default": {
      "expiry": "876000h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "876000h"
      }
    }
  }
}
EOF

ls /etc/kubernetes/pki/
ca-key.pem  ca.csr  ca.pem

注释:

  • ca.pem (公钥) 和 ca-key.pem (私钥) 是集群所有信任的根源。
  • ca-config.json 定义了如何使用 CA 为其他证书签名。

生成 k8s 各组件证书

使用上面创建的 CA,为以下组件签发证书:

  • kube-apiserver:API 服务器证书, hosts 字段必须包含所有可能访问 API Server 的 IP 和域名。
  • kube-controller-manager:控制器管理器访问 apiserver 端证书。
  • kube-scheduler:调度器访问 apiserver 的客户端证书。
  • kube-proxy:在每个节点上运行的网络代理组件访问 apiserver 的客户端证书。
  • admin:集群管理员(kubectl)使用的证书, O: system:massters 使其拥有最高权限。
生成 apiserver 证书
# kube-apiserver 证书中的 hosts(SAN) 必须包含所有组件访问 API Server 时
# 使用的IP、域名、VIP(如 Service IP、VIP、localhost、DNS 名称等),否则会出现 TLS 证书校验失败。

#1) 创建 CSR 申请文件
## 192.168.0. 是 k8s service 的网段,如果说需要更改 k8s service 网段,那就需要更改 192.168.0.1 。
## 如果不是高可用集群,10.103.236.236 为 Master01 的IP

cd /data/k8s
cat > apiserver-csr.json << EOF
{
  "CN": "kube-apiserver",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "Kubernetes",
      "OU": "Kubernetes-manual"
    }
  ],
  "hosts": [
    "192.168.0.1",
    "127.0.0.1",
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster",
    "kubernetes.default.svc.cluster.local",
    "kubeapi.jasper.org",
    "kubeapi",
    "masert01.jasper.org",
    "master01",
    "master02.jasper.org",
    "master02",
    "master03.jasper.org",
    "master03",
    "10.103.236.236",
    "10.103.236.201",
    "10.103.236.202",
    "10.103.236.203",
    "::1"
  ]

}
EOF

#2) 使用 CA 对 apiserver 颁发证书

cfssl gencert   -ca=/etc/kubernetes/pki/ca.pem \
      -ca-key=/etc/kubernetes/pki/ca-key.pem  \
      -config=ca-config.json  \
      -profile=kubernetes \
      apiserver-csr.json | cfssljson -bare /etc/kubernetes/pki/apiserver

# 生成文件: apiserver.pem(证书),apiserver-key.pem(私钥)
ls /etc/kubernetes/pki/
apiserver-key.pem  apiserver.csr  apiserver.pem  ca-key.pem  ca.csr  ca.pem
生成 controller-manager 证书
#1) 创建 controller-manager 证书 CSR 申请文件
## 它是客户端证书不需要有 hosts 字段
cd /data/k8s

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

#2) 使用 CA 对 controller-manager 颁发证书
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=ca-config.json \
   -profile=kubernetes \
   controller-manager-csr.json | cfssljson -bare /etc/kubernetes/pki/controller-manager

ls /etc/kubernetes/pki/
apiserver-key.pem  apiserver.pem  ca.csr  controller-manager-key.pem  controller-manager.pem
apiserver.csr      ca-key.pem     ca.pem  controller-manager.csr
生成 kube-scheduler 证书
#1) 创建 kube-scheduler 证书 csr 申请文件
cd /data/k8s

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

#2) 使用 CA 对 kube-scheduler 颁发证书
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=ca-config.json \
   -profile=kubernetes \
   scheduler-csr.json | cfssljson -bare /etc/kubernetes/pki/scheduler


ls /etc/kubernetes/pki/
apiserver-key.pem  apiserver.pem  ca.csr  controller-manager-key.pem  controller-manager.pem  scheduler.csr
apiserver.csr      ca-key.pem     ca.pem  controller-manager.csr      scheduler-key.pem       scheduler.pem
生成 kube-proxy 证书
#1) 创建 kube-proxy 证书 csr 申请文件
cd /data/k8s

cat > kube-proxy-csr.json << EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "system:kube-proxy",
      "OU": "Kubernetes-manual"
    }
  ]
}
EOF

#2) 使用 CA 对 kube-proxy 颁发证书
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=ca-config.json \
   -profile=kubernetes \
   kube-proxy-csr.json | cfssljson -bare /etc/kubernetes/pki/kube-proxy


ls /etc/kubernetes/pki/
apiserver-key.pem  ca-key.pem  controller-manager-key.pem  kube-proxy-key.pem  scheduler-key.pem
apiserver.csr      ca.csr      controller-manager.csr      kube-proxy.csr      scheduler.csr
apiserver.pem      ca.pem      controller-manager.pem      kube-proxy.pem      scheduler.pem
生成 admin 用户证书

关于system:masters

注意:"0":"system:masters" 不能修改

在 Kubernetes 中(无论 kubeadm 安装还是二进制安装), system:masters 都是默认存在的逻辑管理员组,不需要你手动创建。

在 Kubernetes中,默认存在:ClusterRoleBinding system:masters → cluster-admin,也就是说:system:maters ≈ root 用户,这是 Kubernetes 的"超级管理员",CN 决定你是谁,O 决定你属于哪个组,加入 system:masters 组,即 Kubernetees root

kubernetes最常见的内置组

主要有下面这些:其中最重要的是前 6

内置组 作用
system:masters 超级管理员组(最高权限)
system:nodes 所有Node(kubelet)所属组
system:authenticated 所有已认证用户
system:unauthenticated 未认证用户(匿名访问)
system:serviceaccounts 所有ServiceAccount总组
system:serviceaccounts:<ns> 某个namespace下所有SA
system:bootstrappers kubelet TLS Bootstrap使用
system:monitoring 某些监控组件使用
system:kube-controller-manager controller-manager使用
system:kube-scheduler scheduler使用
system:kube-proxy kube-proxy使用
#1) 创建 admin 用户证书 csr 申请文件
cd /data/k8s
cat > admin-csr.json << EOF
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "system:masters",
      "OU": "Kubernetes-manual"
    }
  ]
}
EOF

#2) 使用 CA 对 admin 用户颁发证书
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=ca-config.json \
   -profile=kubernetes \
   admin-csr.json | cfssljson -bare /etc/kubernetes/pki/admin


ls /etc/kubernetes/pki/
admin-key.pem  apiserver-key.pem  ca-key.pem  controller-manager-key.pem  kube-proxy-key.pem  scheduler-key.pem
admin.csr      apiserver.csr      ca.csr      controller-manager.csr      kube-proxy.csr      scheduler.csr
admin.pem      apiserver.pem      ca.pem      controller-manager.pem      kube-proxy.pem      scheduler.pem

生成 k8s 聚合层 front-prox 证书

Kubernetes 不只有一个 API Server。除了 kube-apiserveics-server,prometheus-adapter,istio 等额外的 API 各种 CRD 扩展 API,它们通过 APIService 注册到 kubectl api-resources

生成 API 聚合层证书,主要是为了添加额外的 API 服务器,这些服务器不是核心 Kubernetes API 的一部分,但可以作为 Kubernetes API的扩展。

例如: kubectl top node 实际上不是 kube-apiserver 返回的,而 metrics-server 返回的。只是 kube-apiserver 作为反向代理(Reverse Proxy)帮你转发请求。

流程如下:

  • kubectl –> kube-apiserver(使用 front-proxy-clent 证书) –> metrics-server 返回结果

注意:kube-apiserver 去访问 metrics-server 时,它要证明"kube-apiserver,不是别人伪造的",这个时候就使用 front-proxy-client.pem 来做客户端证书认证(mTLS)

证书链关系:

  • front-proxy-ca.pem –> 签发 front-proxy-ca.pem,不用普通 ca.pem,而是独立的front-proxy-ca

它是 kube-apiserver 用来"代理认证"聚合API的客户端证书

主要服务于: metrics-server,custom metrics, Promer,KEDA,APIService 扩展,各种 aggregated apiserver

例如:metrics.k8s.io, custom.metrics.k8s.io,这些都依赖它。

生成 front-proxy CA 证书

#1) 生成创建 front-proxy 聚合证书 CA 的请求(CSR)配置文件
cd /data/k8s
cat > front-proxy-ca-csr.json << EOF
{
  "CN": "kubernetes",
  "key": {
     "algo": "rsa",
     "size": 2048
  },
  "ca": {
      "expiry": "876000h"
  }
}
EOF

#2) 创建聚合证书的 CA 证书和私钥
cfssl gencert   -initca front-proxy-ca-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-ca 

# 查看生成的证书文件
ls /etc/kubernetes/pki/
admin-key.pem      apiserver.csr  ca.pem                      front-proxy-ca-key.pem  kube-proxy.csr     scheduler.pem
admin.csr          apiserver.pem  controller-manager-key.pem  front-proxy-ca.csr      kube-proxy.pem
admin.pem          ca-key.pem     controller-manager.csr      front-proxy-ca.pem      scheduler-key.pem
apiserver-key.pem  ca.csr         controller-manager.pem      kube-proxy-key.pem      scheduler.csr

#3) 创建 front-proxy 的 CA 配置文件,定义签名策略
cat > front-proxy-ca-config.json << EOF
{
  "signing": {
    "default": {
      "expiry": "876000h"
    },
    "profiles": {
      "front-proxy": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "876000h"
      }
    }
  }
}
EOF

生成聚合层客户端证书

#1) 创建聚合层客户端证书 csr 申请文件
cd /data/k8s
cat > front-proxy-client-csr.json << EOF
{
  "CN": "aggregator",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "system:masters"
    }
  ]
}
EOF

#2) 使用 front-proxy-ca 签发聚合层客户端证书
cfssl gencert \
      -ca=/etc/kubernetes/pki/front-proxy-ca.pem \
      -ca-key=/etc/kubernetes/pki/front-proxy-ca-key.pem \
      -config=front-proxy-ca-config.json \
      -profile=front-proxy   front-proxy-client-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-client

# 查看生成的证书文件
ls /etc/kubernetes/pki/
admin-key.pem      apiserver.csr  ca.pem                      front-proxy-ca-key.pem      front-proxy-client.csr  kube-proxy.pem
admin.csr          apiserver.pem  controller-manager-key.pem  front-proxy-ca.csr          front-proxy-client.pem  scheduler-key.pem
admin.pem          ca-key.pem     controller-manager.csr      front-proxy-ca.pem          kube-proxy-key.pem      scheduler.csr
apiserver-key.pem  ca.csr         controller-manager.pem      front-proxy-client-key.pem  kube-proxy.csr          scheduler.pem

生成 etcd 相关证书

生成 etcd 的 CA 证书

Master01节点生成etcd证书

# 确认创建 etcd 证书使用目录
mkdir -p /etc/kubernetes/pki/etcd/

#1) 生成创建 etcd CA 的请求(CSR)配置文件
cd /data/k8s
cat > etcd-ca-csr.json << EOF
{
  "CN": "etcd",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "etcd",
      "OU": "Etcd Security"
    }
  ],
  "ca": {
    "expiry": "876000h"
  }
}
EOF

#2) 创建 CA 证书和私钥
cfssl gencert  -initca etcd-ca-csr.json | cfssljson -bare /etc/kubernetes/pki/etcd/etcd-ca

#3) 生成 CA 证书的 config 文件,CA 给别人签发证书时使用该规则
cat > etcd-ca-config.json << EOF
{
  "signing": {
    "default": {
      "expiry": "876000h"
    },
    "profiles": {
      "etcd": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "876000h"
      }
    }
  }
}
EOF

生成 etcd 服务端证书

#1) 生成 etcd 证书的 csr 配置
# 要指定 etcd 节点的 ip 和主机名
cd /data/k8s
cat > etcd-csr.json << EOF
{
  "CN": "etcd",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "etcd",
      "OU": "Etcd Security"
    }
  ],
  "hosts": [
    "127.0.0.1",
    "master01",
    "master02",
    "master03",
    "master01.jasper.org",
    "master02.jasper.org",
    "master03.jasper.org",
    "10.103.236.201",
    "10.103.236.202",
    "10.103.236.203"
  ]
}
EOF


#2) 使用 etcd 的 CA 证书签发 etcd 证书
cfssl gencert \
   -ca=/etc/kubernetes/pki/etcd/etcd-ca.pem \
   -ca-key=/etc/kubernetes/pki/etcd/etcd-ca-key.pem \
   -config=etcd-ca-config.json \
   -profile=etcd \
   etcd-csr.json | cfssljson -bare /etc/kubernetes/pki/etcd/etcd


ls /etc/kubernetes/pki/etcd/
etcd-ca-key.pem  etcd-ca.csr  etcd-ca.pem  etcd-key.pem  etcd.csr  etcd.pem

创建 ServiceAccount 密钥对

此密钥还是 TLS 通信证书。用于 ServiceAccount(Pod 内的身份)签发和验证 JWT Token,是 Pod 访问 API Server 的一种方式。

例如: Pod 内自动挂载的 Token,就是它签发的。

openssl genrsa -out /etc/kubernetes/pki/sa.key 2048
openssl rsa -in /etc/kubernetes/pki/sa.key -pubout -out /etc/kubernetes/pki/sa.pub

# 查看最后生成所有文件如下

tree /etc/kubernetes/pki
/etc/kubernetes/pki
├── admin-key.pem
├── admin.csr
├── admin.pem
├── apiserver-key.pem
├── apiserver.csr
├── apiserver.pem
├── ca-key.pem
├── ca.csr
├── ca.pem
├── controller-manager-key.pem
├── controller-manager.csr
├── controller-manager.pem
├── etcd
│   ├── etcd-ca-key.pem
│   ├── etcd-ca.csr
│   ├── etcd-ca.pem
│   ├── etcd-key.pem
│   ├── etcd.csr
│   └── etcd.pem
├── front-proxy-ca-key.pem
├── front-proxy-ca.csr
├── front-proxy-ca.pem
├── front-proxy-client-key.pem
├── front-proxy-client.csr
├── front-proxy-client.pem
├── kube-proxy-key.pem
├── kube-proxy.csr
├── kube-proxy.pem
├── sa.key
├── sa.pub
├── scheduler-key.pem
├── scheduler.csr
└── scheduler.pem
2 directories, 32 files

生成 kubeconfig 文件(master01 执行)

Kubeconfig 文件包含了访问集群所需的全部信息:

集群地址、CA 证书、用户证书和私钥。它为 kubectl 和各组件(scheduler,controller-manager)提供认证凭据。

生成 controller-manager.kubeconfig, scheduler.kubeconfig, admin.kubeconfig (给 kubectl 用), kube-proxy.kubeconfig, bootstrap-kubelet.kubeconfig (用于 TLS 引导)等配置文

为 controller-manager 生成 kubeconfig

# 注释:此文件将用于 kube-controller-manager 组件连接 API Server 时的身份认证。

# 注意,如果不是高可用集群,10.103.236.236:8443 改为
# master01 的地址,8443 改为 apiserver 的端口,默认是 6443


#创建配置文件,指定使用的 CA 证书和 api 地址
# set-cluster:设置一个集群项
kubectl config set-cluster kubernetes \
     --certificate-authority=/etc/kubernetes/pki/ca.pem \
     --embed-certs=true \
     --server=https://kubeapi.jasper.org:8443 \
     --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig
# set-credentials 设置身份验证凭据
kubectl config set-credentials system:kube-controller-manager \
     --client-certificate=/etc/kubernetes/pki/controller-manager.pem \
     --client-key=/etc/kubernetes/pki/controller-manager-key.pem \
     --embed-certs=true \
     --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig
# 为配置文件追加一个上下文,上下文关联了特定的集群和用户
kubectl config set-context system:kube-controller-manager@kubernetes \
    --cluster=kubernetes \
    --user=system:kube-controller-manager \
    --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig
# 使用某个环境当做默认环境
kubectl config use-context system:kube-controller-manager@kubernetes \
     --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig


ls /etc/kubernetes/
controller-manager.kubeconfig  pki

# 查看 kubeconfig 内容
root@master01:/data/k8s# kubectl config view --kubeconfig /etc/kubernetes/controller-manager.kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://kubeapi.jasper.org:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: system:kube-controller-manager
  name: system:kube-controller-manager@kubernetes
current-context: system:kube-controller-manager@kubernetes
kind: Config
users:
- name: system:kube-controller-manager
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED

为 scheduler 生成 kubeconfig

# 注意,如果不是高可用集群,10.103.236.236:8443 改为
# master01 的地址,8443 改为 apiserver 的端口,默认是 6443

#创建配置文件,指定使用的 CA 证书和 api 地址
kubectl config set-cluster kubernetes \
     --certificate-authority=/etc/kubernetes/pki/ca.pem \
     --embed-certs=true \
     --server=https://kubeapi.jasper.org:8443 \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig
# set-credentials 设置身份验证凭据
kubectl config set-credentials system:kube-scheduler \
     --client-certificate=/etc/kubernetes/pki/scheduler.pem \
     --client-key=/etc/kubernetes/pki/scheduler-key.pem \
     --embed-certs=true \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig
# 为配置文件追加一个上下文,上下文关联了特定的集群和用户
kubectl config set-context system:kube-scheduler@kubernetes \
     --cluster=kubernetes \
     --user=system:kube-scheduler \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig
# 使用某个环境当做默认环境
kubectl config use-context system:kube-scheduler@kubernetes \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig


ls /etc/kubernetes/
controller-manager.kubeconfig  pki  scheduler.kubeconfig

# 查看 kubeconfig 内容
root@master01:/data/k8s# kubectl config view --kubeconfig /etc/kubernetes/scheduler.kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://kubeapi.jasper.org:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: system:kube-scheduler
  name: system:kube-scheduler@kubernetes
current-context: system:kube-scheduler@kubernetes
kind: Config
users:
- name: system:kube-scheduler
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED

为 kube-proxy 生成 kubeconfig

# 注意,如果不是高可用集群,10.103.236.236:8443 改为
# master01 的地址,8443 改为 apiserver 的端口,默认是 6443

#创建配置文件,指定使用的 CA 证书和 api 地址
kubectl config set-cluster kubernetes \
        --certificate-authority=/etc/kubernetes/pki/ca.pem \
        --embed-certs=true \
        --server=https://kubeapi.jasper.org:8443 \
        --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig
# set-credentials 设置身份验证凭据
kubectl config set-credentials kube-proxy \
        --client-certificate=/etc/kubernetes/pki/kube-proxy.pem  \
        --client-key=/etc/kubernetes/pki/kube-proxy-key.pem \
        --embed-certs=true \
        --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig
# 为配置文件追加一个上下文,上下文关联了特定的集群和用户
kubectl config set-context kube-proxy@kubernetes \
        --cluster=kubernetes \
        --user=kube-proxy \
        --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig
# 使用某个环境当做默认环境
kubectl config use-context kube-proxy@kubernetes \
        --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig


ls /etc/kubernetes/
controller-manager.kubeconfig  kube-proxy.kubeconfig  pki  scheduler.kubeconfig

# 查看 kubeconfig 内容
root@master01:/data/k8s# kubectl config view --kubeconfig /etc/kubernetes/kube-proxy.kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://kubeapi.jasper.org:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kube-proxy
  name: kube-proxy@kubernetes
current-context: kube-proxy@kubernetes
kind: Config
users:
- name: kube-proxy
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED

为 bootstrap-kubelet 生成 kubeconfig

kubelet 启动时需要访问 apiserver,例如:注册 Node 加入 k8s 集群、上报 Node 状态、获取 Pod、获取 Secret、获取 ConfigMap 等

但 kube-apiserver 开启了 HTTPS + 双向 TLS 认证,kubelet 必须合法证书,才能连接 apiserver,但是无法连接 apiserver 无法申 请证书,即鸡生蛋、蛋生鸡问题,于是 Kubernetes 设计了 Bootsttrap Token 解决。

bootstrap-kubelet.kubeconfig 的作用是让新加入集群的 kubelet 能“第一次”安全连接 kube-apiserver 也叫 TLS Bootstrap 证书自动签发机制

这个 kubeconfig 不是正式证书。而是临时"入场券",它使用: --token=a2e6e0.0123456789abcdef 去访问 apiserver:"我是新节点,请给我签发正式证书",然后 apiserver → CSR → 自动批准的正式 kubelet 证书,之后token就基本不用了,改用 kubeleet-client.crt和 kubelet-client.key 长期通信。

Bootstrap Token 本身只是 kubelet 首次访问 apiserver 的临时它通过 kube-apiserver 的 --enable-bootstrap-token-auth=true 被识别为 system:bootstrappers 用户组,再通过 ClusterRoleBinding 绑定 system:node-bootstrapper 等权限,节点创建 CSR并申请正式客户端证书,最终完成 Node 加入集群。

创建 bootstrap-kubelet.kubeconfig 配置

# 注意,如果不是高可用集群,10.103.236.236:8443改为master01的地址,8443改为apiserver的端口,默认是6443

BOOTSTRAP_TOKEN=$(head -c 6 /dev/urandom | md5sum | head -c 6).$(head -c 16 /dev/urandom | md5sum | head -c 16)

# 或者
#TOKEN_PUB=$(openssl rand -hex 3)
#TOKEN_SECRET=$(openssl rand -hex 8)
#BOOTSTRAP_TOKEN="${TOKEN_PUB}.${TOKEN_SECRET}"

# 保存此 TOKEN 后面会用到
echo $BOOTSTRAP_TOKEN
5ba17d.e9d5d01308883dab


BOOTSTRAP_TOKEN=5ba17d.e9d5d01308883dab

kubectl config set-cluster kubernetes \
        --certificate-authority=/etc/kubernetes/pki/ca.pem \
        --embed-certs=true \
        --server=https://kubeapi.jasper.org:8443 \
        --kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig

kubectl config set-credentials tls-bootstrap-token-user \
        --token=${BOOTSTRAP_TOKEN} \
        --kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig

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

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


ls /etc/kubernetes/
bootstrap-kubelet.kubeconfig  controller-manager.kubeconfig  kube-proxy.kubeconfig  pki  scheduler.kubeconfig

# 查看 kubeconfig 内容
root@master01:/data/k8s# kubectl config view --kubeconfig /etc/kubernetes/bootstrap-kubelet.kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://kubeapi.jasper.org:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: tls-bootstrap-token-user
  name: tls-bootstrap-token-user@kubernetes
current-context: tls-bootstrap-token-user@kubernetes
kind: Config
users:
- name: tls-bootstrap-token-user
  user:
    token: REDACTED

注意:如果要修改 bootstrap.secret.yaml 的 token-id 和 token-secret,需要保证字符串一致的,并且位数是一样的。还要保证上个命令的黄色字体: BOOTSTRAP_TOKEN 与你修改的字符串要一致

为 admin 生成 kubeconfig

# 注意,如果不是高可用集群,10.103.236.236:8443 改为
# master01 的地址,8443 改为 apiserver 的端口,默认是 6443

#创建配置文件,指定使用的 CA 证书和 api 地址
kubectl config set-cluster kubernetes \
        --certificate-authority=/etc/kubernetes/pki/ca.pem \
        --embed-certs=true \
        --server=https://kubeapi.jasper.org:8443 \
        --kubeconfig=/etc/kubernetes/admin.kubeconfig
# set-credentials 设置身份验证凭据
kubectl config set-credentials kubernetes-admin \
        --client-certificate=/etc/kubernetes/pki/admin.pem  \
        --client-key=/etc/kubernetes/pki/admin-key.pem \
        --embed-certs=true \
        --kubeconfig=/etc/kubernetes/admin.kubeconfig
# 为配置文件追加一个上下文,上下文关联了特定的集群和用户
kubectl config set-context kubernetes-admin@kubernetes \
        --cluster=kubernetes \
        --user=kubernetes-admin \
        --kubeconfig=/etc/kubernetes/admin.kubeconfig
# 使用某个环境当做默认环境
kubectl config use-context kubernetes-admin@kubernetes \
        --kubeconfig=/etc/kubernetes/admin.kubeconfig


ls /etc/kubernetes/
admin.kubeconfig  bootstrap-kubelet.kubeconfig  controller-manager.kubeconfig  kube-proxy.kubeconfig  pki  scheduler.kubeconfig

# 查看 kubeconfig 内容
root@master01:/data/k8s# kubectl config view --kubeconfig /etc/kubernetes/admin.kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://kubeapi.jasper.org:8443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
users:
- name: kubernetes-admin
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED

配置 kubectl 授权

mkdir -p /root/.kube
cp /etc/kubernetes/admin.kubeconfig  /root/.kube/config
# 37 个文件
root@master01:/data/k8s# tree /etc/kubernetes/
/etc/kubernetes/
├── admin.kubeconfig
├── bootstrap-kubelet.kubeconfig
├── controller-manager.kubeconfig
├── kube-proxy.kubeconfig
├── pki
│   ├── admin-key.pem
│   ├── admin.csr
│   ├── admin.pem
│   ├── apiserver-key.pem
│   ├── apiserver.csr
│   ├── apiserver.pem
│   ├── ca-key.pem
│   ├── ca.csr
│   ├── ca.pem
│   ├── controller-manager-key.pem
│   ├── controller-manager.csr
│   ├── controller-manager.pem
│   ├── etcd
│   │   ├── etcd-ca-key.pem
│   │   ├── etcd-ca.csr
│   │   ├── etcd-ca.pem
│   │   ├── etcd-key.pem
│   │   ├── etcd.csr
│   │   └── etcd.pem
│   ├── front-proxy-ca-key.pem
│   ├── front-proxy-ca.csr
│   ├── front-proxy-ca.pem
│   ├── front-proxy-client-key.pem
│   ├── front-proxy-client.csr
│   ├── front-proxy-client.pem
│   ├── kube-proxy-key.pem
│   ├── kube-proxy.csr
│   ├── kube-proxy.pem
│   ├── sa.key
│   ├── sa.pub
│   ├── scheduler-key.pem
│   ├── scheduler.csr
│   └── scheduler.pem
└── scheduler.kubeconfig

3 directories, 37 files

同步相关配置文件到节点

配置文件同步到其他 MASTER 节点

FILES=(
    "/etc/kubernetes/admin.kubeconfig"
    "/etc/kubernetes/controller-manager.kubeconfig"
    "/etc/kubernetes/scheduler.kubeconfig"
)

for NODE in master02.jasper.org master03.jasper.org; do
    echo ====$NODE START====
    for FILE in $(ls /etc/kubernetes/pki | grep -v etcd); do
        ssh  root@${NODE} "mkdir -p /etc/kubernetes/pki"
        scp /etc/kubernetes/pki/${FILE} $NODE:/etc/kubernetes/pki/${FILE};
    done; 

    for FILE in "${FILES[@]}"; do
        scp "$FILE" "root@${NODE}:$FILE"
    done;
    echo ====$NODE END====
done
将 etcd 证书复制到其他节点
MasterNodes='master02.jasper.org master03.jasper.org'
WorkNodes='node01.jasper.org node02.jasper.org'

FILES=(
    "/etc/kubernetes/pki/etcd/etcd-ca.pem"
    "/etc/kubernetes/pki/etcd/etcd-ca-key.pem"
    "/etc/kubernetes/pki/etcd/etcd.pem"
    "/etc/kubernetes/pki/etcd/etcd-key.pem"
)
/etc/kubernetes/pki/
for NODE in $MasterNodes; do
     #ssh $NODE "mkdir -p /etc/etcd/ssl"
    for FILE in "${FILES[@]}"; do
        ssh  root@${NODE} "mkdir -p /etc/kubernetes/pki/etcd"
        scp "$FILE" "root@${NODE}:$FILE"
     done
 done
# Master 节点确认相关配置文件创建成功
root@master02:~# tree /etc/kubernetes/
/etc/kubernetes/
├── admin.kubeconfig
├── controller-manager.kubeconfig
├── pki
│   ├── admin-key.pem
│   ├── admin.csr
│   ├── admin.pem
│   ├── apiserver-key.pem
│   ├── apiserver.csr
│   ├── apiserver.pem
│   ├── ca-key.pem
│   ├── ca.csr
│   ├── ca.pem
│   ├── controller-manager-key.pem
│   ├── controller-manager.csr
│   ├── controller-manager.pem
│   ├── etcd
│   │   ├── etcd-ca-key.pem
│   │   ├── etcd-ca.pem
│   │   ├── etcd-key.pem
│   │   └── etcd.pem
│   ├── front-proxy-ca-key.pem
│   ├── front-proxy-ca.csr
│   ├── front-proxy-ca.pem
│   ├── front-proxy-client-key.pem
│   ├── front-proxy-client.csr
│   ├── front-proxy-client.pem
│   ├── kube-proxy-key.pem
│   ├── kube-proxy.csr
│   ├── kube-proxy.pem
│   ├── sa.key
│   ├── sa.pub
│   ├── scheduler-key.pem
│   ├── scheduler.csr
│   └── scheduler.pem
└── scheduler.kubeconfig

3 directories, 33 files

配置文件同步到其他 NODE 节点

将 CA 证书、front-proxy CA 证书以及 bootstrap-kubelet、kube-proxy 的 kubeconfig 文件复制到 NODE 节点

NODE_IPS="node01.jasper.org node02.jasper.org"

NODES=($NODE_IPS)
FILES=(
    "/etc/kubernetes/pki/ca.pem"
    "/etc/kubernetes/bootstrap-kubelet.kubeconfig"
    "/etc/kubernetes/kube-proxy.kubeconfig"
)

for NODE in "${NODES[@]}"; do
    echo ====$NODE START====
    for FILE in "${FILES[@]}"; do
        ssh  root@${NODE} "mkdir -p /etc/kubernetes/pki"
        scp "$FILE" "root@${NODE}:$FILE"
    done
    echo ====$NODE END====
done
# Node 节点确认相关配置文件创建成功
root@node02:~# tree /etc/kubernetes/
/etc/kubernetes/
├── bootstrap-kubelet.kubeconfig
├── kube-proxy.kubeconfig
└── pki
    └── ca.pem

2 directories, 3 files

部署 etcd(Master 节点)

注释:etcd 是 kubernetes 的后端存储,所有集群数据(Pod、Service、配置等)都保存在这里。

安装 etcd 二进制文件

在master01执行下载etcd安装包

ETCD_VERSION=v3.7.1
ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)

cd /data/k8s
wget https://github.com/etcd-io/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-${ARCH}.tar.gz

tar tf etcd-${ETCD_VERSION}-linux-${ARCH}.tar.gz
tar xvf etcd-${ETCD_VERSION}-linux-${ARCH}.tar.gz \
    --strip-components=1 \
    -C /usr/local/bin \
    etcd-${ETCD_VERSION}-linux-${ARCH}/etcd{,ctl}


etcdctl version
etcdctl version: 3.7.1
API version: 3.7

将组件发送到其他节点

MasterNodes='master02.jasper.org master03.jasper.org'

for NODE in $MasterNodes; do
    echo $NODE;
    scp /usr/local/bin/etcd* $NODE:/usr/local/bin/;
done

创建 etcd 配置文件

etcd配置大致相同,注意修改每个 Master 节点的 etcd 配置的主机名和 IP 地址

master01

master01

mkdir -p /etc/etcd

cat > /etc/etcd/etcd.config.yml << EOF
name: 'master01.jasper.org'   # etcd 节点唯一标识
data-dir: /var/lib/etcd       # etcd 数据存储
wal-dir: /var/lib/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://10.103.236.201:2380'   # 本节点用于集群节点间通信连接地址和端口: etcd <--> etcd(RAft 同步)
listen-client-urls: 'https://10.103.236.201:2379,http://127.0.0.1:2379' # 本节点用于客户端通信连接地址:客户端(kube-apiserver / etcdctl) --> etcd
max-snapshots: 3
max-wals: 5
cors:
initial-advertise-peer-urls: 'https://10.103.236.201:2380' # 告诉其他 etcd 节点:用这个地址和我通信
advertise-client-urls: 'https://10.103.236.201:2379'     # 告诉客户端:你应该用这个地址来访问来
discovery:
discovery-fallback: 'proxy'
discovery-proxy:
discovery-srv:
# 定义整个 etcd 集群成员列表
initial-cluster: 'master01.jasper.org=https://10.103.236.201:2380,master02.jasper.org=https://10.103.236.202:2380,master03.jasper.org=https://10.103.236.203:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
proxy-failure-wait: 5000
proxy-refresh-interval: 30000
proxy-dial-timeout: 1000
proxy-write-timeout: 5000
proxy-read-timeout: 0
client-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
peer-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  peer-client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
debug: false
log-package-levels:
log-outputs: [default]
force-new-cluster: false
EOF

master02

mkdir -p /etc/etcd

cat > /etc/etcd/etcd.config.yml << EOF
name: 'master02.jasper.org'
data-dir: /var/lib/etcd
wal-dir: /var/lib/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://10.103.236.202:2380'
listen-client-urls: 'https://10.103.236.202:2379,http://127.0.0.1:2379'
max-snapshots: 3
max-wals: 5
cors:
initial-advertise-peer-urls: 'https://10.103.236.202:2380'
advertise-client-urls: 'https://10.103.236.202:2379'
discovery:
discovery-fallback: 'proxy'
discovery-proxy:
discovery-srv:
initial-cluster: 'master01.jasper.org=https://10.103.236.201:2380,master02.jasper.org=https://10.103.236.202:2380,master03.jasper.org=https://10.103.236.203:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
proxy-failure-wait: 5000
proxy-refresh-interval: 30000
proxy-dial-timeout: 1000
proxy-write-timeout: 5000
proxy-read-timeout: 0
client-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
peer-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  peer-client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
debug: false
log-package-levels:
log-outputs: [default]
force-new-cluster: false
EOF

master03

mkdir -p /etc/etcd

cat > /etc/etcd/etcd.config.yml << EOF
name: 'master03.jasper.org'
data-dir: /var/lib/etcd
wal-dir: /var/lib/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://10.103.236.203:2380'
listen-client-urls: 'https://10.103.236.203:2379,http://127.0.0.1:2379'
max-snapshots: 3
max-wals: 5
cors:
initial-advertise-peer-urls: 'https://10.103.236.203:2380'
advertise-client-urls: 'https://10.103.236.203:2379'
discovery:
discovery-fallback: 'proxy'
discovery-proxy:
discovery-srv:
initial-cluster: 'master01.jasper.org=https://10.103.236.201:2380,master02.jasper.org=https://10.103.236.202:2380,master03.jasper.org=https://10.103.236.203:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
proxy-failure-wait: 5000
proxy-refresh-interval: 30000
proxy-dial-timeout: 1000
proxy-write-timeout: 5000
proxy-read-timeout: 0
client-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
peer-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  peer-client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
debug: false
log-package-levels:
log-outputs: [default]
force-new-cluster: false
EOF

创建 systemd 服务

所有 Master 节点创建 etcd service 并启动

cat > /lib/systemd/system/etcd.service << EOF
[Unit]
Description=Etcd Service
Documentation=https://coreos.com/etcd/docs/latest/
After=network.target

[Service]
Type=notify
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.config.yml
Restart=on-failure
RestartSec=10
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
Alias=etcd3.service
EOF

# 启动
systemctl daemon-reload
systemctl enable --now etcd.service && systemctl status etcd.service

查看etcd状态

etcdctl --endpoints="10.103.236.203:2379,10.103.236.202:2379,10.103.236.201:2379" \
        --cacert=/etc/kubernetes/pki/etcd/etcd-ca.pem \
        --cert=/etc/kubernetes/pki/etcd/etcd.pem \
        --key=/etc/kubernetes/pki/etcd/etcd-key.pem \
        --write-out=table \
        endpoint status

etcdctl --endpoints="10.103.236.203:2379,10.103.236.202:2379,10.103.236.201:2379" \
        --cacert=/etc/kubernetes/pki/etcd/etcd-ca.pem \
        --cert=/etc/kubernetes/pki/etcd/etcd.pem \
        --key=/etc/kubernetes/pki/etcd/etcd-key.pem \
        --write-out=table \
        endpoint health


┌─────────────────────┬────────┬─────────────┬───────┐
│      ENDPOINT       │ HEALTH │    TOOK     │ ERROR │
├─────────────────────┼────────┼─────────────┼───────┤
│ 10.103.236.201:2379 │   true │  13.39797ms │       │
│ 10.103.236.202:2379 │   true │ 13.540304ms │       │
│ 10.103.236.203:2379 │   true │ 17.219194ms │       │
└─────────────────────┴────────┴─────────────┴───────┘

高可用实现

高可用配置(注意:如果不是高可用集群,haproxy 和 keepalived 无需安装)

如果在云上安装也无需执行此章节的步骤,可以直接使用云上的 lb,比如阿里云 slb,腾讯云 elb 等

公有云要用公有云自带的负载均衡,比如阿里云的 SLB,腾讯云的 ELB,用来替代 haproxy 和 keepalived,因为公有云大部分都是不支持 keepalived的,另外如果用阿里云的话,kubectl 控制端不能放在 master 节点,推荐使用腾讯云,因为阿里云的 slb 有回环的问题,也就是 slb 代理的服务器不能反向访问 SLB,但是腾讯云修复了这个问题。

Slb -> haproxy -> apiserver

所有 Master 节点安装 keepalived 和 haproxy

apt install keepalived haproxy -y

所有 Master 配置 HAProxy,配置一样

vim /etc/haproxy/haproxy.cfg

cp /etc/haproxy/haproxy.cfg{,.bak}

cat > /etc/haproxy/haproxy.cfg << EOF
global
  maxconn  2000
  ulimit-n  16384
  log  127.0.0.1 local0 err
  stats timeout 30s

defaults
  log global
  mode  http
  option  httplog
  timeout connect 5000
  timeout client  50000
  timeout server  50000
  timeout http-request 15s
  timeout http-keep-alive 15s

frontend k8s-master
  bind 0.0.0.0:8443
  bind 127.0.0.1:8443
  mode tcp
  option tcplog
  tcp-request inspect-delay 5s
  default_backend k8s-master

backend k8s-master
  mode tcp
  option tcplog
  option tcp-check
  balance roundrobin
  default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
  server master01.jasper.org    10.103.236.201:6443  check
  server master02.jasper.org    10.103.236.202:6443  check
  server master03.jasper.org    10.103.236.203:6443  check
EOF

master01 keepalived

所有 Master 节点配置 KeepAlived,配置不一样,注意区分 :

  • 网卡名称 interface
  • mcast_src_ip 地址
  • virtual_ipaddress 虚拟 vip 地址,与主机在同一网段内
  • 注意每个节点的IP和网卡(interface参数)
cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
    router_id LVS_DEVEL
}
vrrp_script chk_apiserver {
    script "/etc/keepalived/check_apiserver.sh"
    interval 5 
    weight -5
    fall 2
    rise 1
}
vrrp_instance VI_1 {
    state MASTER
    interface enp2s0
    mcast_src_ip 10.103.236.201
    virtual_router_id 51
    priority 101
    nopreempt
    advert_int 2
    authentication {
        auth_type PASS
        auth_pass K8SHA_KA_AUTH
    }
    virtual_ipaddress {
        10.103.236.236
    }
    track_script {
      chk_apiserver 
} }
EOF

master02 keepalived

cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
    router_id LVS_DEVEL
}
vrrp_script chk_apiserver {
    script "/etc/keepalived/check_apiserver.sh"
    interval 5 
    weight -5
    fall 2
    rise 1
 
}
vrrp_instance VI_1 {
    state BACKUP
    interface enp2s0
    mcast_src_ip 10.103.236.202
    virtual_router_id 51
    priority 100
    nopreempt
    advert_int 2
    authentication {
        auth_type PASS
        auth_pass K8SHA_KA_AUTH
    }
    virtual_ipaddress {
        10.103.236.236
    }
    track_script {
      chk_apiserver 
} }
EOF

master03 keepalived

cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
    router_id LVS_DEVEL
}
vrrp_script chk_apiserver {
    script "/etc/keepalived/check_apiserver.sh"
    interval 5
    weight -5
    fall 2  
    rise 1
}
vrrp_instance VI_1 {
    state BACKUP
    interface enp2s0
    mcast_src_ip 10.103.236.203
    virtual_router_id 51
    priority 100
    nopreempt
    advert_int 2
    authentication {
        auth_type PASS
        auth_pass K8SHA_KA_AUTH
    }
    virtual_ipaddress {
        10.103.236.236
    }
    track_script {
      chk_apiserver 
} }
EOF

健康检查配置

所有master节点

cat > /etc/keepalived/check_apiserver.sh <<\EOF
#!/bin/bash

err=0
for k in $(seq 1 3)
do
    check_code=$(pgrep haproxy)
    if [[ $check_code == "" ]]; then
        err=$(expr $err + 1)
        sleep 1
        continue
    else
        err=0
        break
    fi
done

if [[ $err != "0" ]]; then
    echo "systemctl stop keepalived"
    /usr/bin/systemctl stop keepalived
    exit 1
else
    exit 0
fi
EOF

chmod +x /etc/keepalived/check_apiserver.sh

所有 master 节点启动 haproxy 和 keepalived

systemctl daemon-reload
systemctl enable --now haproxy
systemctl enable --now keepalived
systemctl restart haproxy keepalived

VIP测试

[root@master01.jasper.org pki]# ping 10.103.236.236
PING 10.103.236.236 (10.103.236.236) 56(84) bytes of data.
64 bytes from 10.103.236.236: icmp_seq=1 ttl=64 time=1.39 ms
64 bytes from 10.103.236.236: icmp_seq=2 ttl=64 time=2.46 ms
64 bytes from 10.103.236.236: icmp_seq=3 ttl=64 time=1.68 ms
64 bytes from 10.103.236.236: icmp_seq=4 ttl=64 time=1.08 ms

重要:如果安装了 keepalived 和 haproxy,需要测试 keepalived 是否是正常的

nc -zv 10.103.236.236 8443
Connection to 10.103.236.236 8443 port [tcp/*] succeeded!

如果 ping 不通且 telnet/nc 没有出现 ],则认为 VIP 不可以,不可在继续往下执行,需要排查 keepalived 的问题,比如防火墙和 selinux,haproxy 和 keepalived 的状态,监听端口等

所有节点查看防火墙状态必须为 disable 和 inactive:systemctl status firewalld

所有节点查看 selinux 状态,必须为 disable:getenforce

master节点查看 haproxy 和 keepalived 状态:systemctl status keepalived haproxy

master节点查看监听端口:netstat -lntp

部署 k8s 控制平面组件(Master 节点)

注释:控制平面组件包括 kube-apiserver, kube-controller-manager, kube-scheduler。它们将以 systemd 服务运行。

创建必要的目录

所有节点创建相关目录

mkdir -p /etc/kubernetes/manifests/ \
      /etc/systemd/system/kubelet.service.d \
      /var/lib/kubelet \
      /var/log/kubernetes

配置并启动 kube-apiserver

所有 Master 节点创建 kube-apiserver service,注意,如果不是高可用集群,10.103.236.236 改为 master01 的地址

注意本文档使用的k8s service网段为192.168.0.0/16,该网段不能和宿主机的网段、Pod网段的重复,请按需修改

LOCAL_IP=`hostname -I|awk '{print $1}'`
ETCD1_IP=10.103.236.201
ETCD2_IP=10.103.236.202
ETCD3_IP=10.103.236.203


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

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
      --v=2  \\
      --allow-privileged=true  \\
      --bind-address=0.0.0.0  \\
      --secure-port=6443  \\
      --advertise-address=${LOCAL_IP} \\
      --service-cluster-ip-range=192.168.0.0/16  \\
      --service-node-port-range=30000-32767  \\
      --etcd-servers=https://${ETCD1_IP}:2379,https://${ETCD2_IP}:2379,https://${ETCD3_IP}:2379 \\
      --etcd-cafile=/etc/kubernetes/pki/etcd/etcd-ca.pem  \\
      --etcd-certfile=/etc/kubernetes/pki/etcd/etcd.pem  \\
      --etcd-keyfile=/etc/kubernetes/pki/etcd/etcd-key.pem  \\
      --client-ca-file=/etc/kubernetes/pki/ca.pem  \\
      --tls-cert-file=/etc/kubernetes/pki/apiserver.pem  \\
      --tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem  \\
      --kubelet-client-certificate=/etc/kubernetes/pki/apiserver.pem  \\
      --kubelet-client-key=/etc/kubernetes/pki/apiserver-key.pem  \\
      --service-account-key-file=/etc/kubernetes/pki/sa.pub  \\
      --service-account-signing-key-file=/etc/kubernetes/pki/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  \\
      --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem  \\
      --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.pem  \\
      --proxy-client-key-file=/etc/kubernetes/pki/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 \\
      --enable-aggregator-routing=true
      # --token-auth-file=/etc/kubernetes/token.csv

Restart=on-failure
RestartSec=10s
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF


# 启动 apiserver
systemctl daemon-reload && systemctl enable --now kube-apiserver.service && systemctl status kube-apiserver

# 查看组件状态
root@master01:/data/k8s# kubectl  get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS      MESSAGE                                                                                        ERROR
scheduler            Unhealthy   Get "https://127.0.0.1:10259/healthz": dial tcp 127.0.0.1:10259: connect: connection refused
controller-manager   Unhealthy   Get "https://127.0.0.1:10257/healthz": dial tcp 127.0.0.1:10257: connect: connection refused
etcd-0               Healthy     ok

配置并启动 kube-controller-manager

这两个服务的配置相对简单,主要指定其 kubeconfig 文件以连接 API Server 进行认证。

  • kube-controller-manager.service: 负责节点管理、Po复制、端点管理等控制循环。
  • kube-scheduler.service: 负责为新创建的 Pod 选择运行节点。

启动后,通过 kubectl get cs 检查组件健康状态。

所有 Master 节点配置 kube-controller-manager service(所有master节点配置一样)

注意本文档使用的k8s Pod网段为 172.16.0.0/12,该网段不能和宿主机的网段、k8s Service网段的重复,请按需修改

# 创建 kube-controller-manager service
cat > /usr/lib/systemd/system/kube-controller-manager.service << EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
      --v=2 \\
      --bind-address=0.0.0.0 \\
      --root-ca-file=/etc/kubernetes/pki/ca.pem \\
      --cluster-signing-cert-file=/etc/kubernetes/pki/ca.pem \\
      --cluster-signing-key-file=/etc/kubernetes/pki/ca-key.pem \\
      --service-account-private-key-file=/etc/kubernetes/pki/sa.key \\
      --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig \\
      --leader-elect=true \\
      --use-service-account-credentials=true \\
      --node-monitor-grace-period=40s \\
      --node-monitor-period=5s \\
      --controllers=*,bootstrapsigner,tokencleaner \\
      --allocate-node-cidrs=true \\
      --service-cluster-ip-range=192.168.0.0/16 \\
      --cluster-cidr=172.16.0.0/12 \\
      --node-cidr-mask-size=24 \\
      --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem

      
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF

# 启动kube-controller-manager
systemctl daemon-reload && systemctl enable --now kube-controller-manager.service
systemctl status kube-controller-manager
# 查看启动状态 默认端口 10257
root@master01:/data/k8s# kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS      MESSAGE                                                                                        ERROR
scheduler            Unhealthy   Get "https://127.0.0.1:10259/healthz": dial tcp 127.0.0.1:10259: connect: connection refused
etcd-0               Healthy     ok
controller-manager   Healthy     ok

配置并启动 kube-scheduler

所有Master节点配置 kube-scheduler service(所有 master 节点配置一样)

# 创建启动 kube-scheduler service
cat > /usr/lib/systemd/system/kube-scheduler.service << EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
      --v=2 \\
      --bind-address=0.0.0.0 \\
      --leader-elect=true \\
      --kubeconfig=/etc/kubernetes/scheduler.kubeconfig

Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF

# 启动服务
systemctl daemon-reload && systemctl enable --now kube-scheduler.service
systemctl status kube-scheduler.service
# 查看组件状态。默认端口 10259
root@master01:/data/k8s# kubectl  get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-0               Healthy   ok

配置集群引导与 RBAC

创建 bootstrap token 和相关的 ClusterRoleBing,允许新节点(kubelet)通过 TLS 引导流程自动向 API Server 申请证书加入集群。

cd /data/k8s

BOOTSTRAP_TOKEN=5ba17d.e9d5d01308883dab
TOKEN_ID=$(echo $BOOTSTRAP_TOKEN |cut -d. -f1)
TOKEN_SECRET=$(echo $BOOTSTRAP_TOKEN |cut -d. -f2)

cat > bootstrap.secret.yaml << EOF
apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-$TOKEN_ID   # 使用前面的的变量 BOOTSTRAP_TOKEN=5ba17d.e9d5d01308883dab
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  description: "The default bootstrap token generated by 'kubelet '."
  token-id: $TOKEN_ID                # Token ID,使用前面的变量 BOOTSTRAP_TOKEN=5ba17d.e9d5d01308883dab
  token-secret: $TOKEN_SECRET        # Token secret,使用前面的变量 BOOTSTRAP_TOKEN=5ba17d.e9d5d01308883dab
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"
  auth-extra-groups:  system:bootstrappers:default-node-token,system:bootstrappers:worker,system:bootstrappers:ingress
 
---
# 授权 bootstrap token 创建证书签名请求(CSR)
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
---
# 自动批准 kubelet 的客户端证书 CSR
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 apply -f bootstrap.secret.yaml
kubectl apply -f bootstrap.secret.yaml
secret/bootstrap-token-5ba17d created
clusterrolebinding.rbac.authorization.k8s.io/kubelet-bootstrap created
clusterrolebinding.rbac.authorization.k8s.io/node-autoapprove-bootstrap created
clusterrolebinding.rbac.authorization.k8s.io/node-autoapprove-certificate-rotation created
clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created
clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created

# 查看 secret
root@master01:/data/k8s# kubectl  get secret -n kube-system |grep bootstrap-token
bootstrap-token-5ba17d   bootstrap.kubernetes.io/token   6      4m37s

kubectl  get secret,clusterroles,clusterrolebindings -n kube-system |grep -E "bootstrap|node-auto|apiserver"

root@master01:/data/k8s# kubectl  get node
No resources found

部署 k8s 工作节点组件(Node 节点)

安装 kubelet 和 kube-proxy 二进制文件

# 确认在 work 节点 node01 上的 kubelet 和 kube-proxy 二进制文件已安装
root@node01:~# ls /usr/local/bin/kube*
/usr/local/bin/kube-proxy  /usr/local/bin/kubelet

必要目录

所有节点创建相关目录

mkdir -p /etc/kubernetes/manifests/ \
      /etc/systemd/system/kubelet.service.d \
      /var/lib/kubelet \
      /var/log/kubernetes

配置并启动 kubelet

注释: kubelet 是运行在每个节点上的“节点代理”,负责管理 Pod 和容器的生命周期。

创建 kubelet 配置文件

注意:如果更改了k8s的service网段,需要更改kubelet-conf.yml 的 clusterDNS 配置,改成k8s Service网段的第十个地址,比如192.168.0.10

mkdir -p /etc/kubernetes/manifests/

cat > /etc/kubernetes/kubelet-conf.yml << 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: /etc/kubernetes/pki/ca.pem
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
cgroupDriver: systemd
cgroupsPerQOS: true
clusterDNS:
- 192.168.0.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: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s
EOF

创建 kubelet systemd 服务

# 所有节点配置 kubelet service
cat > /lib/systemd/system/kubelet.service << EOF
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=network-online.target containerd.service
Wants=network-online.target
Requires=containerd.service

[Service]
ExecStart=/usr/local/bin/kubelet \\
    --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig \\
    --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
    --config=/etc/kubernetes/kubelet-conf.yml \\
    --node-labels=node.kubernetes.io/node=
    #--container-runtime-endpoint=unix:///run/cri-docker.sock  # 基于 docker 需要启动此项
Restart=always
StartLimitInterval=0
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# 启动服务
systemctl daemon-reload && systemctl enable --now kubelet.service
systemctl status kubelet.service

验证 kubelete 服务

# 启动后,在 master 节点执行 kubectl get nodes,应能看到节点状态 NotReady(因为没安装网络插件)
root@master01:/data/k8s# kubectl get nodes
NAME                STATUS     ROLES    AGE     VERSION
node01.jasper.org   NotReady   <none>   2m39s   v1.37.0
node02.jasper.org   NotReady   <none>   7m6s    v1.37.0


# 在 node 节点自动生成证书文件
root@node02:~# ll /var/lib/kubelet/pki
total 20
drwxr-xr-x  2 root root 4096 Sep 11 21:04 ./
drwxr-xr-x 11 root root 4096 Sep 11 21:04 ../
-rw-------  1 root root 1256 Sep 11 21:04 kubelet-client-2026-09-11-21-04-49.pem
lrwxrwxrwx  1 root root   59 Sep 11 21:04 kubelet-client-current.pem@ -> /var/lib/kubelet/pki/kubelet-client-2026-09-11-21-04-49.pem
-rw-r--r--  1 root root 2331 Sep 11 21:04 kubelet.crt
-rw-------  1 root root 1675 Sep 11 21:04 kubelet.key

配置并启动 kube-proxy

注释:kube-proxy 负责维护节点上的网络规划,实现 Service 的负载均衡和访问。

创建 kube-proxy 配置文件

注意,如果不是高可用集群,10.103.236.236:8443 改为 master01 的地址,8443 改为 apiserver 的端口,默认是 6443

如果更改了集群 Pod 的网段,需要更改 kube-proxy.yaml 的 clusterCIDR 为自己的 Pod 网段,mode 改成 ipvs:

cat > /etc/kubernetes/kube-proxy.yaml <<EOF
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
bindAddress: 0.0.0.0
clientConnection:
  acceptContentTypes: ""
  burst: 10
  contentType: application/vnd.kubernetes.protobuf
  kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig
  qps: 5
clusterCIDR: 172.16.0.0/12   # Pod 的 IP 地址段,必须与 controller-manager 的 --cluster-cidr 一致
configSyncPeriod: 15m0s
conntrack:
  max: null
  maxPerCore: 32768
  min: 131072
  tcpCloseWaitTimeout: 1h0m0s
  tcpEstablishedTimeout: 24h0m0s
# 其他通用配置
healthzBindAddress: 0.0.0.0:10256
metricsBindAddress: 0.0.0.0:10249
enableProfiling: false
hostnameOverride: ""
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
# 核心:开启nftables代理模式
mode: nftables
# nftables相关参数
nftables:
  masqueradeBit: 14
  masqueradeAll: false
  syncPeriod: 60s
  minSyncPeriod: 0s
  # skipKernelVersionCheck: true  # 测试用,内核低于5.13才开启,生产不要开
EOF

创建并启动 kube-proxy 服务

所有节点添加kube-proxy的配置和service文件:

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

[Service]
ExecStart=/usr/local/bin/kube-proxy \\
  --config=/etc/kubernetes/kube-proxy.yaml \\
  --cluster-cidr=172.16.0.0/12 \\
  --v=2

Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF

# 启动服务
systemctl daemon-reload && systemctl enable --now kube-proxy.service
systemctl status kube-proxy
# 获取当前完整配置(包含填充后的默认值)
curl http://127.0.0.1:10249/configz | jq

# 查看当前 proxy 代理模式
curl 127.0.0.1:10249/proxyMode

# 查看nftables规则表
nft list table ip kube-proxy

安装 CNI 网络插件 与 CoreDNS

注释:网络插件是实现 Pod 跨节点网络通信的关键。 CoreDNS 是集群内的 DNS 服务器。

安装 CNI 网络插件

所有节点都需要安装 CNI 依赖的相关程序(Node 节点)

kubelet 创建 Pod 的时候,会去宿主机 /opt/cni/bin 目录找 CNI 插件, /etc/cni/net.d 找 CNI 配置文件。

# 在所有节点安装 CNI 依赖的相关程序
mkdir -p /opt/cni/bin /etc/cni/net.d

CNI_VERSION=1.9.1
ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)

wget https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz
#tar tf cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz
tar xf cni-plugins-linux-${ARCH}-v${CNI_VERSION}.tgz -C /opt/cni/bin/

# 验证
ls /opt/cni/bin

安装 cali co (Master 节点)

更改 calico 的网段,主要需要将红色部分的网段,改为自己的 Pod 网段

# 指定版本
CALICO_VERSION=3.32.2
wget https://raw.githubusercontent.com/projectcalico/calico/v${CALICO_VERSION}/manifests/calico.yaml

# 修改 calico-config ConfigMap 的 CALICO_IPV4POOL_CIDR 和 Pod 网段一致
            - name: CALICO_IPV4POOL_CIDR
              value: "172.16.0.0/12"

# 应用清单文件
kubectl apply -f calico.yaml

# 创建 FelixConfiguration,开启 NFT 后端
cat > felix-nft.yaml << EOF
apiVersion: crd.projectcalico.org/v1
kind: FelixConfiguration
metadata:
  name: default
spec:
  iptablesBackend: NFT
  logSeverityScreen: Info
  reportingInterval: 0s
EOF

kubectl apply -f felix-nft.yaml

kubectl rollout restart ds calico-node

# 等待 calico-node 全部就绪,日志验证 NFT 启用:
root@master01:/data/k8s# k get pod -A
NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-db57f7644-frdhp   1/1     Running   0          79m
kube-system   calico-node-vvhzj                         1/1     Running   0          31m
kube-system   calico-node-ww8d9                         1/1     Running   0          31m

kubectl logs -n kube-system -l k8s-app=calico-node | grep -i "nftables backend"

root@master01:/data/k8s# kubectl  get node
NAME                STATUS   ROLES    AGE    VERSION
node01.jasper.org   Ready    <none>   6h1m   v1.37.0
node02.jasper.org   Ready    <none>   6h5m   v1.37.0

安装 calicoctl

# 下载二进制
CALICO_VERSION=3.32.2
ARCH=$([ `arch`  = "aarch64" ] && echo arm64 || echo amd64)

curl -L https://github.com/projectcalico/calico/releases/download/v${CALICO_VERSION}/calicoctl-linux-${ARCH} -o /usr/local/bin/calicoctl

chmod +x /usr/local/bin/calicoctl

# 查看是否正常
calicoctl node status
calicoctl get nodes
calicoctl get ippools

安装 CoreDNS

如果更改了 k8s service 的网段需要将 coredns 的 serviceIP 改成 k8s service 网段的第十个IP

COREDNS_SERVICE_IP=`kubectl get svc | grep kubernetes | awk '{print $3}'`0
K8S_VERSION=v1.37.0

curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/refs/tags/${K8S_VERSION}/cluster/addons/dns/coredns/coredns.yaml.base

# 修改配置文件中的相关配置
sed -e 's/__DNS__SERVER__/'"${COREDNS_SERVICE_IP}"'/g' \
    -e 's#image: registry.k8s.io/coredns#image: registry.aliyuncs.com/google_containers#g' \
    -e 's/__DNS__DOMAIN__/cluster.local/g' \
    -e 's/__DNS__MEMORY__LIMIT__/1024Mi/g' \
    coredns.yaml.base > coredns.yaml


root@master01:/data/k8s# kubectl  apply -f coredns.yaml
serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.apps/coredns created
service/kube-dns created
root@master01:/data/k8s# kubectl  -n kube-system get pod
NAME                                      READY   STATUS             RESTARTS      AGE
coredns-544cdd5dd8-4495b                  0/1     CrashLoopBackOff   4 (44s ago)   2m16s

# 查看原因:出现 CoreDNS 发生了 DNS 递归死循环(DNS)
root@master01:/data/k8s# kubectl  -n kube-system logs -f coredns-544cdd5dd8-4495b
maxprocs: Leaving GOMAXPROCS=2: CPU quota undefined
.:53
[INFO] plugin/reload: Running configuration SHA512 = 591cf328cccc12bc490481273e738df59329c62c0b729d94e8b61db9961c2fa5f046dd37f1cf888b953814040d180f52594972691cd6ff41be96639138a43908
CoreDNS-1.14.6
linux/arm64, go1.26.5, 424d125
# 出现死循环。收到 dns 请求一直转给自己
[FATAL] plugin/loop: Loop (127.0.0.1:35907 -> :53) detected for zone ".", see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO 4322075668335749943.3134280878331088951."

# 解决方法:转给互联网上真空的 DNS
kubectl -n  kube-system edit cm coredns
....
        #forward . /etc/resolv.conf {
        forward . 223.6.6.6 180.76.76.76 {
            max_concurrent 1000
        }

# 重启 coredns
kubectl  rollout restart deploy -n kube-system coredn

安装最新版本 CoreDNS

COREDNS_SERVICE_IP=`kubectl get svc | grep kubernetes | awk '{print $3}'`0

git clone https://github.com/coredns/deployment.git
cd deployment/kubernetes

./deploy.sh -s -i ${COREDNS_SERVICE_IP} | kubectl apply -f -

serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.apps/coredns created
service/kube-dns created

# 查看状态
# kubectl get po -n kube-system -l k8s-app=kube-dns
NAME                       READY   STATUS    RESTARTS   AGE
coredns-85b4878f78-h29kh   1/1     Running   0          8h

部署 Metrics Server (Master 节点)

Metrics Server

在新版的 Kubernetes 中系统资源的采集均使用 Metrics-server,可以通过 Metrics 采集节点和 Pod 的内存、磁盘、CPU 和网络的使用率。

curl -LO https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 关闭 tls 检验,更换国内镜像。
vim components.yaml
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=10250
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --metric-resolution=15s
        - --kubelet-insecure-tls # HTTPS 加密,跳过证书校验;鉴权(token)仍然保留
        image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server:v0.9.0
        imagePullPolicy: IfNotPresent


kubectl  apply -f components.yaml

root@master01:/data/k8s# kubectl  get pod -n kube-system
NAME                                      READY   STATUS    RESTARTS   AGE
calico-kube-controllers-db57f7644-frdhp   1/1     Running   0          144m
calico-node-vvhzj                         1/1     Running   0          96m
calico-node-ww8d9                         1/1     Running   0          96m
coredns-54d449487f-gv78z                  1/1     Running   0          18m
metrics-server-74ccf797cb-4zm25           1/1     Running   0          47s

等待metrics server启动然后查看状态

# kubectl  top node
NAME           CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
master01.jasper.org   231m         5%     1620Mi          42%
master02.jasper.org   274m         6%     1203Mi          31%
master03.jasper.org   202m         5%     1251Mi          32%
node01.jasper.org     69m          1%     667Mi           17%
node02.jasper.org     73m          1%     650Mi           16%

验证集群(Master 节点)

# 创建测试应用
kubectl create deploy nginx --image=nginx:latest --replicas=1 -oyaml --dry-run=client |kubectl apply -f -
kubectl create service clusterip nginx --tcp=80:80

root@master01:/data/k8s# kubectl  get pod -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE                NOMINATED NODE   READINESS GATES
nginx-75d67d6d8c-knvq2   1/1     Running   0          62s   172.30.118.134   node02.jasper.org   <none>           <none>
root@master01:/data/k8s# kubectl  get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   192.168.0.1      <none>        443/TCP   10h
nginx        ClusterIP   192.168.192.68   <none>        80/TCP    108s


# 测试
kubectl run t --restart=Never --rm -it --image=busybox --command -- /bin/sh

/ # nc -zv nginx 80
nginx (192.168.192.68:80) open
/ # ping 172.30.118.134
PING 172.30.118.134 (172.30.118.134): 56 data bytes
64 bytes from 172.30.118.134: seq=0 ttl=62 time=2.105 ms
64 bytes from 172.30.118.134: seq=1 ttl=62 time=0.859 ms
64 bytes from 172.30.118.134: seq=2 ttl=62 time=1.088 ms

master 节点不通是因为没有安装 kubelet 和 网络插件。master 执行 kubectl top node 失败。

添加新的 Node 节点加入集群

安装方法是一样的。

环境变量定义(Node01 执行)

MASTER_IPS="10.103.236.201 10.103.236.202 10.103.236.203"
NODE_IPS="10.103.236.206"

实现 Node1 到新节点的 ssh 验证

ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa

for i in $NODE_IPS;do
    ssh-copy-id -i ~/.ssh/id_rsa.pub $i;
done

确认 /etc/hosts 中添加新节点的 IP 和与主机名映射

echo 10.103.236.206 node03.jasper.org node03 >> /etc/hosts
scp /etc/hosts node03:/etc/hosts

在新的 Node 节点完成前面环境初始化和容器安装时的所有操作

# 设置主机名,在新的 Node 节点执行。
LOCAL_IP=`hostname -I|awk '{print $1}'`
HOST_NAME=$(awk -v ip=$LOCAL_IP '{if($1==ip && $2 !~ "kubeapi")print $2}' /etc/hosts)
hostnamectl set-hostname $HOST_NAME || { color "主机名配置失败,检查 /etc/hosts 文件" 1 ; exit 1; }

# 环境初始化和安装容器运行时的所有操作

在新 NODE 节点的重复 node01 的 k8s 相关操作

# 拷贝 node01 上的文件
NODE_IPS="node03.jasper.org"

NODES=($NODE_IPS)
FILES=(
    "/etc/hosts"
    "/usr/local/bin/runc"
    "/etc/containerd/config.toml"
    "/usr/local/bin/kubelet"
    "/usr/local/bin/kube-proxy"
    "/lib/systemd/system/kubelet.service"
    "/lib/systemd/system/kube-proxy.service"
    "/etc/kubernetes"
    "/opt/cni"
)


for NODE in "${NODES[@]}"; do
    echo ====$NODE START====
    for FILE in "${FILES[@]}"; do
        scp -r "$FILE" "root@${NODE}:$FILE"
    done
    echo ====$NODE END====
done

systemctl daemon-reload && systemctl enable --now kubelet.service kube-proxy.service
systemctl status kubelet.service
systemctl status kube-proxy

略。

# node 节点
root@node03:~# tree /etc/kubernetes/
/etc/kubernetes/
├── bootstrap-kubelet.kubeconfig
├── kube-proxy.kubeconfig
├── kube-proxy.yaml
├── kubelet-conf.yml
├── kubelet.kubeconfig
├── manifests
└── pki
    └── ca.pem

# kubectl 查看新节点
root@master01:/data/k8s# kubectl  get node
NAME                STATUS   ROLES    AGE     VERSION
node01.jasper.org   Ready    worker   23h     v1.37.0
node02.jasper.org   Ready    <none>   23h     v1.37.0
node03.jasper.org   Ready    <none>   5m49s   v1.37.0

# 验证 pod 运行
root@master01:/data/k8s# kubectl  get deploy
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           16h

root@master01:/data/k8s# kubectl  scale deploy nginx --replicas 6

root@master01:/data/k8s# kubectl  get pod -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE                NOMINATED NODE   READINESS GATES
nginx-75d67d6d8c-bj94f   1/1     Running   0          16s   172.20.7.142     node01.jasper.org   <none>           <none>
nginx-75d67d6d8c-ftjq2   1/1     Running   0          16s   172.18.37.1      node03.jasper.org   <none>           <none>
nginx-75d67d6d8c-knvq2   1/1     Running   0          16h   172.30.118.134   node02.jasper.org   <none>           <none>
nginx-75d67d6d8c-snh5z   1/1     Running   0          16s   172.20.7.143     node01.jasper.org   <none>           <none>
nginx-75d67d6d8c-x6wk8   1/1     Running   0          16s   172.18.37.2      node03.jasper.org   <none>           <none>
nginx-75d67d6d8c-xx6vt   1/1     Running   0          16s   172.30.118.135   node02.jasper.org   <none>           <none>

将所有 Master 节点也加入集群的 Worker 节点

加入 worker 节点

# 因为没有将 Master 节点加入集群,所以 Master 节点和 Pod 网络不能,造成无法查看性能信息
root@master01:/data/k8s# kubectl top node
error: Metrics API not available
root@master01:/data/k8s# kubectl top pod
error: Metrics API not available

所有 Master 节点都需要加入到集群的 worker 节点

MASTER_IPS="10.103.236.202 10.103.236.203"
NODES=(${MASTER_IPS})

# master01 可以免密登录所有 master 节点
#ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
for i in ${NODES[@]};do
    ssh-copy-id -i ~/.ssh/id_rsa.pub $i;
done

# 复制 Master01 节点相关必要文件到所有 master 节点,执行下面命令。
FILES=(
    "/etc/kubernetes/kubelet-conf.yml"
    "/lib/systemd/system/kubelet.service"
    "/etc/kubernetes/kube-proxy.yaml"
    "/lib/systemd/system/kube-proxy.service"
    "/etc/kubernetes/kube-proxy.kubeconfig"
    "/etc/kubernetes/bootstrap-kubelet.kubeconfig"
    #"/opt/cni"
)

for NODE in "${NODES[@]}"; do
    echo ====$NODE START====
    for FILE in "${FILES[@]}"; do
        scp -r "$FILE" "root@${NODE}:$FILE"
    done
    scp -r /opt/cni root@${NODE}:/opt
    #ssh root@${NODE} "systemctl daemon-reload && systemctl enable --now kube-proxy kubelet"
    echo ====$NODE END====
done

for NODE in "${NODES[@]}"; do
    echo ====$NODE START====
    ssh root@${NODE} "systemctl daemon-reload && systemctl enable --now kube-proxy kubelet"
    echo ====$NODE END====
done
# 再次查看集群节点成员
root@master01:~# kubectl  get node
NAME                  STATUS   ROLES           AGE    VERSION
master01.jasper.org   Ready    control-plane   89m    v1.37.0
master02.jasper.org   Ready    <none>          2m9s   v1.37.0
master03.jasper.org   Ready    <none>          118s   v1.37.0
node01.jasper.org     Ready    worker          26h    v1.37.0
node02.jasper.org     Ready    worker          26h    v1.37.0
node03.jasper.org     Ready    worker          149m   v1.37.0

节点标记

# 给节点添加 ROLES
kubectl label nodes master01.jasper.org node-role.kubernetes.io/control-plane=
kubectl label nodes node01.jasper.org node-role.kubernetes.io/worker=
kubectl label nodes node02.jasper.org node-role.kubernetes.io/worker=
kubectl label nodes node03.jasper.org node-role.kubernetes.io/worker=

root@master01:~# kubectl  get node
NAME                  STATUS   ROLES           AGE    VERSION
master01.jasper.org   Ready    control-plane   89m    v1.37.0
master02.jasper.org   Ready    <none>          2m9s   v1.37.0
master03.jasper.org   Ready    <none>          118s   v1.37.0
node01.jasper.org     Ready    worker          26h    v1.37.0
node02.jasper.org     Ready    worker          26h    v1.37.0
node03.jasper.org     Ready    worker          149m   v1.37.0


# 只看 node 节点
alias kgn='kubectl get node -l "!node-role.kubernetes.io/control-plane"'

root@master01:~# kgn
NAME                STATUS   ROLES    AGE   VERSION
node01.jasper.org   Ready    worker   2d    v1.37.0
node02.jasper.org   Ready    worker   2d    v1.37.0
node03.jasper.org   Ready    worker   24h   v1.37.0
# 重新生成 Pod,可以看到调试到 master01 节点
kubectl scale deployment nginx --replicas 0
kubectl scale deployment nginx --replicas 3

root@master01:/data/k8s# kubectl  get pod -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP             NODE                  NOMINATED NODE   READINESS GATES
nginx-75d67d6d8c-4mnfv   1/1     Running   0          43s   172.20.7.144   node01.jasper.org     <none>           <none>
nginx-75d67d6d8c-x4r8v   1/1     Running   0          43s   172.18.37.3    node03.jasper.org     <none>           <none>
nginx-75d67d6d8c-zsld5   1/1     Running   0          43s   172.30.9.193   master01.jasper.org   <none>           <none>

# 本机可以通过 service 访问了
root@master01:/data/k8s# kubectl  get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   192.168.0.1      <none>        443/TCP   27h
nginx        ClusterIP   192.168.192.68   <none>        80/TCP    17h
root@master01:/data/k8s# curl -I 192.168.192.68
HTTP/1.1 200 OK

加上污点

# 1. 先看现状
kubectl get node -o custom-columns='NAME:.metadata.name,TAINTS:.spec.taints'
# 2. 补齐 master 的角色标签
kubectl label node master01.jasper.org master02.jasper.org master03.jasper.org \
    node-role.kubernetes.io/control-plane=
# 3. 打污点
kubectl taint node master01.jasper.org master02.jasper.org master03.jasper.org \
  node-role.kubernetes.io/control-plane=:NoSchedule

# 4. 清掉已经在 master 上的存量 Pod
## NoSchedule 只拦新调度的 Pod,已经跑在上面的不动。
## 别用 NoExecute 去做这件事——它会连带影响那些只容忍 NoSchedule 的系统组件。
kubectl drain master01.jasper.org --ignore-daemonsets --delete-emptydir-data
kubectl uncordon master02.jasper.org   # 只解除 unschedulable,污点保留

# 5. 让污点持久化(二进制部署容易踩的坑)
## kubectl taint 改的是 apiserver 里的 Node 对象。
## 如果哪天你删了 Node 对象让 kubelet 重新注册
## (重装、改 hostname、清 etcd 数据等),污点就没了。
## 建议同时写进 kubelet 启动参数:
## systemd unit 里加:
--register-with-taints=node-role.kubernetes.io/control-plane=:NoSchedule

## 或者 KubeletConfiguration yaml 里:
registerWithTaints:
  - key: node-role.kubernetes.io/control-plane
    effect: NoSchedule

Dashboard

Dashboard 用于展示集群中的各类资源,同时也可以通 过Dashboard 实时查看 Pod 的日志和在容器中执行一些命令等。

Headlamp

https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/

官方GitHub地址:https://headlamp.dev/

使用 helm 安装

# 方法1 yaml 文件
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/headlamp/main/kubernetes-headlamp.yaml

# 方法2 helm
helm repo add headlamp https://kubernetes-sigs.github.io/headlamp/
helm search repo -l  headlamp # 查看版本
helm pull headlamp/headlamp --version 0.45.0

tar xf headlamp-0.45.0.tgz
cd headlamp/

cat > my-values.yaml << EOF
service:
  type: NodePort
  nodePort: 30080

resources:
  requests: { cpu: 100m, memory: 128Mi }
  limits:   { cpu: 500m, memory: 512Mi }
EOF

helm install my-headlamp  . \
  -f my-values.yaml \
  --create-namespace -n kube-system 

root@master01:~/hdp/headlamp# kubectl  -n kube-system get svc  my-headlamp
NAME          TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
my-headlamp   NodePort   192.168.249.40   <none>        80:30080/TCP   2m51s

创建管理员用户vim admin.yaml

cat > admin.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system
---
apiVersion: v1
kind: Secret
metadata:
  name: admin-user
  namespace: kube-system
  annotations:
    kubernetes.io/service-account.name: "admin-user"   
type: kubernetes.io/service-account-token  
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding 
metadata: 
  name: admin-user
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system
EOF

# 执行
kubectl apply -f admin.yaml

登录 dashboard

访问 页面,使用 token 登录。

# 获得管理员 token
echo $(kubectl -n kube-system get secret admin-user -o jsonpath={.data.token} | base64 -d)
img_20260913_182316.webp

多集群管理

# 把远端集群的 kubeconfig 做成 Secret 挂进去。
## kubeconfig 用 ServiceAccount token 或客户端证书最稳。

  config:
    extraArgs:
      - -kubeconfig=/headlamp/kubeconfig/config
  volumeMounts:
    - name: kubeconfig
      mountPath: /headlamp/kubeconfig
      readOnly: true
  volumes:
    - name: kubeconfig
      secret:
        secretName: headlamp-kubeconfig

  多个 kubeconfig 文件用 : 分隔:

  config:
    extraArgs:
      - -kubeconfig=/headlamp/kubeconfig/cluster-a:/headlamp/kubeconfig/cluster-b

# 一个 kubeconfig 里有多个 context 也行,Headlamp 会把每个 context 都列成一个集群


kubectl -n kube-system rollout restart deploy/headlamp

插件

# 桌面版可以在 UI 里点着装。集群内部署不行,得声明式配,用 chart 的 pluginsManager:

pluginsManager:
  enabled: true
  configContent: |
    plugins:
      - name: metallb
        source: https://artifacthub.io/packages/headlamp/headlamp-metallb/headlamp-metallb-plugin
        version: 0.5.1
    installOptions:
      parallel: true
      maxConcurrent: 3

收尾工作

生产必备配置

关键性配置请参考视频,不要直接配置!

Runtime 设置

Docker配置 采用containerd作为Runtime无需配置

cat > /etc/docker/daemon.json <<EOF
{
 "registry-mirrors": [
    "https://registry.docker-cn.com",
    "http://hub-mirror.c.163.com",
    "https://docker.mirrors.ustc.edu.cn"
  ],
 "exec-opts": ["native.cgroupdriver=systemd"],
 "max-concurrent-downloads": 10,
 "max-concurrent-uploads": 5,
 "log-opts": {
   "max-size": "300m",
   "max-file": "2"
 },
 "live-restore": true
}
EOF

# 所有节点改完重启docker
systemctl daemon-reload && systemctl restart docker

# 参数说明
#max-concurrent-downloads # 下载并发数
#max-concurrent-uploads   # 上传并发数
#max-size                 # 日志文件最大到多少切割 (此处是300m)
#max-file                 # 日志文件保留个数 (此处是2个)
#live-restore             # 设置为 true,docker 重启是不会影响上面的容器的

kubelet 证书有效期设置

# controller-manager 颁发的证书有效期是 1 年,指定证书过期时间

vim /usr/lib/systemd/system/kube-controller-manager.service
# --feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true \ # 新版是 true
--cluster-signing-duration=876000h0m0s \

# 改完重启
systemctl daemon-reload && systemctl restart kube-controller-manager

安全设置

#vim /etc/systemd/system/kubelet.service.d/10-kubelet.conf
[Service]

Environment="KUBELET_KUBECONFIG_ARGS=--kubeconfig=/etc/kubernetes/kubelet.kubeconfig --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig"

Environment="KUBELET_SYSTEM_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"

Environment="KUBELET_CONFIG_ARGS=--config=/etc/kubernetes/kubelet-conf.yml  --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"

Environment="KUBELET_EXTRA_ARGS=--tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384    --image-pull-progress-deadline=30m"

ExecStart=

ExecStart=/usr/local/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_SYSTEM_ARGS $KUBELET_EXTRA_ARGS

--tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 :设置加密方式,默认 k8s 集群容易被扫到

--image-pull-progress-deadline=30m :下载镜像时间长点

关键性配置请参考视频,不要直接配置,可能会造成集群故障!!

vim /etc/kubernetes/kubelet-conf.yml

rotateServerCertificates: true
allowedUnsafeSysctls:
 - "net.core*"
 - "net.ipv4.*"
kubeReserved:  
  cpu: "1"
  memory: 1Gi
  ephemeral-storage: 10Gi
systemReserved:
  cpu: "1"
  memory: 1Gi
  ephemeral-storage: 10Gi
  
# 改完重启
systemctl daemon-reload && systemctl restart kubelet  

说明

allowedUnsafeSysctl:根据安全,判定是否允许修改内核
kubeReserved:k8s 组件预留资源
systemReserved:系统预留资源

测试环境:
rotateServerCertificates: true
allowedUnsafeSysctls:
 - "net.core*"
 - "net.ipv4.*"
kubeReserved:
  cpu: "10m"
  memory: 10Mi
  ephemeral-storage: 10Mi
systemReserved:
  cpu: "1"
  memory: 20Mi
  ephemeral-storage: 1Gi

安装总结:

1、  kubeadm
2、  二进制
3、  自动化安装
  a)  Ansible
    i.  Master节点安装不需要写自动化。
    ii. 添加Node节点,playbook。
4、  安装需要注意的细节
  a)  上面的细节配置
  b)  生产环境中etcd一定要和系统盘分开,一定要用ssd硬盘。
  c)  Docker数据盘也要和系统盘分开,有条件的话可以使用ssd硬盘

Bootstrapping

这里是补充扩展的知识

kubelet启动

  1. 寻找自己的 kubeconfig 文件
  2. 检索 API 服务器的 URL 和凭据,通常是来自 kubeconfig 文件中的 TLS 密钥和已签名证书
  3. 尝试使用这些凭据来与 API 服务器通信

Bootstrapping CSR申请和证书颁发原理

1.kubelet启动

2.kubele t查找 kubelet.kubeconfig 文件,假设没有这个文件

3.kubelet 会查找本地 bootstrap-kubelet.kubeconfig

4.kubelet 读取 bootstrap.kubeconfig 文件,检索apiserver 的 url 和一个token

5.kubelet 链接 apiserver, 使用这个token 进行认证

a) apiserver 会识别tokenid, apiserver 会查找该 tokenid 对应的 bootstrap 的要给 secret

创建 secret 过程

TOKEN_PUB=$(openssl rand -hex 3)
TOKEN_SECRET=$(openssl rand -hex 8)
BOOTSTRAP_TOKEN="${TOKEN_PUB}.${TOKEN_SECRET}"

kubectl -n kube-system create secret generic bootstrap-token-${TOKEN_PUB} \
        --type 'bootstrap.kubernetes.io/token' \
        --from-literal description="cluster bootstrap token" \
        --from-literal token-id=${TOKEN_PUB} \
        --from-literal token-secret=${TOKEN_SECRET} \
        --from-literal usage-bootstrap-authentication=true \
        --from-literal usage-bootstrap-signing=true

b) 找这个 secret 中的一个字段, apiserver 把这个 token 识别成一个 username,名称是 system:bootstrap:<token-id> ,属于system:bootstrappers这个组,这个组具有申请csr的权限, 该组的权限绑定在一个叫`system:node-bootstrapper`的 clusterrole; clusterrole k8s 集群级别的权限控制,它作用整个k8s集群。

# kubectl get secret -n kube-system |grep boot
bootstrap-signer-token-h6m5b                     kubernetes.io/service-account-token   3      2d8h
bootstrap-token-c8ad9c                           bootstrap.kubernetes.io/token         6      2d

# kubectl get secret -n kube-system bootstrap-token-c8ad9c -o yaml 
apiVersion: v1
data:
  auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6ZGVmYXVsdC1ub2RlLXRva2VuLHN5c3RlbTpib290c3RyYXBwZXJzOndvcmtlcixzeXN0ZW06Ym9vdHN0cmFwcGVyczppbmdyZXNz
  description: VGhlIGRlZmF1bHQgYm9vdHN0cmFwIHRva2VuIGdlbmVyYXRlZCBieSAna3ViZWxldCAnLg==
  token-id: YzhhZDlj
  token-secret: MmU0ZDYxMGNmM2U3NDI2ZQ==
  usage-bootstrap-authentication: dHJ1ZQ==
  usage-bootstrap-signing: dHJ1ZQ==
。。。。
  name: bootstrap-token-c8ad9c
  namespace: kube-system
  resourceVersion: "38320"
  uid: 9a5d1542-1fd2-4713-9505-688cc77958e6
type: bootstrap.kubernetes.io/token
[root@master01.jasper.org ~] eth0 = 192.168.7.107
# echo 'c3lzdGVtOmJvb3RzdHJhcHBlcnM6ZGVmYXVsdC1ub2RlLXRva2VuLHN5c3RlbTpib290c3RyYXBwZXJzOndvcmtlcixzeXN0ZW06Ym9vdHN0cmFwcGVyczppbmdyZXNz'|base64 -d 
system:bootstrappers:default-node-token,system:bootstrappers:worker,system:bootstrappers:ingress

c) CSR: 相当于申请表,可以拿着这个申请表去申请我们的证书。

6.经过上面的认证,kubelet 就有了一个创建和检索 CSR的权限。

7.kubelet 为自己创建一个CSR,名称为 kubernetes.io/kube-apiserver-clinet-kubelet

8.CSR 被允许有两种方式:

a) k8s 管理员使用 kubectl 手动的颁发证书

b) 如果配置了相关权限,kube-controller-manager 会自动同意。

controller-manager有一个CSRAprovingController。会校验kubelet发来的csr的username和group是否有创建csr权限,而且还要验证签发者是否是kubernetes.io/kube-apiserver-client–kubelet

Controller-manager同意CSR请求

  1. CSR 被同意后, controller-manager 创建 kubelet的证书文件
  2. controller-manager 将证书更新至 csr的 status字段
  3. kubelet 从 apiserver 获取证书
  4. kubelet 从获取到的 ey 和证书文件 创建 kubelet.kubeconfig
  5. kubelet 启动完成并正常工作
  6. 可选:如果配置了自动续期,kubelet 会在证书文件过期的时候利用之前的 kubeconfig 文件去申请一个新的证书,相当于续约。
  7. 新的证书被同意或签发,取决于我们的配置。

    a)kubelet创建的CSR是属于一个O: system:nodes

    b)CN: system:nodes:主机名

参考: