k8s-云原生cicd
1、helm1.1、什么是helmHelm 就是 Kubernetes 的“应用商店”和“软件包管理器”。就像 Ubuntu 的apt或 Python 的pip一样Helm 专门用来管理 Kubernetes 上的复杂应用。核心概念三个词搞定Chart包相当于一个应用的“安装压缩包”。里面包含了部署这个应用所需的所有 YAML 文件如 Deployment、Service、Ingress 等。Values配置相当于安装时的“选配单”。比如你想安装 MySQL通过 Values 你可以设置“密码是多少”、“存储多大”等参数无需手动改 YAML。Release运行实例相当于你把 Chart 安装到 K8s 集群后正在运行的“应用程序本体”。同一个 Chart 可以安装多次产生多个不同的 Release比如mysql-test和mysql-prod。1.2、helm的作用告别“YAML 地狱”手动写几十个互相依赖的 YAML 文件非常痛苦Helm 将它们打包成一个整体。一键安装/升级安装复杂应用如 Nginx Ingress、Prometheus只需一行helm install命令升级时只需helm upgrade即使出错也能一键helm rollback回滚到上一版本。标准化分发开发者可以把 Chart 推到公共仓库如 Artifact Hub别人直接下载即用极大地降低了部署门槛。Helm 把杂乱无章的 YAML 文件变成了可配置、可版本管理、可分享的标准安装包是 Kubernetes 运维中“开箱即用”的核心工具。1.3、helm安装#注意此处安装的helm版本为4.2.2 #https://helm.sh/zh/docs/intro/install curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-4 chmod 700 get_helm.sh ./get_helm.sh #测试 rootk8s-master:~# helm version version.BuildInfo{Version:v4.2.2, GitCommit:b05881cf967a5a09e19866799d0edfd40675803a, GitTreeState:clean, GoVersion:go1.26.4, KubeClientVersion:v1.36}2、helm部署Jenkins2.1、添加Jenkins Helm 仓库helm repo add jenkins https://charts.jenkins.io helm repo update2.2、编辑values.yaml文件此处均为博主踩过无数坑总结出来的默认值文件helm仓库拉取的chart包会有许多参数需要设备此文件是专门用来记录改参数的文件详情大家可以ai了解一下兄弟们只需要看到这个文件了解对应参数的作用即可此处镜像是拉取的本地harbor仓库镜像由于前边出过教程此处便不再赘述了拉取命令如下docker pull jenkins/jenkins docker pull kiwigrid/k8s-sidecar:1.0.0 docker pull jenkins/inbound-agent# # Jenkins Helm Chart 自定义配置values.yaml # 适用 Chart: jenkins/jenkins ( 5.x) # 部署命名空间: jenkins # # -------- 全局设置 -------- # 强制指定部署的命名空间与 Helm 命令行 -n 保持一致 namespaceOverride: jenkins # 集群内部域名后缀用于 Service 全限定域名FQDN解析 # 默认通常为 cluster.local但若集群自定义了域名需相应修改 clusterZone: jenkins.local # # Jenkins ControllerMaster配置 # controller: # -------- 服务暴露方式 -------- # 服务的 Kubernetes 类型ClusterIP / NodePort / LoadBalancer # 本地测试环境使用 NodePort 方便外部访问 serviceType: NodePort # 如果 serviceType 为 NodePort可指定固定端口范围 30000-32767 # 若不指定Kubernetes 会自动分配一个随机端口 nodePort: 32000 # -------- 镜像配置 -------- image: # 私有镜像仓库地址需提前配置 imagePullSecret registry: harbor.local # 镜像仓库和镜像名注意与 Harbor 中的实际项目/镜像名完全一致 repository: jenkins/jenkins # 镜像标签强烈建议使用具体版本号不要使用 latest tag: 2.555.3-jdk21 # 拉取策略Always / IfNotPresent / Never # 当 tag 为 latest 时即便设为 IfNotPresentKubernetes 也可能强制拉取 # 建议使用具体版本号以让 IfNotPresent 真正生效 pullPolicy: IfNotPresent # 用于拉取私有仓库镜像的 Secret 名称需提前在命名空间中创建 # 此 Secret 会被自动注入到 Controller Pod 和 Agent Pod 中 imagePullSecretName: harbor-secret # -------- 管理员账号配置 -------- admin: # 管理员用户名默认 admin username: admin # 管理员密码生产环境建议使用 existingSecret 引用外部 Secret避免明文 password: 123456 # -------- 插件管理 -------- # 是否在 Jenkins 启动时自动下载并安装插件列表 # 设置为 false 可避免因网络问题导致下载失败从而引发启动崩溃踩坑点 # 后续可通过 UI 或 Jenkins CLI 手动安装插件 installPlugins: false # -------- JCasC配置即代码设置 -------- JCasC: # 是否启用 Chart 自带的默认 JCasC 配置 # 设为 false 后必须在 configScripts 中完整定义所有配置否则默认配置可能丢失 defaultConfig: false # 自定义 JCasC 脚本YAML 格式可定义多个文件 configScripts: # 此处定义了一个名为 minimal 的配置片段 minimal: | jenkins: # -------- 安全域用户认证 -------- securityRealm: local: allowsSignup: false # 禁止用户自助注册 enableCaptcha: false # 禁用验证码简单环境 users: - id: admin # 用户名 name: Jenkins Admin password: 123456 # 与上方 admin.password 保持一致 # -------- 授权策略 -------- authorizationStrategy: loggedInUsersCanDoAnything: # 登录用户可做任何事非管理员权限 allowAnonymousRead: false # 禁止匿名用户读取 # -------- Agent 连接端口JNLP TCP -------- # 此端口必须显式设置否则 Agent 无法通过 TCP 连接踩坑点 # 如果改用 WebSocket此端口可设置但非必须建议保留以作备用 slaveAgentPort: 50000 # -------- Kubernetes 云配置动态 Agent -------- clouds: - kubernetes: name: kubernetes # 云名称在 Jenkins 中显示 serverUrl: https://kubernetes.default # 集群 API Server 内部地址 skipTlsVerify: true # 本地集群跳过证书验证生产环境谨慎 namespace: jenkins # Agent Pod 运行的命名空间 jenkinsUrl: http://my-jenkins:8080 # Agent 回调 Master 的地址 websocket: true # 启用 WebSocket 连接推荐避免 TCP 端口复杂配置 # -------- 安全相关令牌策略 -------- security: apiToken: creationOfLegacyTokenEnabled: false # 禁止创建旧版 API Token tokenGenerationOnCreationEnabled: false usageStatisticsEnabled: true # -------- 全局设置 -------- unclassified: location: url: http://my-jenkins:8080 # Jenkins 根 URL必须与上方 jenkinsUrl 一致 # -------- Sidecar 容器辅助工具 -------- sidecars: # Config AutoReload用于监听 JCasC ConfigMap 变更并自动重载配置无需重启 Pod configAutoReload: enabled: true # 强烈建议开启便于调试和热更新 image: registry: harbor.local repository: jenkins/k8s-sidecar # 原镜像 kiwigrid/k8s-sidecar需推送到私有仓库 tag: 1.0.0 imagePullPolicy: IfNotPresent # # Jenkins Agent执行器配置 # agent: # 是否启用 Kubernetes Agent 功能即允许动态创建 Agent Pod enabled: true # Agent 使用的镜像默认 JNLP 连接容器 image: registry: harbor.local repository: jenkins/inbound-agent tag: latest # 建议改为具体版本 pullPolicy: IfNotPresent # # 持久化存储配置PVC # persistence: # 使用的 StorageClass需提前创建本例使用本地存储 storageClass: local-storage # PVC 请求的存储容量 size: 8Gi # 其他可选参数如 accessMode默认 ReadWriteOnce、existingClaim 等未指定2.3、创建pv卷前边value文件当中默认有pvc但是没有pv所有此处我们手动创建pvcat jenkins-pv.yaml EOF apiVersion: v1 kind: PersistentVolume metadata: name: jenkins-pv namespace: jenkins spec: capacity: storage: 10Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: path: /mnt/jenkins nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-worker-01 EOF kubectl apply -f jenkins-pv.yaml#查询是否创建成功 rootk8s-master:~/k8s/helm# kubectl -n jenkins get pv jenkins-pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE jenkins-pv 10Gi RWO Delete Bound jenkins/my-jenkins local-storage unset 8d rootk8s-master:~/k8s/helm#2.4、helm部署jenkins#安装jenkins helm install my-jenkins jenkins/jenkins -f values.yaml -n jenkins --create-namespace --version 5.9.33 #验证状态 kubectl -n jenkins get pods -w #执行成功 rootk8s-master:~/k8s/helm# kubectl -n jenkins get pod |grep my my-jenkins-0 2/2 Running 2 (31m ago) 3d23h rootk8s-master:~/k8s/helm#2.5、创建RBAC账户待后续jenkins使用#创建仅对pod拥有所有权限的账户 cat EOF | kubectl apply -f - apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: my-jenkins-deploy-role namespace: jenkins rules: - apiGroups: [*] resources: [*] verbs: [*] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: my-jenkins-deploy-binding namespace: jenkins roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: my-jenkins-deploy-role subjects: - kind: ServiceAccount name: my-jenkins namespace: jenkins EOF3、基于jenkins实现k8s pod自动化部署3.1、部署流程3.2、组件要求下载如上组件3.3、构建包含kubectl命令的agent镜像此处避免后续持续拉取kubectl二进制包#编写dockerfile文件 cat Dockerfile EOF FROM harbor.local/jenkins/inbound-agent:latest USER root RUN curl -LO https://dl.k8s.io/release/v1.36.2/bin/linux/amd64/kubectl \ chmod x kubectl \ mv kubectl /usr/local/bin/ USER jenkins EOF #构建新镜像 docker build -t harbor.local/jenkins/inbound-agent-with-kubectl:latest . #上传镜像至harbor docker push harbor.local/jenkins/inbound-agent-with-kubectl:latest #后续执行替换成此镜像即可3.4、基础命令测试测试docker与kubectl命令是否可以正常使用pipeline { agent { kubernetes { yaml spec: serviceAccountName: my-jenkins containers: - name: jnlp #解决docker命令无权限问题 securityContext: runAsUser: 0 # root image: harbor.local/jenkins/inbound-agent-with-kubectl:latest env: - name: JENKINS_URL value: http://my-jenkins:8080/ - name: JENKINS_WEBSOCKET value: true - name: JENKINS_TUNNEL value: #映射kubectl命令 - name: KUBERNETES_SERVICE_HOST value: kubernetes.default - name: KUBERNETES_SERVICE_PORT value: 443 #挂载宿主机docker命令 volumeMounts: - mountPath: /var/run/docker.sock name: docker-sock - name: docker-bin mountPath: /usr/bin/docker volumes: #映射dockers命令 - name: docker-sock hostPath: path: /var/run/docker.sock type: Socket - name: docker-bin hostPath: path: /usr/bin/docker type: File imagePullSecrets: - name: harbor-secret restartPolicy: Never } } stages { stage(kubectl命令验证) { steps { sh kubectl version kubectl -n jenkins get pods } } stage(docker命令验证) { steps { sh echo 验证docker命令 docker version docker login harbor.local -u admin -p Harbor12345 echo 验证docker build命令 cat Dockerfile EOF FROM nginx:latest RUN echo aaa /usr/share/nginx/html/index.html EOF docker build -t harbor.local/jenkins/nginx:v1 . docker push harbor.local/jenkins/nginx:v1 } } } }3.5、创建pipeline流水线并部署测试podpipeline { agent { kubernetes { yaml spec: serviceAccountName: my-jenkins containers: - name: jnlp #解决docker命令无权限问题 securityContext: runAsUser: 0 # root image: harbor.local/jenkins/inbound-agent-with-kubectl:latest env: - name: JENKINS_URL value: http://my-jenkins:8080/ - name: JENKINS_WEBSOCKET value: true - name: JENKINS_TUNNEL value: - name: KUBECONFIG - name: KUBERNETES_SERVICE_HOST value: kubernetes.default - name: KUBERNETES_SERVICE_PORT value: 443 volumeMounts: - mountPath: /var/run/docker.sock name: docker-sock - name: docker-bin mountPath: /usr/bin/docker volumes: #映射dockers命令 - name: docker-sock hostPath: path: /var/run/docker.sock type: Socket - name: docker-bin hostPath: path: /usr/bin/docker type: File imagePullSecrets: - name: harbor-secret restartPolicy: Never } } stages { stage(部署 Nginx) { steps { sh # 部署 Nginx cat EOF /tmp/nginx-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-dp namespace: jenkins spec: replicas: 3 selector: matchLabels: app: nginx-dp template: metadata: labels: app: nginx-dp spec: containers: - name: nginx-dp image: harbor.local/public/nginx ports: - containerPort: 80 imagePullSecrets: - name: harbor-secret # 如果使用私有镜像保留此项 EOF kubectl apply -f /tmp/nginx-deploy.yaml --request-timeout10s kubectl get pods -n jenkins -l appnginx-dp --request-timeout10s } } } }#测试成功 rootk8s-master:~/k8s/helm# kubectl -n jenkins get pod NAME READY STATUS RESTARTS AGE my-jenkins-0 2/2 Running 2 (72m ago) 3d23h nginx-dp-5bc6f65547-bnmc7 1/1 Running 0 25s nginx-dp-5bc6f65547-c9fck 1/1 Running 0 25s nginx-dp-5bc6f65547-npgw6 1/1 Running 0 25s rootk8s-master:~/k8s/helm#4、基于jenkins实现k8s 考试系统部署全流程4.1、服务器部署在做这个测试前博主觉得还是有必要和兄弟们聊一聊宿主机上该项目是如何部署的以便于大家理解后续Jenkins上pipeline脚本当中的命令的具体含义4.1.1、项目目录详解顶层目录可以这样理解 xzs ├── source │ ├── xzs Java 后端 │ ├── vue │ │ ├── xzs-admin 管理员网页 │ │ └── xzs-student 学生网页 │ └── wx │ └── xzs-student 微信小程序 ├── sql PostgreSQL 数据库脚本 ├── docker Docker 部署文件 ├── release 部署说明 ├── docs 相关文档 └── README.md 项目简介 它并不是三个完全独立的系统而是三个前端共用一个后端 管理端 Vue ─────┐ │ HTTP/JSON 学生端 Vue ─────┼──────────→ Spring Boot ──→ MyBatis ──→ PostgreSQL │ 微信小程序 ─────┘ #部署架构如下 用户 ↓ HTTPS Nginx / 负载均衡 ├── /admin、/student → Vue 静态文件 └── /api/** → Spring Boot 8000 ↓ PostgreSQL 5432 此处架构为前后端不分离架构因此我们需要先将adminstudent的前端打包成一个静态文件移到后端的static目录下最后打成一个总的jar包4.1.2、项目文件读取在上手一个Java项目时以下文件是运维人员必读的1. 后端总配置 application.yml 配置文件。 需要知道 服务端口8000 Web 容器Undertow MyBatis XML 位置 默认运行环境dev 安全放行地址 数据库连接池配置 日志配置 特别注意 spring: profiles: active: dev 这意味着没有通过启动参数覆盖时默认连接开发环境。 生产启动必须明确指定prod为生产环境spring 配置文件 Linux java -Duser.timezoneAsia/Shanghai \ -Dspring.profiles.activeprod \ -jar xzs-3.9.0.jar4.1.3、项目如何构建前端 student vue端 cd source/vue/xzs-student npm ci npm run build:prod admin vue端 cd source/vue/xzs-admin npm ci npm run build:prod 后端 cd source/xzs mvn clean package 文件生成查看 source/xzs/target/xzs-3.9.0.jar4.1.4、环境准备jdk1.8mavennode.js命令下载#maven下载 wget https://dlcdn.apache.org/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.tar.gz sudo tar -zxvf ./apache-maven-3.9.16-bin.tar.gz -C /usr/local/ sudo ln -s /usr/local/apache-maven-3.9.16 /usr/local/maven echo export MAVEN_HOME/usr/local/maven /etc/profile echo export PATH$MAVEN_HOME/bin:$PATH /etc/profile #声明环境变量 source /etc/profile #jdk下载 sudo apt install openjdk-8-jdk -y #node.js下载 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash source ~/.bashrc nvm install 244.1.5、部署xzs系统#1、拉取代码 git clone https://github.com/mindskip/xzs.git #切换到代码目录 cd xzs/ #2、前端构建 cd source/vue/xzs-admin npm ci npm run build:prod cd ../../.. cd source/vue/xzs-student npm ci npm run build:prod cd ../../.. #确认前端是否构建成功 test -f source/vue/xzs-admin/admin/index.html echo 管理端构建成功 test -f source/vue/xzs-student/student/index.html echo 学生端构建成功 #3、把前端资源复制到后端static文件下 mkdir -p source/xzs/src/main/resources/static/admin rsync -av --delete \ source/vue/xzs-admin/admin/ \ source/xzs/src/main/resources/static/admin/ mkdir -p source/xzs/src/main/resources/static/student rsync -av --delete \ source/vue/xzs-student/student/ \ source/xzs/src/main/resources/static/student/ #4、后端构建 cd source/xzs mvn clean package #检查jar包是否存在 ls -lh target/xzs-3.9.0.jar #5、数据库准备 #5.1、安装数据库 apt install -y postgresql psql --version systemctl status postgresql #5.2、创建数据库和应用账户 #创建专有用户 sudo -u postgres createuser --pwprompt xzs_app #5.3、创建专有数据库 sudo -u postgres createdb \ --ownerxzs_app \ --encodingUTF8 \ xzs #查询数据库是否创建成功 sudo -u postgres psql -c \l | grep xzs #5.4、导入xzs sql文件 psql \ -h 127.0.0.1 \ -p 5432 \ -U xzs_app \ -d xzs \ -f sql/xzs-postgresql.sql #导入完成后检查表 psql \ -h 127.0.0.1 \ -U xzs_app \ -d xzs \ -c \dt #应该存在如下表 t_user t_subject t_question t_exam_paper t_exam_paper_answer t_text_content ... #6、创建运行应用目录 sudo mkdir -p /opt/xzs/app sudo mkdir -p /opt/xzs/log sudo mkdir -p /opt/xzs/config #7、专有用户创建 sudo useradd \ --system \ --home /opt/xzs \ --shell /sbin/nologin \ xzs #复制jar包至应用目录下 sudo cp source/xzs/target/xzs-3.9.0.jar \ /opt/xzs/app/xzs-3.9.0.jar #创建application.xml文件并配置严格的权限将原本xzs目录下的文件提取到专门目录下便于后续查看与修改 sudo cat /opt/xzs/config/application-prod.yml EOF server: port: 8000 logging: path: /opt/xzs/log spring: datasource: url: jdbc:postgresql://127.0.0.1:5432/xzs username: xzs_app password: 请替换成实际密码 hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 30000 EOF sudo chown xzs:xzs /opt/xzs/config/application-prod.yml sudo chmod 600 /opt/xzs/config/application-prod.yml #前台启动 sudo -u xzs java \ -Xms512m \ -Xmx1024m \ -Duser.timezoneAsia/Shanghai \ -Dspring.profiles.activeprod \ -Dspring.config.additional-locationfile:/opt/xzs/config/ \ -jar /opt/xzs/app/xzs-3.9.0.jar #日志查看 tail -f /opt/xzs/log/* #接口测试预期返回3个3022个200 curl -I http://127.0.0.1:8000/ curl -I http://127.0.0.1:8000/student curl -I http://127.0.0.1:8000/admin curl -I http://127.0.0.1:8000/student/index.html curl -I http://127.0.0.1:8000/admin/index.html #浏览器访问 http://宿主机IP:8000/student http://宿主机IP:8000/admin4.2、k8s部署xzs系统4.2.1、构建含jdkmavennode命令镜像cat Dockerfile EOF FROM ubuntu:22.04 ARG MAVEN_VERSION3.9.16 ARG JDK_VERSION8u351 ARG NODE_VERSIONv14.21.3 ENV DEBIAN_FRONTENDnoninteractive RUN apt-get update apt-get install -y --no-install-recommends \ ca-certificates curl wget unzip \ apt-get clean rm -rf /var/lib/apt/lists/* ADD apache-maven-${MAVEN_VERSION}-bin.tar.gz /opt/ ADD jdk-${JDK_VERSION}-linux-x64.tar.gz /opt/ ADD node-${NODE_VERSION}-linux-x64.tar.xz /opt/ RUN ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven \ ln -s /opt/jdk1.8.0_351 /opt/jdk1.8 \ ln -s /opt/node-${NODE_VERSION}-linux-x64 /opt/node-v14 # ----- 关键修改分开定义环境变量 ----- ENV JAVA_HOME/opt/jdk1.8 ENV MAVEN_HOME/opt/maven ENV NODE_HOME/opt/node-v14 ENV PATH$JAVA_HOME/bin:$MAVEN_HOME/bin:$NODE_HOME/bin:$PATH # ------------------------------------ WORKDIR /opt CMD [/bin/bash] EOF docker build -t my-java-node-maven:latest . docker tag my-java-node-maven:latest harbor.local/jenkins/my-java-node-maven:latest docker push harbor.local/jenkins/my-java-node-maven:latest4.2.2、构建xzs镜像FROM harbor.local/jenkins/my-java-node-maven RUN mkdir -p /opt/xzs/app /opt/xzs/config COPY xzs-3.9.0.jar /opt/xzs/app/ COPY application-prod.yml /opt/xzs/config/ WORKDIR /opt/xzs/app CMD [java, -Xms512m, -Xmx1024m, \ -Duser.timezoneAsia/Shanghai, \ -Dspring.profiles.activeprod, \ -Dspring.config.additional-locationfile:/opt/xzs/config/, \ -jar, /opt/xzs/app/xzs-3.9.0.jar]#前置条件将xzs的jar包与application文件移到当前目录 docker build -t xzs:v1.0 .、 #打包推送 rootk8s-worker-02:/opt/xzs/app# docker tag xzs:v1.0 harbor.local/jenkins/xzs:v1.0 rootk8s-worker-02:/opt/xzs/app# docker push harbor.local/jenkins/xzs:v1.04.2.3、编写yaml文件# 使用前必须先完成两件事 # 1. 将 postgres-secret 中的 CHANGE_ME_BEFORE_FIRST_APPLY 替换为真实强密码。 # 2. 在 PostgreSQL 第一次启动前创建初始化 SQL ConfigMap # kubectl create namespace xzs --dry-runclient -o yaml | kubectl apply -f - # kubectl create configmap xzs-db-init \ # --from-file001-xzs-init.sql/opt/xzs-postgresql.sql \ # -n xzs --dry-runclient -o yaml | kubectl apply -f - # # 如果 Harbor 需要认证还需提前创建拉取凭据 # kubectl create secret docker-registry harbor-pull-secret \ # --docker-serverharbor.local \ # --docker-usernameharbor-user \ # --docker-passwordharbor-password \ # -n xzs # # 部署kubectl apply -f k8s/xzs-all.yaml --- apiVersion: v1 kind: Namespace metadata: name: xzs --- apiVersion: v1 kind: Secret metadata: name: postgres-secret namespace: xzs type: Opaque stringData: # 必须在 PostgreSQL 第一次启动前修改。PVC 初始化后仅修改此值不会自动修改数据库内部密码。 POSTGRES_PASSWORD: 123456 --- apiVersion: v1 kind: ConfigMap metadata: name: xzs-config namespace: xzs data: application-prod.yml: | server: port: 8000 spring: datasource: url: jdbc:postgresql://postgres-service:5432/xzs username: xzs_app hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 # 项目原有 prod 日志只写文件。Kubernetes 中改为 stdout便于 kubectl logs 和日志采集。 logback-k8s.xml: | ?xml version1.0 encodingUTF-8? configuration include resourceorg/springframework/boot/logging/logback/defaults.xml/ appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern${CONSOLE_LOG_PATTERN}/pattern charsetUTF-8/charset /encoder /appender root levelINFO appender-ref refCONSOLE/ /root /configuration --- apiVersion: v1 kind: Service metadata: name: postgres-service namespace: xzs labels: app: postgres spec: # StatefulSet 使用无头 Service 获得稳定网络标识。 clusterIP: None selector: app: postgres ports: - name: postgres protocol: TCP port: 5432 targetPort: postgres --- apiVersion: apps/v1 kind: StatefulSet metadata: name: postgres namespace: xzs spec: serviceName: postgres-service replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: terminationGracePeriodSeconds: 60 containers: - name: postgres image: postgres:16-alpine imagePullPolicy: IfNotPresent env: - name: POSTGRES_USER value: xzs_app - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: postgres-secret key: POSTGRES_PASSWORD - name: POSTGRES_DB value: xzs - name: PGDATA value: /var/lib/postgresql/data/pgdata - name: TZ value: Asia/Shanghai ports: - name: postgres containerPort: 5432 protocol: TCP volumeMounts: - name: postgres-storage mountPath: /var/lib/postgresql/data # 官方 postgres 镜像只会在空 PGDATA 首次初始化时执行此 SQL。 - name: init-sql mountPath: /docker-entrypoint-initdb.d/001-xzs-init.sql subPath: 001-xzs-init.sql readOnly: true resources: requests: cpu: 250m memory: 512Mi limits: cpu: 1 memory: 1Gi startupProbe: exec: command: - sh - -c - pg_isready -U xzs_app -d xzs periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 60 readinessProbe: exec: command: - sh - -c - pg_isready -U xzs_app -d xzs periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 6 livenessProbe: exec: command: - sh - -c - pg_isready -U xzs_app -d xzs periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 6 volumes: - name: init-sql configMap: name: xzs-db-init volumeClaimTemplates: - metadata: name: postgres-storage spec: storageClassName: postgres-storage accessModes: - ReadWriteOnce # 如果集群没有默认 StorageClass请在这里增加 storageClassName。 resources: requests: storage: 10Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: xzs namespace: xzs labels: app: xzs environment: production spec: # 当前项目的 Session 默认保存在单个 JVM 内存中首次部署先使用一个副本。 replicas: 1 revisionHistoryLimit: 5 minReadySeconds: 10 selector: matchLabels: app: xzs strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 template: metadata: labels: app: xzs spec: terminationGracePeriodSeconds: 40 # 如果 Harbor 允许匿名拉取可以删除 imagePullSecrets。 imagePullSecrets: - name: harbor-secret initContainers: - name: wait-for-postgres image: postgres:16-alpine imagePullPolicy: IfNotPresent env: - name: PGPASSWORD valueFrom: secretKeyRef: name: postgres-secret key: POSTGRES_PASSWORD command: - sh - -c - | until pg_isready -h postgres-service -p 5432 -U xzs_app -d xzs; do echo waiting for PostgreSQL... sleep 3 done until psql -h postgres-service -U xzs_app -d xzs -tAc \ SELECT to_regclass(public.t_user) | grep -q t_user; do echo waiting for xzs database schema... sleep 3 done containers: - name: xzs # 建议由 Jenkins 替换为不可变标签例如 3.9.0-42-a1b2c3d。 image: harbor.local/jenkins/xzs:v2.0 imagePullPolicy: IfNotPresent env: - name: TZ value: Asia/Shanghai - name: SPRING_PROFILES_ACTIVE value: prod - name: SPRING_CONFIG_ADDITIONAL_LOCATION value: file:/opt/xzs/config/ - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: name: postgres-secret key: POSTGRES_PASSWORD - name: LOGGING_CONFIG value: file:/opt/xzs/config/logback-k8s.xml - name: JAVA_TOOL_OPTIONS value: - -Xms512m -Xmx1024m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/tmp -Duser.timezoneAsia/Shanghai ports: - name: http containerPort: 8000 protocol: TCP volumeMounts: - name: xzs-config mountPath: /opt/xzs/config readOnly: true resources: requests: cpu: 250m memory: 768Mi limits: cpu: 1 memory: 1536Mi startupProbe: httpGet: path: /student/index.html port: http periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 60 readinessProbe: httpGet: path: /student/index.html port: http periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 6 # Liveness 只判断进程和端口不把数据库故障变成 Pod 重启风暴。 livenessProbe: tcpSocket: port: http periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 6 lifecycle: preStop: exec: command: - sh - -c - sleep 10 securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL volumes: - name: xzs-config configMap: name: xzs-config --- apiVersion: v1 kind: Service metadata: name: xzs-service namespace: xzs spec: type: NodePort selector: app: xzs # 这是多副本前的过渡方案不能替代 Spring Session Redis。 sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800 ports: - name: http protocol: TCP port: 8000 targetPort: http nodePort: 31111apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: postgres-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer --- apiVersion: v1 kind: PersistentVolume metadata: name: postgres-pv spec: capacity: storage: 10Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: postgres-storage local: path: /mnt/data/xzs-postgres nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-worker-02