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 到外部的五种方式:

  1. hostNetwork — Pod 直接使用宿主机网络
  2. hostPort — 将容器端口映射到宿主机端口
  3. NodePort — 通过 Service 在每个节点开放端口
  4. Ingress — 七层反向代理(HTTP/HTTPS),最常用 ⭐
  5. 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 文件格式完全一致,只需修改 rootserver_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 applyset 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 的全流程。

posted @ 2026-03-31 23:40  gzjwo  阅读(26)  评论(0)    收藏  举报