4. K8s 高阶实战:Helm/CI-CD/日志监控/HA集群
K8s 高阶实战:Helm/CI-CD/日志监控/HA集群
| 章节 | 核心知识点 |
|---|---|
| 一、StatefulSet | headless无头服务(clusterIP:None)、FQDN稳定网络标识、VolumeClaimTemplate独享存储 |
| 二、Metric-Server与HPA | metric-server部署(--kubelet-insecure-tls)、kubectl top前提、HPA响应式/声明式、stress压力测试观察扩缩容 |
| 三、Helm V3 | 移除Tiller、Chart生命周期(create/install/upgrade/rollback/uninstall)、升级(-f/--set)、公有仓库、Helm部署Traefik |
| 四、Helm实战 | 游戏镜像拆分(Dockerfile ARG参数化)、批量build.sh脚本(docker build+push Harbor) |
| 五、Jenkins CI/CD | Gitee推送→Jenkins构建→Harbor→K8S apply全链路、Deployment+Service+Ingress资源清单 |
| 六、EFK日志 | ES单节点+PVC、Kibana中文(zh-CN)、Filebeat DaemonSet采集、RBAC权限、⚠️版本锁定7.10.2 |
| 七、Prometheus监控 | 6步按序创建(setup→alertmanager→node-exporter→grafana→prometheus→serviceMonitor)、sed改IP、导入Grafana仪表盘 |
| 八、二进制高可用集群 | 5节点3Master2Node规划、内核4.19+/ipvs模块、HAProxy+Keepalived VIP漂移、etcd三节点集群、8套证书体系、Bootstrap自动颁发、Master三大组件systemd、Node(kubelet+kube-proxy) |
一、StatefulSet 有状态服务控制器
1.1 为什么需要 StatefulSet?
无状态服务(如 Nginx):任意一个 Pod 挂掉,重新创建一个即可,逻辑完全相同。
有状态服务(如 MySQL 主从):
- 启动/停止有顺序要求(先主后从)
- 每个 Pod 的数据需要独立存储
- 需要固定的网络标识(IP 或主机名)
1.2 StatefulSet 核心能力
| 能力 | 说明 |
|---|---|
| 稳定唯一的网络标识 | 每个 Pod 有固定的主机名 |
| 稳定独立持久的存储 | 每个 Pod 绑定专属 PVC |
| 有序优雅的部署和缩放 | 按 0→1→2 顺序启动 |
| 有序自动的滚动更新 | 从序号最大的开始更新 |
1.3 无头服务(Headless Service)—— 实现稳定网络标识
普通 Service 会分配 ClusterIP(VIP),而无头服务 不分配 VIP,DNS 直接返回所有 Pod 的 IP。
两个必要条件:
# 条件1:Service 的 clusterIP 设为 None
clusterIP: None
# 条件2:StatefulSet 的 serviceName 声明为该无头服务的名称
serviceName: linux-headless
实战:StatefulSet + Headless Service 资源清单
# ====== 01-statefulset-headless-network.yaml ======
apiVersion: v1
kind: Service
metadata:
name: linux-headless # 无头服务的名称
spec:
ports:
- port: 80
name: web
# 将clusterIP设置为None → 不分配VIP → 成为无头服务
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: linux-web-sts # StatefulSet 控制器名称
spec:
selector:
matchLabels:
app: nginx
serviceName: linux-headless # ★ 关键!绑定无头服务,实现稳定网络标识
replicas: 3 # 3个副本,会按 0,1,2 顺序创建
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: harbor.oldboyedu.com/web/apps:v1
FQDN 格式(完全限定域名):
<sts-name>-<ordinal>.<svc-name>.<namespace>.svc.<cluster-domain>
# 示例:
linux-web-sts-0.linux-headless.default.svc.oldboyedu.com
linux-web-sts-1.linux-headless.default.svc.oldboyedu.com
linux-web-sts-2.linux-headless.default.svc.oldboyedu.com
验证 DNS 解析:
# 启动一个临时测试 Pod
kubectl run -it dns-test --rm --image=harbor.oldboyedu.com/linux/alpine -- sh
# 在 Pod 内部依次 ping 每个 Pod 的 FQDN
for i in `seq 0 2`;do ping linux-web-sts-${i}.linux-headless.default.svc.oldboyedu.com -c 3;done
1.4 VolumeClaimTemplate —— 实现独享存储
StatefulSet 使用 卷申请模板(VolumeClaimTemplate),为每个 Pod 自动创建独立的 PVC。
# ====== 02-statefulset-headless-volumeClaimTemplates.yaml ======
apiVersion: v1
kind: Service
metadata:
name: linux-headless-volume
spec:
ports:
- port: 80
name: web
clusterIP: None # 无头服务
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: linux-web-sts-volume
spec:
selector:
matchExpressions:
- key: apps
operator: Exists
serviceName: linux-headless-volume
replicas: 3
# ★ 卷申请模板:每个 Pod 自动创建唯一的 PVC
volumeClaimTemplates:
- metadata:
name: data # 存储卷名称
spec:
accessModes: [ "ReadWriteOnce" ] # 访问模式:单节点读写
storageClassName: "managed-nfs-storage" # 动态存储类名称
resources:
requests:
storage: 2Gi # 每个 Pod 申请 2GB 存储
template:
metadata:
labels:
apps: nginx
spec:
containers:
- name: nginx
image: harbor.oldboyedu.com/web/apps:v1
volumeMounts:
- name: data # 引用上面的 volumeClaimTemplate
mountPath: /usr/share/nginx/html # 挂载路径
---
# 普通 ClusterIP Service 用于负载均衡访问
apiVersion: v1
kind: Service
metadata:
name: oldboyedu-linux-sts-svc
spec:
selector:
apps: nginx
ports:
- port: 80
targetPort: 80
测试独享存储效果——逐个修改首页验证数据隔离:
# 进入 Pod-0 修改首页
kubectl exec -it linux-web-sts-volume-0 -- sh
echo 'www.oldboyedu.com v0.1' > /usr/share/nginx/html/index.html
exit
# 进入 Pod-1 修改首页
kubectl exec -it linux-web-sts-volume-1 -- sh
echo 'www.oldboyedu.com v0.2' > /usr/share/nginx/html/index.html
exit
# 进入 Pod-2 修改首页
kubectl exec -it linux-web-sts-volume-2 -- sh
echo 'www.oldboyedu.com v0.3' > /usr/share/nginx/html/index.html
exit
# 通过 ClusterIP Service 轮询访问,可以看到不同的返回内容
for i in `seq 1000`;do curl 10.200.161.211;sleep 0.5; done
二、Metric-Server 与 HPA 水平自动扩缩容
2.1 部署 Metric-Server
Metric-Server 是 K8S 的资源监控组件,是 kubectl top 命令和 HPA 扩缩容的前提条件。
# 第一步:下载官方高可用资源清单
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/high-availability-1.21+.yaml
# 第二步:修改镜像地址 + 添加 --kubelet-insecure-tls 参数
vim high-availability-1.21+.yaml
# 在 Deployment 的 args 中添加:
# - --kubelet-insecure-tls
# 修改 image 为阿里云镜像源:
# image: registry.aliyuncs.com/google_containers/metrics-server:v0.6.3
# 第三步:创建资源
kubectl apply -f high-availability-1.21+.yaml
# 第四步:检查 Pod 是否 Running
kubectl -n kube-system get pods | grep metrics-server
# 第五步:验证 metric-server 工作正常
kubectl top node # 查看各节点资源使用情况
kubectl top pods # 查看 Pod 资源使用情况
⚠️
--kubelet-insecure-tls的作用:跳过 kubelet 的 TLS 证书校验(生产环境建议配置正规证书)
2.2 HPA(HorizontalPodAutoscaler)实战
第一步:创建带资源限制的 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: oldboyedu-linux85-stress
spec:
replicas: 1
selector:
matchExpressions:
- key: apps
operator: Exists
template:
metadata:
labels:
apps: stress
spec:
containers:
- name: web
image: jasonyin2020/oldboyedu-linux-tools:v0.1
command: # 启动命令:tail 保持容器运行
- tail
- -f
- /etc/hosts
resources: # ★ 必须设置 requests,HPA 才能计算使用率
requests:
cpu: 500m # 请求 0.5 CPU(调度最低保障)
memory: 200M
limits:
cpu: 1 # CPU 上限 1 核
memory: 500M
第二步:创建 HPA 规则
响应式方式:
# 最少2个Pod,最多5个Pod,CPU使用率超过80%时触发扩容
kubectl autoscale deployment oldboyedu-linux85-stress --min=2 --max=5 --cpu-percent=80
声明式 YAML 方式(推荐):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: oldboyedu-linux85-stress
namespace: default
spec:
maxReplicas: 5 # 最大副本数
minReplicas: 2 # 最小副本数
scaleTargetRef: # 关联的目标资源
apiVersion: apps/v1
kind: Deployment
name: oldboyedu-linux85-stress
metrics: # 监控指标
- resource:
name: cpu
target:
averageUtilization: 80 # CPU 平均使用率阈值 80%
type: Utilization
type: Resource
第三步:压力测试观察自动扩容
# 先看下当前 Pod 数量
kubectl get pods
# 进入某个 Pod 执行压力测试(占用4核CPU,持续10分钟)
kubectl exec oldboyedu-linux85-stress-xxx -- stress -c 4 --verbose --timeout 10m
# 另一个终端持续观察 HPA 状态
kubectl get hpa
# 输出示例:
# NAME TARGET MINPODS MAXPODS REPLICAS
# stress 138%/80% 2 5 5 ← 当前CPU 138%,已扩到最大5个
# 观察 Pod 数量变化
kubectl get pods
第四步:停止压力测试后观察自动缩容
# 停止压力测试后,CPU 降低,HPA 会自动缩容回最小值(2个)
# 缩容有冷却期,不会立即发生
kubectl get hpa -w # -w 参数可以持续监控
三、Helm V3 包管理器
3.1 Helm 概述
Helm 是 K8S 的包管理工具,类似 Linux 的 yum/apt。
| 对比项 | Helm V2 | Helm V3 |
|---|---|---|
| 架构 | Client + Tiller(服务端) | 仅 Client(移除 Tiller) |
| 发布时间 | 早期版本 | 2019年11月发布 |
| 推荐度 | ❌ 已废弃 | ✅ 生产推荐 |
3.2 安装 Helm V3
# 下载 Helm 二进制包
wget https://get.helm.sh/helm-v3.9.0-linux-amd64.tar.gz
# 解压 helm 程序到系统 PATH 目录
# --strip-components=1 表示跳过一层目录前缀
tar xf helm-v3.9.0-linux-amd64.tar.gz -C /usr/local/sbin/ linux-amd64/helm --strip-components=1
# 验证安装
helm version
# 配置命令自动补全(新手必备!按两下 Tab 即可补全命令)
helm completion bash > /etc/bash_completion.d/helm
source /etc/bash_completion.d/helm
helm # 连续按两次 Tab 键,出现子命令列表即成功
3.3 Chart 生命周期管理
# ① 创建一个新的 Chart(模板包)
helm create oldboyedu-linux
# 生成目录结构:
# oldboyedu-linux/
# ├── Chart.yaml # Chart 元信息(名称、版本等)
# ├── values.yaml # 默认配置值
# ├── charts/ # 依赖的子 Chart
# ├── templates/ # Kubernetes 资源模板文件
# └── NOTES.txt # 安装后的提示信息
# ② 安装(部署)Chart
# 格式:helm install <Release名称> <Chart目录> -n <命名空间>
helm install web01 oldboyedu-linux -n oldboyedu-helm
# ③ 查看 Release 列表
helm list -n oldboyedu-helm
# ④ 升级应用 —— 基于自定义 values 文件
# 先修改 values.yaml 中的镜像标签等参数
cat oldboyedu-linux/values.yaml
oldboyedu_linux_apps:
namespace: oldboyedu-helm
image: harbor.oldboyedu.com/web/apps
tags: v2 # 修改镜像标签为 v2
replicas: 5 # 修改副本数为 5
# 执行升级
helm upgrade web01 oldboyedu-linux -f oldboyedu-linux/values.yaml -n oldboyedu-helm
# ⑤ 升级应用 —— 基于 --set 传参(更灵活,不修改文件)
helm upgrade --set oldboyedu_linux_apps.tags=v3,replicas=2 \
web01 oldboyedu-linux -n oldboyedu-helm
# ⑥ 查看历史版本
helm history web01 -n oldboyedu-helm
# REVISION UPDATED STATUS DESCRIPTION
# 1 ... superseded Install complete
# 2 ... superseded Upgrade complete
# 3 ... deployed Upgrade complete
# ⑦ 回滚到上一个版本
helm rollback web01 -n oldboyedu-helm
# ⑧ 回滚到指定版本(比如回到版本1)
helm rollback web01 1 -n oldboyedu-helm
# ⑨ 卸载 Release
helm uninstall web01 -n oldboyedu-helm
3.4 公有仓库管理
# 添加阿里云 Helm 仓库
helm repo add oldboyedu-aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# 添加 Azure Helm 仓库
helm repo add oldboyedu-azure http://mirror.azure.cn/kubernetes/charts/
# 查看已添加的仓库列表
helm repo list
# 更新所有仓库的索引
helm repo update
# 搜索 Chart(比如搜索 MySQL 相关的)
helm search repo mysql
# 下载 Chart 到本地(--untar 自动解压)
helm pull oldboyedu-aliyun/mysql --untar
# 使用 Helm 部署 MySQL
helm install db01 mysql -n oldboyedu-helm
# 测试连接 MySQL
# 1) 获取 root 密码(从 Secret 中解码)
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace oldboyedu-helm db01-mysql \
-o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)
# 2) 启动临时客户端连接
kubectl run -it --rm db-client --image=harbor.oldboyedu.com/db/mysql:8.0.32-oracle \
-- mysql -h db01-mysql.oldboyedu-helm.svc.oldboyedu.com -p$MYSQL_ROOT_PASSWORD
3.5 Helm 部署 Traefik Ingress 控制器
暴露 Pod 到外部的五种方式:
- hostNetwork — Pod 直接使用宿主机网络
- hostPort — 将容器端口映射到宿主机端口
- NodePort — 通过 Service 在每个节点开放端口
- Ingress — 七层反向代理(HTTP/HTTPS),最常用 ⭐
- kubectl port-forward — 端口转发,仅用于调试
# ① 添加 Traefik 官方 Helm 仓库
helm repo add traefik https://traefik.github.io/charts
# ② 更新仓库索引
helm repo update
# ③ 拉取 Traefik Chart 到本地
helm pull traefik/traefik --untar
# ④ 修改 values.yaml 配置
vim traefik/values.yaml
# 修改以下两处:
# image:
# repository: harbor.oldboyedu.com/traefik/traefik # 改为自己的镜像仓库地址
# service:
# type: NodePort # 改为 NodePort 类型
# ⑤ 安装 Traefik
helm install traefik traefik
# ⑥ 开启 Dashboard 端口转发(默认未开启,需手动暴露)
kubectl port-forward `kubectl get pods -l "app.kubernetes.io/name=traefik" -o name` \
--address=0.0.0.0 9000:9000
# ⑦ 浏览器访问 Traefik Dashboard
# http://10.0.0.231:9000/dashboard/
四、Helm 综合实战:游戏镜像拆分 + 批量构建
4.1 项目背景
将一个包含 5 个小游戏(bird 坦克、chengbao 宝藏、pingtai 平台、pinshu 评书、tanke 坦克)的综合镜像,拆分为 5 个独立镜像,并使用 Docker + Helm 部署到 K8S。
镜像命名规划:
harbor.oldboyedu.com/games/bird:v1
harbor.oldboyedu.com/games/chengbao:v1
harbor.oldboyedu.com/games/pingtai:v1
harbor.oldboyedu.com/games/pinshu:v1
harbor.oldboyedu.com/games/tanke:v1
4.2 准备工作
# 运行原始镜像提取源代码
docker run -d --name linux85-games jasonyin2020/oldboyedu-games:v0.1
# 创建工作目录结构
mkdir scripts softwares config dockerfile
# 从容器中拷贝出 HTML 源代码
docker cp linux85-games:/usr/local/nginx/html/ softwares/
# 分别打包各游戏代码为 tar.gz
tar zcf softwares/oldboyedu-bird.tar.gz -C softwares/html bird
tar zcf softwares/oldboyedu-chengbao.tar.gz -C softwares/html chengbao
tar zcf softwares/oldboyedu-pingtai.tar.gz -C softwares/html pingtai
tar zcf softwares/oldboyedu-pinshu.tar.gz -C softwares/html pinshu
tar zcf softwares/oldboyedu-tanke.tar.gz -C softwares/html tanke
rm -rf softwares/html/ # 清理原始代码目录
4.3 编写 Nginx 配置文件(每个游戏一个)
# ====== config/bird.conf ======
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
root /usr/local/nginx/html/bird/; # 各游戏的根目录不同
server_name bird.oldboyedu.com; # 域名不同
}
}
其他 4 个游戏的 conf 文件格式完全一致,只需修改
root和server_name对应的值。
4.4 编写 Dockerfile(5 个结构相同,利用 ARG 参数化)
# ====== dockerfile/bird.yaml ======
FROM harbor.oldboyedu.com/linux/alpine:latest # 基础镜像:Alpine Linux(轻量级)
LABEL school=oldboyedu \ # 元数据标签
class=linux85 \
auther=JasonYin
EXPOSE 80 # 暴露 80 端口
ARG apps # 构建参数:接收外部传入的游戏名称
# 替换 Alpine 软件源为阿里云镜像 + 安装 Nginx + 清理缓存
RUN sed -i 's#dl-cdn.alpinelinux.org#mirrors.aliyun.com#' /etc/apk/repositories && \
apk update && apk add nginx && rm -rf /var/cache
ADD softwares/oldboyedu-${apps}.tar.gz /usr/local/nginx/html # 解压对应游戏代码
COPY config/${apps}.conf /etc/nginx/nginx.conf # 复制对应的 Nginx 配置
CMD ["nginx","-g","daemon off;"] # 前台运行 Nginx
5 个 Dockerfile 内容完全相同,通过
--build-arg apps=xxx传入不同参数实现复用。
4.5 编写批量构建脚本
#!/bin/bash
# ============================================================
# 功能:批量构建5个游戏镜像并推送到 Harbor 私有仓库
# 作者:JasonYin
# ============================================================
APPS=("bird" "chengbao" "pingtai" "pinshu" "tanke") # 游戏名称数组
WORKDIR=/root/dockerfile/homework # 构建上下文目录
PROJECT=harbor.oldboyedu.com/games # Harbor 项目路径
VERSION=v1 # 镜像版本号
USERNAME=admin # Harbor 用户名
PASSWORD=1 # Harbor 密码
SERVER=harbor.oldboyedu.com # Harbor 地址
# 登录 Harbor 仓库
docker login -u $USERNAME -p $PASSWORD $SERVER
# 循环遍历每个游戏,依次构建和推送
for app in ${APPS[@]}
do
echo "========== 正在构建: ${app} =========="
# 构建:通过 --build-arg 传入游戏名称,选择对应的 Dockerfile
docker build --build-arg apps=${app} \
-f dockerfile/${app}.yaml \
-t ${PROJECT}/${app}:${VERSION} \
${WORKDIR}
# 推送到 Harbor
echo "========== 正在推送: ${app} =========="
docker push harbor.oldboyedu.com/games/${app}:v1
done
# 登出 Harbor
docker logout $SERVER
执行批量构建:
chmod +x scripts/build.sh
bash scripts/build.sh
五、Jenkins 集成 K8S —— CI/CD 全流程
5.1 整体流程图
开发者写代码 → 推送到 Gitee/GitLab → Webhook 触发 Jenkins →
拉取代码 → Docker Build 构建镜像 → Push 到 Harbor →
kubectl apply 更新 K8S 资源 → 服务上线
5.2 推送代码到 Gitee
# 解压开发代码
unzip yiliaoqixie.zip && cd yiliaoqixie
# 安装 Git 并配置全局信息
yum -y install git
git config --global user.name "jasonyin2020"
git config --global user.email "y1053419035@qq.com"
# 初始化 Git 仓库
git init
git add .
git commit -m 'first commit'
# 打标签(用于版本管理)
git tag -a v0.1 -m 'first commit'
git tag -l # 查看标签列表
# 关联远程仓库(Gitee)并推送
git remote add origin https://gitee.com/jasonyin2020/oldboyedu-linux85-yiliao.git
git push -u origin master # 推送代码
git push -u origin master --tags # 推送标签
5.3 部署 Jenkins(Tomcat 方式)
# ① 安装 JDK 环境
rpm -ivh jdk-8u102-linux-x64.rpm
java -version # 验证 Java 版本
# ② 解压 Tomcat
mkdir -pv /oldboyedu/softwares
tar xf apache-tomcat-8.0.27.tar.gz -C /oldboyedu/softwares
# ③ 清空 Tomcat 默认应用
rm -rf /oldboyedu/softwares/apache-tomcat-8.0.27/webapps/*
# ④ 放入 Jenkins war 包
cp jenkins.war /oldboyedu/softwares/apache-tomcat-8.0.27/webapps/ROOT.war
# ⑤ 解压 Jenkins 数据(包含之前备份的用户数据和插件)
tar xf jenkins-data.tar.gz -C /root/ # 会在 /root 下创建 .jenkins 隐藏目录
# ⑥ 启动 Tomcat
/oldboyedu/softwares/apache-tomcat-8.0.27/bin/startup.sh
# ⑦ 验证端口
ss -ntl | grep 8080 # 确认 8080 端口在监听
# ⑧ 访问 Jenkins WebUI
# http://10.0.0.231:8080/
5.4 项目 Dockerfile 与构建脚本
# ====== Dockerfile ======
FROM harbor.oldboyedu.com/web/apps:v1 # 基于 Nginx 基础镜像
LABEL school=oldboyedu class=linux85 auther=JasonYin
ADD . /usr/share/nginx/html # 将项目代码复制到 Nginx 目录
#!/bin/bash
# ====== build.sh ======
# 功能:构建医疗器材项目的 Docker 镜像并推送到 Harbor
# 用法:./build.sh <版本号>
VERSION=$1 # 接收外部传入的版本号参数
# 登录 Harbor
docker login -u admin -p 1 harbor.oldboyedu.com
# 构建镜像(版本号格式:v0.1, v0.2 ...)
docker build -t harbor.oldboyedu.com/project/yiliaoqixie:v0.${VERSION:-1} .
# 推送到 Harbor
docker push harbor.oldboyedu.com/project/yiliaoqixie:v0.${VERSION:-1}
# 登出
docker logout harbor.oldboyedu.com
5.5 K8S 资源清单(Deployment + Service + Ingress)
# ====== deploy-yiliaoqixie.yaml ======
---
# ① Deployment:声明应用的期望状态
apiVersion: apps/v1
kind: Deployment
metadata:
name: oldboyedu-linux-yiliao
spec:
replicas: 3 # 3 个副本
selector:
matchExpressions:
- key: apps
operator: Exists
template:
metadata:
labels:
apps: yiliao # Pod 标签
spec:
containers:
- name: yiliao
image: harbor.oldboyedu.com/project/yiliaoqixie:v0.4
---
# ② Service:提供内部负载均衡
apiVersion: v1
kind: Service
metadata:
name: oldboyedu-linux-yiliao-svc
spec:
selector:
apps: yiliao # 选择对应标签的 Pod
type: ClusterIP # 仅集群内部可访问
ports:
- port: 80 # Service 端口
targetPort: 80 # Pod 目标端口
---
# ③ Ingress:七层路由规则(通过域名对外暴露)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oldboyedu-linux-yiliao
annotations:
kubernetes.io/ingress.class: traefik # 指定使用 Traefik 作为 Ingress 控制器
spec:
rules:
- host: yiliao.oldboyedu.com # 外部访问域名
http:
paths:
- backend:
service:
name: oldboyedu-linux-yiliao-svc # 转发到哪个 Service
port:
number: 80
path: "/" # 匹配所有路径
pathType: "Prefix"
# 应用资源清单到集群
kubectl apply -f deploy-yiliaoqixie.yaml
5.6 更新应用流程
修改代码 → 推送到 Gitee → Jenkins 触发构建 → 新镜像推送到 Harbor → 修改 Deployment 的 image 版本 → kubectl apply 或 set image 滚动更新
六、EFK 日志系统
6.1 EFK 架构
┌──────────┐ ┌───────────┐ ┌──────────────┐ ┌────────┐
│ Filebeat │────▶│ Elastic- │────▶│ Kibana │ │ 浏览器 │
│ (采集日志) │ │ search │ │ (可视化展示) │◀────│ │
│ DaemonSet │ │ (存储+搜索)│ │ Deployment │ │ │
└──────────┘ └───────────┘ └──────────────┘ └────────┘
6.2 部署 Elasticsearch(单节点)
# ====== deploy-es.yaml ======
apiVersion: v1
kind: Namespace
metadata:
name: oldboyedu-efk # 创建独立命名空间
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: elasticsearch
namespace: oldboyedu-efk
labels:
k8s-app: elasticsearch
spec:
replicas: 1 # 单节点模式
selector:
matchLabels:
k8s-app: elasticsearch
template:
metadata:
labels:
k8s-app: elasticsearch
spec:
containers:
- image: harbor.oldboyedu.com/project/elasticsearch:7.17.5
name: elasticsearch
resources: # 资源限制(ES 比较吃内存!)
limits:
cpu: 2
memory: 3Gi
requests:
cpu: 0.5
memory: 500Mi
env:
- name: "discovery.type"
value: "single-node" # 单节点模式
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m" # JVM 堆内存设置
ports:
- containerPort: 9200 # REST API 端口
name: db
protocol: TCP
volumeMounts:
- name: elasticsearch-data
mountPath: /usr/share/elasticsearch/data # 数据持久化目录
volumes:
- name: elasticsearch-data
persistentVolumeClaim:
claimName: es-pvc # 引用下面的 PVC
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: es-pvc
namespace: oldboyedu-efk
spec:
storageClassName: "managed-nfs-storage" # 使用 NFS 动态存储
accessModes:
- ReadWriteMany # 多节点读写
resources:
requests:
storage: 10Gi # 申请 10GB 存储
---
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
namespace: oldboyedu-efk
spec:
ports:
- port: 9200
targetPort: 9200
selector:
k8s-app: elasticsearch
kubectl apply -f deploy-es.yaml
6.3 部署 Kibana(中文界面 + Ingress)
# ====== deploy-kibana.yaml ======
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: oldboyedu-efk
spec:
replicas: 1
selector:
matchLabels:
k8s-app: kibana
template:
metadata:
labels:
k8s-app: kibana
spec:
containers:
- name: kibana
image: harbor.oldboyedu.com/project/kibana:7.17.5
env:
- name: ELASTICSEARCH_HOSTS
value: http://elasticsearch.oldboyedu-efk.svc.oldboyedu.com:9200 # ES 地址
- name: I18N_LOCALE
value: zh-CN # ★ 中文界面!
ports:
- containerPort: 5601
name: ui
---
apiVersion: v1
kind: Service
metadata:
name: oldboyedu-kibana
namespace: oldboyedu-efk
spec:
ports:
- port: 5601
targetPort: ui
selector:
k8s-app: kibana
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oldboyedu-kibana-ing
namespace: oldboyedu-efk
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: kibana.oldboyedu.com # 通过域名访问 Kibana
http:
paths:
- backend:
service:
name: oldboyedu-kibana
port:
number: 5601
path: "/"
pathType: "Prefix"
kubectl apply -f deploy-kibana.yaml
# 访问地址:http://kibana.oldboyedu.com
6.4 部署 Filebeat 日志采集器(DaemonSet)
Filebeat 以 DaemonSet 形式运行,确保每个 Node 节点都运行一个采集 Pod。
# ====== deploy-filebeat.yaml ======
# ConfigMap1:Filebeat 主配置(输出到 ES)
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: oldboyedu-efk
data:
filebeat.yml: |- # 注意 |- 保留换行格式
filebeat.config:
inputs:
path: ${path.config}/inputs.d/*.yml
reload.enabled: false
modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
output.elasticsearch:
hosts: ['elasticsearch:9200'] # 输出到 Elasticsearch
---
# ConfigMap2:输入配置(采集所有容器的 Docker 日志)
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-inputs
namespace: oldboyedu-efk
data:
kubernetes.yml: |-
- type: docker # ⚠️ docker 类型在 7.2 已废弃,但 7.10.2 仍可用
containers.ids:
- "*" # 采集所有容器
processors:
- add_kubernetes_metadata: # 自动附加 K8S 元数据(命名空间、Pod名等)
in_cluster: true
---
# DaemonSet:确保每个 Node 运行一个 Filebeat Pod
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
namespace: oldboyedu-efk
spec:
selector:
matchLabels:
k8s-app: filebeat
template:
metadata:
labels:
k8s-app: filebeat
spec:
tolerations: # ★ 容忍 Master 节点的污点(Master 也采集)
- key: node-role.kubernetes.io/master
effect: NoSchedule
operator: Exists
serviceAccountName: filebeat # 使用专用 SA
terminationGracePeriodSeconds: 30 # 优雅终止时间
containers:
- name: filebeat
# ★ 推荐 7.10.2 版本!高于此版本可能采集不到 K8s Pod 日志
image: harbor.oldboyedu.com/project/filebeat:7.10.2
args: ["-c", "/etc/filebeat.yml", "-e"]
securityContext:
runAsUser: 0 # 以 root 身份运行
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: config
mountPath: /etc/filebeat.yml
readOnly: true
subPath: filebeat.yml # subPath 只挂载单个文件
- name: inputs
mountPath: /usr/share/filebeat/inputs.d
readOnly: true
- name: data
mountPath: /usr/share/filebeat/data
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers # ★ 挂载宿主机 Docker 日志目录
readOnly: true
volumes:
- name: config
configMap:
defaultMode: 0600 # 权限设为仅所有者读写
name: filebeat-config
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: inputs
configMap:
defaultMode: 0600
name: filebeat-inputs
- name: data
hostPath:
path: /var/lib/filebeat-data # 采集进度保存目录(重启不重复采集)
type: DirectoryOrCreate
---
# RBAC:ClusterRole(定义权限规则)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: filebeat
rules:
- apiGroups: [""]
resources:
- namespaces
- pods # 需要读取 Pod 信息以获取元数据
verbs:
- get
- watch
- list
---
# RBAC:ClusterRoleBinding(将角色绑定到 SA)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: filebeat
subjects:
- kind: ServiceAccount
name: filebeat
namespace: oldboyedu-efk
roleRef:
kind: ClusterRole
name: filebeat
apiGroup: rbac.authorization.k8s.io
---
# RBAC:ServiceAccount(身份认证主体)
apiVersion: v1
kind: ServiceAccount
metadata:
name: filebeat
namespace: oldboyedu-efk
kubectl apply -f deploy-filebeat.yaml
⚠️ Filebeat 版本兼容性注意: 官方推荐使用
elastic/filebeat:7.10.2,高于该版本可能无法正确采集 K8s Pod 日志。type: docker在 Filebeat 7.2 已被官方标记废弃(建议改用type: container),但实际测试 7.12.2 仍未解决 K8s 采集问题,因此仍沿用 7.10.2 + docker 类型。
七、Prometheus 监控系统
7.1 Prometheus 架构
┌──────────────┐ ┌─────────────┐ ┌──────────┐ ┌──────────────┐
│ AlertManager │◀──│ Prometheus │──▶│ Grafana │ │ ServiceMonitor│
│ (告警通知) │ (数据采集+存储)│ (可视化) │ (监控目标发现) │
└──────────────┘ └──────┬───────┘ └──────────┘ └──────────────┘
│
┌──────┼──────┬──────────┬──────────┐
▼ ▼ ▼ ▼ ▼
kubelet apiserver etcd scheduler controller node...
7.2 部署步骤(严格按顺序执行!)
# ① 下载 Prometheus 资源清单
wget http://192.168.15.253/Kubernetes/day11-/prometheus.zip
unzip prometheus.zip && cd prometheus
# ② ★ 修改 ServiceMonitor 中的 IP 地址为你的实际节点 IP
sed -i 's#10.0.0.151#10.0.0.231#' `ls serviceMonitor/*` # Master01
sed -i 's#10.0.0.152#10.0.0.232#' `ls serviceMonitor/*` # Node01
sed -i 's#10.0.0.153#10.0.0.233#' `ls serviceMonitor/*` # Node02
# 验证替换结果
grep "ip:" serviceMonitor/*
# ③ 按顺序依次创建资源!!!
kubectl apply -f setup/ # 第1步:CRD 自定义资源定义 +_RBAC
kubectl apply -f alertmanager/ # 第2步:告警管理器
kubectl apply -f node-exporter/ # 第3步:节点指标导出器
kubectl apply -f grafana/ # 第4步:可视化面板
kubectl apply -f prometheus/ # 第5步:Prometheus 核心
kubectl apply -f serviceMonitor/ # 第6步:监控目标配置
# ⑦ 查找 Grafana 访问端口
kubectl get svc -A | grep grafana
# ⑧ 浏览器访问 Grafana,导入仪表盘模板
# 推荐导入 node-exporter_rev17.json(Node 性能监控大屏)
# 菜单:Dashboard → Import → Upload JSON File
八、K8S 二进制高可用集群部署(终极实战)
8.1 集群规划
| 角色 | 主机名 | IP 地址 |
|---|---|---|
| Master01 | k8s-master01 | 10.0.0.201 |
| Master02 | k8s-master02 | 10.0.0.202 |
| Master03 | k8s-master03 | 10.0.0.203 |
| Node01 | k8s-node01 | 10.0.0.204 |
| Node02 | k8s-node02 | 10.0.0.205 |
VIP(虚拟漂移IP):10.0.0.222(由 Keepalived 管理,绑定到健康的 Master 上)
8.2 第一阶段:基础环境准备(所有节点执行)
步骤一:安装常用软件包
# 所有节点统一执行
yum -y localinstall 01-Linux常用的软件包/*.rpm
步骤二:免密登录 + 同步脚本
# 设置各节点主机名
hostnamectl set-hostname k8s-master01 # 各节点分别执行,改为对应名称
# 所有节点添加 hosts 解析
cat >> /etc/hosts <<'EOF'
10.0.0.201 k8s-master01
10.0.0.202 k8s-master02
10.0.0.203 k8s-master03
10.0.0.204 k8s-node01
10.0.0.205 k8s-node02
EOF
# 在 k8s-master01 配置免密登录到其他节点
cat > password_free_login.sh <<'EOF'
#!/bin/bash
ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa -q
export mypasswd=yinzhengjie
k8s_host_list=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)
for i in ${k8s_host_list[@]}; do
expect -c "
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@$i
expect {
\"*yes/no*\" {send \"yes\r\"; exp_continue}
\"*password*\" {send \"$mypasswd\r\"; exp_continue}
}"
done
EOF
sh password_free_login.sh
# 编写文件同步分发脚本(后续配置文件一键同步到所有节点)
cat > /usr/local/sbin/data_rsync.sh <<'EOF'
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage: $0 /path/to/file(绝对路径)"
exit
fi
if [ ! -e $1 ]; then
echo "[ $1 ] dir or file not find!"
exit
fi
fullpath=`dirname $1`
basename=`basename $1`
cd $fullpath
k8s_host_list=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)
for host in ${k8s_host_list[@]}; do
tput setaf 2
echo ===== rsyncing ${host}: $basename =====
tput setaf 7
rsync -az $basename `whoami`@${host}:$fullpath
done
EOF
chmod +x /usr/local/sbin/data_rsync.sh
# 测试同步脚本
cp /etc/hosts /tmp/
data_rsync.sh /tmp/hosts
步骤三:关闭防火墙 / SELinux / Swap
# 关闭防火墙和 NetworkManager
systemctl disable --now firewalld
systemctl disable --now NetworkManager
# 关闭 SELinux(永久+临时)
setenforce 0
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/sysconfig/selinux
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
# 关闭 Swap 分区(K8S 强制要求!)
swapoff -a && sysctl -w vm.swappiness=0
sed -ri '/^[^#]*swap/s@^@#@' /etc/fstab # 注释 fstab 中的 swap 行
free -h # 确认 swap 为 0
步骤四:时间同步
# 设置时区为上海
ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 手动同步一次时间
ntpdate ntp.aliyun.com
# 配置定时任务每5分钟同步
crontab -e
*/5 * * * * /usr/sbin/ntpdate ntp.aliyun.com
步骤五:内核参数优化
cat > /etc/sysctl.d/k8s.conf <<'EOF'
net.ipv4.ip_forward = 1 # 开启路由转发
net.bridge.bridge-nf-call-iptables = 1 # 网桥经过 iptables
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv6.conf.all.disable_ipv6 = 1 # 关闭 IPv6
fs.may_detach_mounts = 1
vm.overcommit_memory=1 # 允许 overcommit
vm.panic_on_oom=0 # OOM 不 panic
fs.inotify.max_user_watches=89100
fs.file-max=52706963
fs.nr_open=52706963
net.netfilter.nf_conntrack_max=2310720 # 连接跟踪表大小
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl =15
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.tcp_tw_reuse = 1 # 允许重用 TIME_WAIT
net.ipv4.tcp_max_orphans = 327680
net.ipv4.tcp_syncookies = 1 # 防 SYN 攻击
net.ipv4.tcp_max_syn_backlog = 16384
net.core.somaxconn = 16384
EOF
sysctl --system # 加载所有 sysctl 配置
步骤六:升级内核至 4.19+
# 下载内核 RPM 包(ELRepo 源)
wget http://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-4.19.12-1.el7.elrepo.x86_64.rpm
wget http://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-devel-4.19.12-1.el7.elrepo.x86_64.rpm
# 安装内核
yum -y localinstall 02-Linux-kernel/*.rpm
# 修改默认启动内核为第一个(刚安装的新内核)
grub2-set-default 0 && grub2-mkconfig -o /etc/grub2.cfg
grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
grubby --default-kernel # 确认默认内核
步骤七:加载 ipvs 内核模块
# 安装 ipvsadm 工具
yum -y localinstall 04-Linux-ipvsadm/*.rpm
# 手动加载模块
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack
# 配置开机自动加载(写入 modules-load 配置文件)
cat > /etc/modules-load.d/ipvs.conf << 'EOF'
ip_vs ip_vs_lc ip_vs_wlc ip_vs_rr ip_vs_wrr
ip_vs_lblc ip_vs_lblcr ip_vs_dh ip_vs_sh
ip_vs_fo ip_vs_nq ip_vs_sed ip_vs_ftp nf_conntrack
ip_tables ip_set xt_set ipt_set ipt_rp_filter
ipt_REJECT ipip
EOF
# 重启后检查模块是否加载
reboot
uname -r # 确认内核版本 >= 4.19
lsmod | grep --color=auto -e ip_vs -e nf_conntrack
💡 小知识: 4.19+ 内核中
nf_conntrack_ipv4已更名为nf_conntrack,写配置时要注意!
步骤八:安装 Docker
# 安装 Docker CE 19.03
yum -y localinstall 05-Linux-docker-ce-19_03/*.rpm
# 配置 daemon.json(★ CgroupDriver 必须为 systemd)
mkdir /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": ["https://registry.docker-cn.com","https://tuv7rqqq.mirror.aliyuncs.com"],
"log-driver": "json-file",
"log-opts": {"max-size": "200m"},
"storage-driver": "overlay2"
}
EOF
# 启动 Docker 并设置开机自启
systemctl daemon-reload && systemctl enable --now docker
docker info | grep "Cgroup Driver" # 确认 Cgroup Driver = systemd
8.3 第二阶段:etcd 集群部署(三节点)
etcd 是 K8S 的分布式键值数据库,存储集群所有状态信息,必须高可用。
# 在所有 Master 节点下载 cfssl 证书工具
cp 08-cfssl/* /usr/local/bin
chmod +x /usr/local/bin/{cfssl,cfssljson}
# 创建证书目录
mkdir /etc/etcd/ssl -p
mkdir -p /etc/kubernetes/pki
生成 etcd 证书
cd 07-k8s-ha-install/pki/
# ① 生成 CA 证书(证书颁发机构)
cfssl gencert -initca etcd-ca-csr.json | cfssljson -bare /etc/etcd/ssl/etcd-ca
# ② 颁发 etcd 证书(包含所有 master 节点 IP 和主机名)
cfssl gencert \
-ca=/etc/etcd/ssl/etcd-ca.pem \
-ca-key=/etc/etcd/ssl/etcd-ca-key.pem \
-config=ca-config.json \
-hostname=127.0.0.1,k8s-master01,k8s-master02,k8s-master03,10.0.0.201,10.0.0.202,10.0.0.203 \
-profile=kubernetes \
etcd-csr.json | cfssljson -bare /etc/etcd/ssl/etcd
# ③ 分发证书到其他 Master 节点
MasterNodes='k8s-master02 k8s-master03'
for NODE in $MasterNodes; do
ssh $NODE "mkdir -p /etc/etcd/ssl"
for FILE in etcd-ca-key.pem etcd-ca.pem etcd-key.pem etcd.pem; do
scp /etc/etcd/ssl/${FILE} $NODE:/etc/etcd/ssl/${FILE}
done
done
编写 etcd 配置文件(每个节点不同)
# ====== k8s-master01 的 etcd 配置 ======
cat > /etc/etcd/etcd.config.yml <<'EOF'
name: 'k8s-master01' # 节点名称(每个节点不同)
data-dir: /var/lib/etcd
wal-dir: /var/lib/etcd/wal
listen-peer-urls: 'https://10.0.0.201:2380' # 集群间通信端口
listen-client-urls: ' https://10.0.0.201:2379 ,http://127.0.0.1:2379' # 客户端端口
initial-advertise-peer-urls: 'https://10.0.0.201:2380'
advertise-client-urls: 'https://10.0.0.201:2379'
initial-cluster: 'k8s-master01= https://10.0.0.201:2380,k8s-master02=https://10.0.0.202:2380 ,k8s-master03=https://10.0.0.203:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
enable-v2: true
client-transport-security:
cert-file: '/etc/etcd/ssl/etcd.pem'
key-file: '/etc/etcd/ssl/etcd-key.pem'
client-cert-auth: true
trusted-ca-file: '/etc/etcd/ssl/etcd-ca.pem'
auto-tls: true
peer-transport-security:
cert-file: '/etc/etcd/ssl/etcd.pem'
key-file: '/etc/etcd/ssl/etcd-key.pem'
peer-client-cert-auth: true
trusted-ca-file: '/etc/etcd/ssl/etcd-ca.pem'
auto-tls: true
EOF
⚠️ 其他两个节点(master02/master03)的配置格式相同,只需修改:
name改为对应主机名- 所有 IP 改为本机 IP
启动 etcd 集群
# 创建 systemd 服务单元(三个节点相同)
cat > /usr/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
# 建立软链接(让 etcd 能找到证书)
mkdir -p /etc/kubernetes/pki/etcd
ln -s /etc/etcd/ssl/* /etc/kubernetes/pki/etcd/
# 启动 etcd(三个节点都要执行)
systemctl daemon-reload
systemctl enable --now etcd
systemctl status etcd
# 验证集群健康状态
etcdctl --endpoints="10.0.0.201:2379,10.0.0.202:2379,10.0.0.203:2379" \
--cacert=/etc/kubernetes/pki/etcd/etcd-ca.pem \
--cert=/etc/kubernetes/pki/etcd/etcd.pem \
--key=/etc/kubernetes/pki/etcd/etcd-key.pem \
endpoint status --write-out=table
8.4 第三阶段:高可用 LB(HAProxy + Keepalived)
原理: HAProxy 反向代理 3 个 Master 的 API Server(6443端口),Keepalived 提供 VIP(10.0.0.222)漂移能力。
# 所有 Master 节点安装
yum -y localinstall 09-keepalive-haproxy/*.rpm
HAProxy 配置(三个节点相同)
cat > /etc/haproxy/haproxy.cfg <<'EOF'
global
maxconn 2000
ulimit-n 16384
log 127.0.0.1 local0 err
defaults
log global
mode http
option httplog
timeout connect 5000
timeout client 50000
timeout server 50000
frontend k8s-master
bind 0.0.0.0:16443 # 监听 16443 端口
bind 127.0.0.1:16443
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 # 轮询算法
server k8s-master01 10.0.0.201:6443 check # 后端3个 APIServer
server k8s-master02 10.0.0.202:6443 check
server k8s-master03 10.0.0.203:6443 check
EOF
Keepalived 配置(三个节点略有不同)
# ====== k8s-master01 的 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 MASTER # 初始状态为 MASTER
interface eth0 # 网卡名称
mcast_src_ip 10.0.0.201 # 本机 IP(每个节点不同!)
virtual_router_id 51 # VRID 必须一致
priority 101 # 优先级
advert_int 2
authentication {
auth_type PASS
auth_pass K8SHA_KA_AUTH # 认证密码必须一致
}
virtual_ipaddress {
10.0.0.222 # ★ VIP 虚拟 IP
}
track_script {
chk_apiserver
}
}
EOF
其他节点只需修改
mcast_src_ip为各自 IP。
健康检查脚本(三个节点相同)
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
systemctl stop keepalived # haproxy 挂了就停掉 keepalived
exit 1
else
exit 0
fi
EOF
chmod +x /etc/keepalived/check_apiserver.sh
启动高可用服务
# 三个 Master 节点都执行
systemctl enable --now haproxy
systemctl enable --now keepalived
# 验证 VIP 是否存在
ip a | grep 10.0.0.222 # 应能在某个 Master 上看到 VIP
8.5 第四阶段:K8S 证书体系
K8S 使用 PKI 体系进行双向 TLS 认证,共需生成 8 套证书:
| 序号 | 证书名称 | 用途 |
|---|---|---|
| 1 | CA(ca.pem) | 集群根证书,签发其他所有证书 |
| 2 | etcd 证书 | etcd 集群间通信加密 |
| 3 | apiserver 证书 | API Server 服务端证书 |
| 4 | front-proxy 证书 | API Server 聚合层证书 |
| 5 | controller-manager 证书 | CM 组件访问 apiserver |
| 6 | scheduler 证书 | Scheduler 组件访问 apiserver |
| 7 | admin 证书 | 管理员(kubectl)使用 |
| 8 | sa.key/sa.pub | ServiceAccount Token 签发密钥对 |
生成核心证书
cd 07-k8s-ha-install/pki/
# ① 生成 K8S 根 CA
cfssl gencert -initca ca-csr.json | cfssljson -bare /etc/kubernetes/pki/ca
# ② 生成 APIServer 证书(包含 VIP、Service 网段首地址、所有 Master IP)
cfssl gencert \
-ca=/etc/kubernetes/pki/ca.pem \
-ca-key=/etc/kubernetes/pki/ca-key.pem \
-config=ca-config.json \
-hostname=10.96.0.1,10.0.0.222,127.0.0.1,kubernetes,default.svc,...,10.0.0.201,10.0.0.202,10.0.0.203 \
-profile=kubernetes \
apiserver-csr.json | cfssljson -bare /etc/kubernetes/pki/apiserver
# ③ 生成聚合层证书(front-proxy)
cfssl gencert -initca front-proxy-ca-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-ca
cfssl gencert -ca=/etc/kubernetes/pki/front-proxy-ca.pem ... front-proxy-client-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-client
# ④ 生成 ControllerManager 证书 + kubeconfig
cfssl gencert -ca=/etc/kubernetes/pki/ca.pem ... manager-csr.json | cfssljson -bare /etc/kubernetes/pki/controller-manager
# 生成 CM 的 kubeconfig(四步:设集群→设用户→设上下文→切换上下文)
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.pem \
--embed-certs=true \
--server=https://10.0.0.222:6443 \ # 通过 VIP 访问 APIServer
--kubeconfig=/etc/kubernetes/controller-manager.kubeconfig
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
# ⑤ Scheduler 证书 + kubeconfig(同上四步模式)
cfssl gencert -ca=/etc/kubernetes/pki/ca.pem ... scheduler-csr.json | cfssljson -bare /etc/kubernetes/pki/scheduler
# 四步生成 scheduler.kubeconfig(省略,格式与CM相同,改名为scheduler即可)
# ⑥ Admin 用户证书 + kubeconfig(管理员使用)
cfssl gencert -ca=/etc/kubernetes/pki/ca.pem ... admin-csr.json | cfssljson -bare /etc/kubernetes/pki/admin
# 四步生成 admin.kubeconfig(格式同上)
# ⑦ ServiceAccount 密钥对(用于签发 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
# ⑧ 分发证书到其他 Master 节点
for NODE in k8s-master02 k8s-master03; do
for FILE in $(ls /etc/kubernetes/pki | grep -v etcd); do
scp /etc/kubernetes/pki/${FILE} $NODE:/etc/kubernetes/pki/${FILE};
done
for FILE in admin.kubeconfig controller-manager.kubeconfig scheduler.kubeconfig; do
scp /etc/kubernetes/${FILE} $NODE:/etc/kubernetes/${FILE};
done
done
8.6 第五阶段:Master 组件启动(APIServer / ControllerManager / Scheduler)
APIServer(三个节点配置基本相同,仅 advertise-address 不同)
# ====== k8s-master01 的 APIServer systemd 配置 ======
cat > /usr/lib/systemd/system/kube-apiserver.service << 'EOF'
[Unit]
Description=Kubernetes API Server
After=network.target
[Service]
ExecStart=/usr/local/bin/kube-apiserver \
--v=2 \
--logtostderr=true \
--allow-privileged=true \
--bind-address=0.0.0.0 \
--secure-port=6443 \
--insecure-port=0 \
--advertise-address=10.0.0.201 \ # ★ 每个节点改自己的 IP
--service-cluster-ip-range=10.96.0.0/12 \ # Service 网段
--service-node-port-range=3000-50000 \ # NodePort 范围
--etcd-servers= https://10.0.0.201:2379,https://10.0.0.202:2379 ,https://10.0.0.203:2379 \
--etcd-cafile=/etc/etcd/ssl/etcd-ca.pem \
--etcd-certfile=/etc/etcd/ssl/etcd.pem \
--etcd-keyfile=/etc/etcd/ssl/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 \
--service-account-key-file=/etc/kubernetes/pki/sa.pub \
--service-account-signing-key-file=/etc/kubernetes/pki/sa.key \
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,... \
--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
Restart=on-failure
RestartSec=10s
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
# 启动 APIServer(三个 Master 都执行)
systemctl daemon-reload && systemctl enable --now kube-apiserver
ControllerManager(三个节点相同)
cat > /usr/lib/systemd/system/kube-controller-manager.service << 'EOF'
[Unit]
Description=Kubernetes Controller Manager
After=network.target
[Service]
ExecStart=/usr/local/bin/kube-controller-manager \
--v=2 \
--address=127.0.0.1 \
--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 \
--allocate-node-cidrs=true \
--cluster-cidr=172.16.0.0/12 \ # Pod 网段
--node-cidr-mask-size=24
Restart=always
RestartSec=10s
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now kube-controller-manager
Scheduler(三个节点相同)
cat > /usr/lib/systemd/system/kube-scheduler.service << 'EOF'
[Unit]
Description=Kubernetes Scheduler
After=network.target
[Service]
ExecStart=/usr/local/bin/kube-scheduler \
--v=2 \
--address=127.0.0.1 \
--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
验证 Master 组件健康
kubectl get cs --kubeconfig=/etc/kubernetes/admin.kubeconfig
# NAME STATUS MESSAGE
# scheduler Healthy ok
# controller-manager Healthy ok
# etcd-0 Healthy {"health":"true"}
# etcd-1 Healthy {"health":"true"}
# etcd-2 Healthy {"health":"true"}
8.7 第六阶段:Bootstrap 证书自动颁发
Node 节点的 kubelet 证书不需要手动签发,而是通过 Bootstrap 机制向 APIServer 自动申请。
# ① 创建 bootstrap-kubelet.kubeconfig(kubelet 首次启动时用来申请证书的凭证)
cd 07-k8s-ha-install/bootstrap/
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.pem \
--embed-certs=true \
--server=https://10.0.0.222:6443 \
--kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig
kubectl config set-credentials tls-bootstrap-token-user \
--token=c8ad9c.2e4d610cf3e7426e \
--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
# ② 拷贝 admin.kubeconfig 到 ~/.kube/config(供 kubectl 使用)
mkdir -p /root/.kube
cp /etc/kubernetes/admin.kubeconfig /root/.kube/config
# ③ 创建 Bootstrap 资源(RBAC + Secret,允许 kubelet 自动申请证书)
kubectl create -f bootstrap.secret.yaml
8.8 第七阶段:Node 节点部署
# 在 Master01 向所有 Node 节点分发必要的证书和 bootstrap 配置
cd /etc/kubernetes/
for NODE in k8s-master02 k8s-master03 k8s-node01 k8s-node02; do
ssh $NODE mkdir -p /etc/kubernetes/pki /etc/etcd/ssl
# 分发 etcd 证书
for FILE in etcd-ca.pem etcd.pem etcd-key.pem; do
scp /etc/etcd/ssl/$FILE $NODE:/etc/etcd/ssl/
done
# 分发 K8S 证书 + bootstrap 配置
for FILE in pki/ca.pem pki/ca-key.pem pki/front-proxy-ca.pem bootstrap-kubelet.kubeconfig; do
scp /etc/kubernetes/$FILE $NODE:/etc/kubernetes/${FILE}
done
done
Kubelet 配置与启动
# 所有节点创建工作目录
mkdir -p /var/lib/kubelet /var/log/kubernetes /etc/systemd/system/kubelet.service.d /etc/kubernetes/manifests/
# ① kubelet.service(systemd 服务单元)
cat > /usr/lib/systemd/system/kubelet.service <<'EOF'
[Unit]
Description=Kubernetes Kubelet
After=docker.service
Requires=docker.service
[Service]
ExecStart=/usr/local/bin/kubelet
Restart=always
StartLimitInterval=0
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# ② kubelet 配置覆盖文件
cat > /etc/systemd/system/kubelet.service.d/10-kubelet.conf <<'EOF'
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig --kubeconfig=/etc/kubernetes/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-amd64:3.2"
Environment="KUBELET_EXTRA_ARGS=--node-labels=node.kubernetes.io/node='' "
ExecStart=
ExecStart=/usr/local/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_SYSTEM_ARGS $KUBELET_EXTRA_ARGS
EOF
# ③ kubelet 详细配置文件(yaml 格式)
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
cgroupDriver: systemd # ★ 必须与 Docker 一致
clusterDNS:
- 10.96.0.10 # CoreDNS 地址(Service 网段第10个)
clusterDomain: oldboyedu.com # 集群域
containerLogMaxSize: 10Mi
cpuCFSQuota: true
maxPods: 110
failSwapOn: true
rotateCertificates: true # ★ 自动轮转证书
staticPodPath: /etc/kubernetes/manifests # 静态 Pod 目录
resolvConf: /etc/resolv.conf
EOF
# ④ 启动 kubelet(所有节点执行)
systemctl daemon-reload
systemctl enable --now kubelet
systemctl status kubelet
# ⑤ 在 Master01 验证节点注册
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k8s-node01 Ready <none> 5m v1.23.15
# k8s-node02 Ready <none> 3m v1.23.15
Kube-Proxy 配置与启动
# ① 在 Master01 生成 kube-proxy 的 kubeconfig(基于 ServiceAccount Token)
kubectl -n kube-system create serviceaccount kube-proxy
kubectl create clusterrolebinding system:kube-proxy \
--clusterrole system:node-proxier \
--serviceaccount kube-system:kube-proxy
# 获取 SA 的 Token 并生成 kubeconfig
SECRET=$(kubectl -n kube-system get sa/kube-proxy --output=jsonpath='{.secrets[0].name}')
JWT_TOKEN=$(kubectl -n kube-system get secret/$SECRET --output=jsonpath='{.data.token}' | base64 -d)
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.pem \
--embed-certs=true \
--server=https://10.0.0.222:6443 \
--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig
kubectl config set-credentials kubernetes \
--token=${JWT_TOKEN} \
--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=kubernetes \
--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig
kubectl config use-context kubernetes \
--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig
# ② 分发 kube-proxy.kubeconfig 到所有节点
for NODE in k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02; do
scp /etc/kubernetes/kube-proxy.kubeconfig $NODE:/etc/kubernetes/kube-proxy.kubeconfig
done
# ③ 所有节点编写 kube-proxy 配置
cat > /etc/kubernetes/kube-proxy.conf << EOF
KUBE_PROXY_OPTS="--logtostderr=false --v=2 --log-dir=/var/log/kubernetes/ --config=/etc/kubernetes/kube-proxy-config.yml"
EOF
# 注意:每个节点 hostnameOverride 要改成自己的主机名!
cat > /etc/kubernetes/kube-proxy-config.yml << EOF
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
metricsBindAddress: 0.0.0.0:10249
clientConnection:
kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig
hostnameOverride: k8s-master01 # ★ 每个节点改自己的名字
clusterCIDR: 172.30.0.0/16 # Pod 网段
mode: ipvs # 使用 ipvs 模式
EOF
# ④ 创建 systemd 服务
cat > /usr/lib/systemd/system/kube-proxy.service << EOF
[Unit]
Description=Kubernetes Proxy
After=network.target
[Service]
EnvironmentFile=/etc/kubernetes/kube-proxy.conf
ExecStart=/usr/local/bin/kube-proxy \$KUBE_PROXY_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
# ⑤ 启动 kube-proxy(所有节点执行)
systemctl daemon-reload
systemctl enable --now kube-proxy
systemctl status kube-proxy
8.9 最终验证
# 检查所有节点状态
kubectl get nodes
# 检查系统 Pod 是否正常运行
kubectl get pods -A
# 检查集群组件健康
kubectl cs
至此,一套 3 Master + 2 Node 的 K8S 高可用二进制集群 搭建完成!
📝 全文总结: 本文从 StatefulSet 有状态服务入手,学习了 Helm 包管理器的完整生命周期操作、Traefik Ingress 部署、游戏镜像拆分与批量构建实战;然后进入运维监控领域,搭建了 EFK 日志系统(含 Filebeat 7.10.2 版本兼容性坑)、Prometheus + Grafana 监控平台;最后以纯二进制方式手搓了一套生产级 K8S 高可用集群,涵盖了从内核升级、ipvs 模块、etcd 集群、HAProxy+Keepalived VIP、完整的 PKI 证书体系、三大 Master 组件 systemd 配置、Bootstrap 自动颁发证书机制到 Node 节点 kubelet+kube-proxy 的全流程。

浙公网安备 33010602011771号