Kubernetes: k8s-安装篇-二进制-1.37
- TAGS: Kubernetes
概述
k8s 架构和组件
整体架构
kubernetes(k8s) 是一个容器编排平台,核心目标:
- 自动部署、自动扩容、服务发现、负载均衡、自愈能力、滚动更新、高可用管理。
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)
- 网络插件 CNI(Container Network Interface):负责为 pod 提供专用通信网络。calico\flannel
- DNS:CoreDns
容器资源监控:metrics-server 通过
metrics.k8s.ioAPI 提供给 k8s 使用。metrics server 在 k8s 中的位置kubetctl top/HPA/VPA/Dashboard metrics.k8s.io API metrics-server kubelet Summary API Node 资源数据
- Web 界面(仪表盘):Headlamp
- 集群层面日志
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
[[email protected] ~]# grubby --default-kernel /boot/vmlinuz-4.19.12-1.el7.elrepo.x86_64
所有节点重启,然后检查内核是不是4.19
[[email protected] ~]# 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 运维必备)
- https://kubernetes.io/zh-cn/docs/tasks/debug/debug-cluster/crictl/
- github https://github.com/kubernetes-sigs/cri-tools
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 节点的二进制文件
- 下载介绍 https://kubernetes.io/zh-cn/releases/download/
- https://www.downloadkubernetes.com/
- 从这里下载 CHANGELOG-1.37.md: Server Binaries 包含所有的 二进制文件
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 二进制文件
- 从 CHANGELOG-1.37.md 查看相关依赖版本: Dependencies
- 在 Kubernetes 增强提案 KEPs 中搜索 etcd 查找版本依赖说明
- 官方 Blog Kubernetes v1.37: etcd RangeStream Cuts Memory Use on Large List Reads
在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测试
[[email protected] 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:
- 官方配置说明 KubeProxyConfiguration
- 参考说明 nftables 代理模式
- 源码内设计文档 proxy/nftables/README.md
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
- 资源监控工具
- github metric-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)
多集群管理
# 把远端集群的 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启动
- 寻找自己的 kubeconfig 文件
- 检索 API 服务器的 URL 和凭据,通常是来自 kubeconfig 文件中的 TLS 密钥和已签名证书
- 尝试使用这些凭据来与 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 [[email protected] ~] 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请求
- CSR 被同意后, controller-manager 创建 kubelet的证书文件
- controller-manager 将证书更新至 csr的 status字段
- kubelet 从 apiserver 获取证书
- kubelet 从获取到的 ey 和证书文件 创建 kubelet.kubeconfig
- kubelet 启动完成并正常工作
- 可选:如果配置了自动续期,kubelet 会在证书文件过期的时候利用之前的 kubeconfig 文件去申请一个新的证书,相当于续约。
新的证书被同意或签发,取决于我们的配置。
a)kubelet创建的CSR是属于一个O: system:nodes
b)CN: system:nodes:主机名