欢迎光临
我们一直在努力

PHP项目Kubernetes部署与Jenkins CI/CD流水线

本文档详细记录了PHP项目从Jenkins环境搭建、Ingress配置、Jenkins Pipeline构建,到基础镜像制作的完整Kubernetes化流程,包含所有配置文件、脚本及操作命令,确保项目可稳定部署与运维。

第一步:Jenkins 配置

Jenkins是实现CI/CD流水线的核心工具,本步骤需完成Master与Agent节点的基础镜像制作、权限配置、存储挂载及界面参数设置,为后续流水线运行提供环境支持。

一、Jenkins Master 基础镜像制作

Jenkins Master负责管理任务与调度Agent,需基于Ubuntu 22.04制作包含JDK、Jenkins WAR包及启动脚本的自定义镜像。

1. 目录结构
目录/文件说明
Dockerfile.master Master节点镜像构建脚本
entrypoint.sh Master节点启动脚本
jdk-17.0.12/ JDK 17安装目录(本地提前准备)
jenkins.war Jenkins WAR包(对应版本2.516.2)

执行以下命令可查看目录结构:

root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/jenkins-setup/master# tree -a -L 1
.
├── Dockerfile.master
├── entrypoint.sh
├── jdk-17.0.12
└── jenkins.war
2 directories, 5 files

2. Dockerfile.master内容

该文件定义了Master镜像的基础环境、依赖安装、用户配置及端口暴露,关键步骤已添加注释说明:

# Dockerfile.master
# 基础镜像:Ubuntu 22.04
FROM ubuntu:22.04
# 维护者信息
LABEL maintainer="devops@example.com"
# 设置环境变量:JDK路径、Jenkins工作目录、Jenkins版本
ENV JAVA_HOME=/data/jdk-17.0.12 \\
JENKINS_HOME=/var/jenkins_home \\
JENKINS_VERSION=2.516.2

# 替换Ubuntu软件源为阿里云,加速依赖安装
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \\
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list

# 安装基础工具:wget(下载工具)、net-tools(网络工具)、curl(HTTP请求工具)等
RUN apt-get update && \\
apt-get install -y wget net-tools curl libfreetype6 fonts-dejavu-core fontconfig && \\
rm -rf /var/lib/apt/lists/* # 清理APT缓存,减少镜像体积

# 创建Jenkins用户(UID=1000,与常见开发环境用户ID一致,避免权限问题)
RUN groupadd –gid 1000 jenkins && \\
useradd -m -u 1000 -g jenkins -d /home/jenkins jenkins && \\
mkdir -p /var/jenkins_home && \\ # 创建Jenkins工作目录
chown -R jenkins:jenkins /var/jenkins_home # 赋予Jenkins用户目录权限

# 将JDK添加到系统PATH,确保java命令可全局调用
ENV PATH="${JAVA_HOME}/bin:${PATH}"
# 设置工作目录为Jenkins用户主目录
WORKDIR /home/jenkins

# 赋予Jenkins用户jenkins.war文件权限(注:WAR包需提前放在构建目录下)
RUN chown jenkins:jenkins jenkins.war

# 按需求使用root用户运行(根据实际权限需求调整,此处为示例配置)
USER root

# 复制启动脚本到容器内
COPY entrypoint.sh /entrypoint.sh
# 赋予启动脚本可执行权限
RUN chmod +x /entrypoint.sh

# 暴露Jenkins Web端口(8080)
EXPOSE 8080
# 容器启动时执行的脚本
ENTRYPOINT ["/entrypoint.sh"]

3. entrypoint.sh内容

该脚本用于启动Jenkins Master,配置了国内插件源、禁用CLI及安全检查,确保启动稳定且加速插件下载:

#!/bin/bash
set -e # 脚本执行过程中遇到错误立即退出

# 指向Jenkins WAR包路径(当前工作目录为/home/jenkins,需确保WAR包在此目录)
JAR="/home/jenkins/jenkins.war"

# 确保Jenkins工作目录权限正确,避免启动时权限不足
chown -R jenkins:jenkins $JENKINS_HOME

# 启动Jenkins Master,关键参数说明:
# -Djenkins.install.runSetupWizard=false:禁用首次启动向导
# -Djenkins.CLI.disabled=true:禁用Jenkins CLI,提升安全性
# -Djenkins.model.Jenkins.pluginsUrl:华为云Jenkins插件源,加速下载
# -Dhudson.model.DownloadService.noSignatureCheck=true:禁用插件签名检查
# –httpPort=8080:Web服务端口
# –argumentsRealm.passwd.jenkins=jenkins:默认管理员账号(jenkins)与密码(jenkins)
exec java \\
-Djenkins.install.runSetupWizard=false \\
-Djenkins.CLI.disabled=true \\
-Djenkins.model.Jenkins.pluginsUrl=https://mirrors.huaweicloud.com/jenkins/latest/plugins/%s.hpi \\
-Djenkins.model.Jenkins.updateCenterUrl=https://mirrors.huaweicloud.com/jenkins/updates/update-center.json \\
-Dhudson.model.DownloadService.noSignatureCheck=true \\
-jar ${JAR} \\
–httpPort=8080 \\
–webroot=$JENKINS_HOME/war \\
–argumentsRealm.passwd.jenkins=jenkins \\
–argumentsRealm.roles.jenkins=admin

4. 镜像创建命令

执行以下命令构建Master镜像,并推送到华为云容器仓库(需提前登录仓库):

docker build -f Dockerfile.master -t swr.cn-east-3.myhuaweicloud.com/bocheng-test/jenkins-master:v5 .

  • -f Dockerfile.master:指定构建使用的Dockerfile
  • -t:为镜像添加标签,格式为「仓库地址/项目名/镜像名:版本」
  • .:构建上下文为当前目录

二、Jenkins Agent 基础镜像制作

Jenkins Agent负责执行具体的构建任务(如拉取代码、编译、部署),需集成JDK、Maven、kubectl、GitLab SSH配置等工具。

1. 目录结构
目录/文件说明
Dockerfile.agent Agent节点镜像构建脚本
jenkins-agent.sh Agent节点启动脚本
jdk-17.0.12/ JDK 17安装目录(本地提前准备)
apache-maven-3.8.8/ Maven 3.8.8目录(Java项目需用,PHP项目可保留)
.kube/ Kubernetes集群配置目录(含config文件,用于连接K8s集群)
kubectl Kubernetes命令行工具(提前下载对应版本)
.ssh/ GitLab SSH密钥目录(含id_rsa_gitlab私钥,用于拉取代码)

执行以下命令可查看目录结构:

root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/jenkins-setup/agent# tree -a -L 2
.
├── agent.jar # 注:无需本地准备,Dockerfile中会自动从Master下载
├── apache-maven-3.8.8 # mvn配置(Java项目需用,PHP项目可保留)
│ ├── bin
│ ├── boot
│ ├── conf
│ ├── lib
│ ├── LICENSE
│ ├── NOTICE
│ └── README.txt
├── Dockerfile.agent
├── jdk-17.0.12 # jdk的bin包
│ ├── bin
│ ├── conf
│ ├── include
│ ├── jmods
│ ├── legal
│ ├── lib
│ ├── LICENSE
│ ├── man
│ ├── README
│ └── release
├── jenkins-agent.sh
├── .kube # 连接k8s集群配置文件
│ ├── cache
│ └── config
├── kubectl # 工具类
└── .ssh # 连接gitlab配置文件
├── authorized_keys
├── config
├── id_rsa_gitlab # GitLab私钥
├── id_rsa_gitlab.pub # GitLab公钥
└── known_hosts # 已知主机列表,避免首次连接确认

2. Dockerfile.agent内容

该文件定义了Agent镜像的基础环境、工具集成及权限配置,关键步骤已添加注释:

# Dockerfile.agent – 完整可运行版本
# 基础镜像:Ubuntu 22.04
FROM ubuntu:22.04
LABEL maintainer="devops@example.com"

# 设置环境变量:Agent工作目录、JDK路径、Maven路径、NVM路径、默认用户
ENV AGENT_WORKDIR=/home/jenkins/agent \\
JAVA_HOME=/data/jdk-17.0.12 \\
MAVEN_HOME=/opt/apache-maven-3.8.8 \\
NVM_DIR=/home/jenkins/.nvm \\
USER=jenkins

# 将Java和Maven添加到系统PATH,确保命令可全局调用
ENV PATH="${JAVA_HOME}/bin:${MAVEN_HOME}/bin:${PATH}"

# 替换Ubuntu软件源为阿里云,加速依赖安装
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \\
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list

# 安装基础工具:curl(HTTP请求)、git(代码拉取)、vim(编辑)、sshpass(SSH密码登录)等
RUN apt-get update && \\
apt-get install -y curl git vim sshpass rsync unzip python3-pip && \\
rm -rf /var/lib/apt/lists/* # 清理APT缓存

# 创建Jenkins用户(与Master保持一致的UID/GID,避免权限问题)
RUN groupadd –gid 1000 jenkins && \\
useradd -m -u 1000 -g jenkins -d /home/jenkins jenkins

# 创建Agent工作目录,并赋予Jenkins用户权限
RUN mkdir -p $AGENT_WORKDIR && \\
chown -R jenkins:jenkins $AGENT_WORKDIR

# 拷贝本地.ssh目录到容器/root/.ssh,用于GitLab SSH免密拉取
COPY .ssh /root/.ssh
# 赋予私钥文件600权限(SSH私钥必须为600权限才能使用)
RUN chmod 600 /root/.ssh/id_rsa_gitlab

# 拷贝本地.kube目录到容器/root/.kube,用于连接K8s集群
COPY .kube /root/.kube
# 拷贝kubectl工具到/usr/bin目录,确保全局可调用
COPY kubectl /usr/bin/kubectl
# 赋予kubectl可执行权限
RUN chmod 755 /usr/bin/kubectl

# 拷贝本地JDK到容器/data目录
COPY jdk-17.0.12 /data/jdk-17.0.12

# 拷贝本地Maven到容器/opt目录
COPY apache-maven-3.8.8 /opt/apache-maven-3.8.8
# 赋予Maven可执行权限,并设置Jenkins用户为所有者
RUN chmod +x /opt/apache-maven-3.8.8/bin/mvn && \\
chown -R jenkins:jenkins /opt/apache-maven-3.8.8

# 拷贝Agent启动脚本到容器/usr/local/bin目录
COPY jenkins-agent.sh /usr/local/bin/jenkins-agent.sh
# 赋予启动脚本可执行权限,并设置Jenkins用户为所有者
RUN chmod +x /usr/local/bin/jenkins-agent.sh && \\
chown jenkins:jenkins /usr/local/bin/jenkins-agent.sh

# 确保/home/jenkins目录下所有文件归属Jenkins用户,避免权限问题
RUN chown -R jenkins:jenkins /home/jenkins

# 切换到Jenkins用户运行(非root用户更安全)
USER jenkins
# 设置工作目录为Agent工作目录
WORKDIR $AGENT_WORKDIR
# 容器启动时执行Agent启动脚本
ENTRYPOINT ["/usr/local/bin/jenkins-agent.sh"]

3. jenkins-agent.sh内容

该脚本用于启动Jenkins Agent,通过JNLP协议连接Master,关键步骤已添加注释:

#!/bin/bash
set -e # 脚本执行过程中遇到错误立即退出

# 检查是否传入JNLP secret和Agent名称(启动Agent必需的两个参数)
if [ -z "$1" ] || [ -z "$2" ]; then
echo "❌ 错误:缺少 JNLP secret 或 agent name"
echo "💡 用法: $0 <secret> <agent-name>"
exit 1
fi

# 设置环境变量:Agent工作目录、JDK路径
export AGENT_WORKDIR="/home/jenkins/agent"
export JAVA_HOME="/data/jdk-17.0.12"
export PATH="$JAVA_HOME/bin:$PATH" # 确保java命令可调用

# 进入Agent工作目录
cd "$AGENT_WORKDIR"
# 从Jenkins Master下载agent.jar(Master地址为集群内Service地址,确保网络可达)
curl -fsSL -o agent.jar http://jenkins-master.jenkins.svc.cluster.local:8080/jnlpJars/agent.jar

# 启动Agent,连接Master,关键参数说明:
# -Duser.home=/home/jenkins:指定用户主目录
# -Djava.awt.headless=true:无GUI模式运行
# -url:Jenkins Master地址(集群内Service地址)
# -webSocket:使用WebSocket协议连接,避免防火墙拦截
# -workDir:Agent工作目录
# "$1" "$2":分别为JNLP secret和Agent名称(从脚本参数传入)
exec java \\
-Duser.home=/home/jenkins \\
-Djava.awt.headless=true \\
-jar ./agent.jar \\
-url http://jenkins-master.jenkins.svc.cluster.local:8080 \\
-webSocket \\
-workDir "$AGENT_WORKDIR" \\
-headless \\
"$1" "$2"

4. 镜像创建命令

执行以下命令构建Agent镜像,并推送到华为云容器仓库(需提前登录仓库):

docker build -f Dockerfile.agent -t swr.cn-east-3.myhuaweicloud.com/bocheng-test/jenkins-agent:v9 .

  • 参数说明与Master镜像构建一致,标签版本为v9(根据实际迭代调整)

三、Jenkins 相关的配置

完成Master与Agent镜像制作后,需通过Kubernetes YAML文件配置Ingress(外部访问)、RBAC权限(连接K8s集群)、存储(数据持久化)及StatefulSet(Master部署)。

1. Jenkins Ingress 配置 (jenkins-ingress.yaml)

该文件配置Ingress规则,实现外部通过域名(jenkins.local)访问Jenkins Master,关键参数已添加注释:

# ~/jenkins-setup/jenkins-ingress.yaml
apiVersion: networking.k8s.io/v1 # Ingress API版本(K8s 1.19+推荐使用v1)
kind: Ingress
metadata:
name: jenkins-ingress # Ingress名称
namespace: jenkins # Ingress所在命名空间(需与Jenkins Master一致)
annotations:
# 移除configuration-snippet(部分Ingress控制器禁用该注解,避免安全风险)
nginx.ingress.kubernetes.io/ssl-redirect: "false" # 禁用HTTPS重定向(测试环境可关闭,生产环境建议开启)
nginx.ingress.kubernetes.io/proxy-body-size: "0" # 禁用请求体大小限制(避免大文件上传失败)
nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600" # 代理连接超时时间(1小时)
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" # 代理发送超时时间(1小时)
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" # 代理读取超时时间(1小时)
nginx.ingress.kubernetes.io/proxy-request-buffering: "off" # 禁用请求缓冲(提升大请求处理效率)
nginx.ingress.kubernetes.io/websocket-services: jenkins-master # 启用WebSocket支持(Jenkins Agent连接需用)
spec:
ingressClassName: nginx # 显式指定Ingress控制器(避免使用默认控制器导致冲突)
rules:
– host: jenkins.local # 访问Jenkins的域名(需在本地DNS或hosts文件配置解析)
http:
paths:
– path: / # 匹配所有路径
pathType: Prefix # 路径匹配类型(前缀匹配)
backend:
service:
name: jenkins-master # 后端Service名称(对应Jenkins Master的Service)
port:
number: 8080 # 后端Service端口(Jenkins Web端口)

2. Jenkins 连接 Kubernetes 集群的权限配置 (rbac.yaml)

该文件配置Service Account、ClusterRole及ClusterRoleBinding,赋予Jenkins操作K8s资源的权限,关键参数已添加注释:

# rbac.yaml
# 1. 创建Service Account(Jenkins使用该账号访问K8s API)
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins # ServiceAccount名称
namespace: jenkins # 所在命名空间(与Jenkins Master一致)

# 2. 创建ClusterRole(定义Jenkins可操作的K8s资源及权限)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: jenkins # ClusterRole名称
rules:
– apiGroups: [""] # 核心API组(对应Pod、Service等资源)
resources: ["pods", "services", "secrets", "configmaps", "events"] # 可操作的资源
verbs: ["get", "list", "watch", "create", "delete", "update"] # 可执行的操作(查询、创建、删除等)
– apiGroups: ["batch"] # Batch API组(对应Job资源)
resources: ["jobs"]
verbs: ["get", "list", "watch", "create", "delete"]
– apiGroups: ["apps"] # Apps API组(对应Deployment、StatefulSet资源)
resources: ["deployments", "statefulsets"]
verbs: ["get", "list", "watch"] # 仅查询权限(避免误删部署)

# 3. 创建ClusterRoleBinding(将ClusterRole绑定到Service Account,赋予权限)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: jenkins # ClusterRoleBinding名称
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins # 绑定的ClusterRole名称
subjects:
– kind: ServiceAccount
name: jenkins # 绑定的ServiceAccount名称
namespace: jenkins # ServiceAccount所在命名空间

3. Jenkins 的落盘存储配置

Jenkins Master需持久化存储工作目录(如插件、任务配置),应用数据需通过NFS存储(支持多节点共享),本部分包含本地PV(Master存储)和NFS PV(应用数据存储)配置。

4.1 Jenkins Master 的落盘存储

该配置使用本地存储(Local PV)持久化Jenkins Master工作目录,仅允许调度到指定节点(k8s-node1),关键参数已添加注释:

# 1. 创建PersistentVolume(PV,提供存储资源)
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv # PV名称
spec:
capacity:
storage: 10Gi # PV容量(根据实际需求调整)
volumeMode: Filesystem # 存储模式(文件系统)
accessModes:
– ReadWriteOnce # 访问模式(仅允许单个节点读写,本地存储仅支持该模式)
persistentVolumeReclaimPolicy: Retain # 回收策略(保留数据,删除PVC后PV仍存在)
storageClassName: local-storage # 存储类名称(需与PVC匹配)
local:
path: /data/jenkins-pv # 本地存储路径(需在k8s-node1节点提前创建该目录)
nodeAffinity: # 节点亲和性(仅允许调度到k8s-node1节点)
required:
nodeSelectorTerms:
– matchExpressions:
– key: kubernetes.io/hostname
operator: In
values:
– k8s-node1 # 目标节点名称

# 2. 创建PersistentVolumeClaim(PVC,申请PV资源)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc # PVC名称(Jenkins Master部署时引用该名称)
namespace: jenkins # 所在命名空间(与Jenkins Master一致)
spec:
accessModes:
– ReadWriteOnce # 访问模式(需与PV一致)
resources:
requests:
storage: 10Gi # 申请的存储容量(需≤PV容量)
storageClassName: local-storage # 存储类名称(需与PV一致)
volumeName: jenkins-pv # 绑定的PV名称(显式绑定,避免自动匹配错误)

4.2 NFS 存储配置 (用于应用数据)

该配置使用NFS存储应用数据(如PHP项目代码),支持多节点共享读写,关键参数已添加注释:

# 1. 创建NFS类型的PersistentVolume(PV)
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-dbb-live-api-test-jenkins # PV名称(避免与其他PV重名)
spec:
capacity:
storage: 10Gi # PV容量(根据应用数据需求调整)
accessModes:
– ReadWriteMany # 访问模式(允许多节点读写,NFS支持该模式)
persistentVolumeReclaimPolicy: Retain # 回收策略(保留数据)
nfs:
server: 192.168.122.189 # NFS服务器地址(需确保K8s集群可访问)
path: /opt/nfs/dbb-live-api-test # NFS共享目录(需在NFS服务器提前创建)
mountOptions: # NFS挂载选项
– nfsvers=4.1 # 使用NFS 4.1版本(稳定性更好)
– hard # 硬挂载(挂载失败时阻塞请求,避免数据丢失)
– intr # 允许中断挂载(避免进程一直阻塞)

# 2. 创建PersistentVolumeClaim(PVC,申请NFS PV资源)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc-dbb-live-api-test # PVC名称(Pipeline中引用该名称)
namespace: jenkins # 所在命名空间(需与Jenkins Agent一致)
spec:
accessModes:
– ReadWriteMany # 访问模式(需与PV一致)
resources:
requests:
storage: 10Gi # 申请的存储容量(需≤PV容量)
volumeName: nfs-pv-dbb-live-api-test-jenkins # 绑定的PV名称(显式绑定)

5. Jenkins 的主YAML文件 (jenkins-main.yaml)

该文件通过StatefulSet部署Jenkins Master,配置了存储挂载、资源限制、节点调度及Service(内部通信与外部访问),关键参数已添加注释:

# 1. 用StatefulSet部署Jenkins Master(StatefulSet适合有状态应用,如需要稳定网络标识的服务)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: jenkins # StatefulSet名称
namespace: jenkins # 所在命名空间
spec:
serviceName: jenkins-headless # Headless Service名称(用于StatefulSet的稳定网络标识)
replicas: 1 # 副本数(Master单副本即可,避免多副本数据冲突)
selector:
matchLabels:
app: jenkins-master # 匹配标签(与Pod模板标签一致)
template:
metadata:
labels:
app: jenkins-master # Pod标签(用于Service和Ingress匹配)
spec:
serviceAccountName: jenkins # 使用的ServiceAccount(对应rbac.yaml中创建的账号)
nodeSelector: # 节点选择器(固定调度到k8s-node1节点,与本地PV节点一致)
kubernetes.io/hostname: k8s-node1
tolerations: # 容忍节点污点(允许调度到Master节点,若k8s-node1是Master节点需配置)
– key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
– key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
volumes: # 卷配置(挂载PVC到Pod)
– name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-pvc # 引用的PVC名称(对应Master存储的PVC)
containers:
– name: jenkins-master # 容器名称
image: swr.cn-east-3.myhuaweicloud.com/bocheng-test/jenkins-master:v5 # 容器镜像(Master镜像)
ports:
– containerPort: 8080
name: http # 端口名称(对应Service端口映射)
– containerPort: 50000
name: jnlp # JNLP端口(Agent连接Master需用)
volumeMounts:
– name: jenkins-home
mountPath: /var/jenkins_home # 挂载路径(Jenkins工作目录,与Dockerfile中配置一致)
resources: # 资源限制(避免Master占用过多资源)
requests: # 最小资源需求
memory: "2Gi"
cpu: "500m"
limits: # 最大资源限制
memory: "4Gi"
cpu: "1000m"

# 2. Headless Service(用于StatefulSet的稳定网络标识,无ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: jenkins-headless # Service名称(与StatefulSet的serviceName一致)
namespace: jenkins
spec:
clusterIP: None # Headless Service标识(无ClusterIP)
selector:
app: jenkins-master # 匹配Pod标签
ports:
– port: 8080
targetPort: 8080
name: http
– port: 5000
targetPort: 50000
name: jnlp

# 3. 外部访问Service(LoadBalancer类型,若无LoadBalancer可改为NodePort)
apiVersion: v1
kind: Service
metadata:
name: jenkins-master # Service名称(Ingress引用该名称)
namespace: jenkins
spec:
type: LoadBalancer # Service类型(LoadBalancer需云服务商支持,本地集群用NodePort)
selector:
app: jenkins-master # 匹配Pod标签
ports:
– name: http
port: 8080 # Service端口
targetPort: 8080 # Pod目标端口
– name: jnlp
port: 50000 # Service端口
targetPort: 50000 # Pod目标端口

6. Jenkins 主界面配置

完成K8s资源部署后,需登录Jenkins Web界面配置Kubernetes云、Pod模板及安装必要插件,确保Agent可动态创建并执行任务。

1. Cloud Kubernetes Configuration

该配置用于Jenkins连接Kubernetes集群,动态创建Agent Pod,界面参数设置如下:

  • 进入Jenkins管理界面 → 系统配置 → 云中,点击「添加Kubernetes云」。
  • 配置以下核心参数(界面截图关键参数说明):
    • Kubernetes名称:自定义名称(如「Kubernetes」)。
    • Kubernetes地址:https://kubernetes.default.svc.cluster.local(K8s集群内API Server地址,默认即可)。
    • 禁用HTTPS证书检查:勾选(测试环境可禁用,生产环境建议配置证书)。
    • Kubernetes命名空间:jenkins(Agent Pod默认创建的命名空间)。
    • 凭据:选择「Secret text」,值为Jenkins ServiceAccount的token(可通过kubectl get secret -n jenkins jenkins-token-xxx -o jsonpath='{.data.token}' | base64 -d获取)。
    • WebSocket:勾选(启用WebSocket协议连接Agent)。
    • Jenkins地址:http://jenkins-master.jenkins.svc.cluster.local:8080(Master在集群内的Service地址)。
    • Connection Timeout:5(连接超时时间,单位秒)。
    • Read Timeout:15(读取超时时间,单位秒)。
    • 容器数量:10(最大并发Agent Pod数量)。
    • Pod Labels:添加标签「键:jenkins,值:jnlp」(用于匹配Agent Pod)。
    • Pod Retention:选择「Never」(任务结束后删除Agent Pod,节省资源)。
  • 2. Pod Template Settings

    该配置定义Agent Pod模板,指定使用自定义的Agent镜像、挂载卷及环境变量,界面参数设置如下:

  • 进入Kubernetes云配置的「Pod模板」部分,点击「添加Pod模板」。
  • 配置以下核心参数(界面截图关键参数说明):
    • 名称:jnlp-slave(Pod模板名称,Pipeline中引用该名称)。
    • 命名空间:jenkins(Pod创建的命名空间)。
    • 标签列表:jnlp-slave(Pipeline通过该标签选择该模板)。
    • 用法:选择「只允许运行绑定到这台机器的Job」(避免其他任务占用)。
    • 容器列表:点击「添加容器模板」,配置如下:
      • 名称:jnlp(容器名称,需与启动脚本匹配)。
      • Docker镜像:swr.cn-east-3.myhuaweicloud.com/bocheng-test/jenkins-agent:v9(自定义的Agent镜像)。
      • 工作目录:/home/jenkins/agent(与Agent镜像中配置的AGENT_WORKDIR一致)。
      • 命令参数:{computer.jnlpmac} {computer.name}(传递JNLP参数给启动脚本)。
    • 环境变量:添加以下环境变量:
      • 键:CONTAINERD_ADDRESS,值:/run/containerd/containerd.sock(容器运行时地址)。
      • 键:CONTAINERD_NAMESPACE,值:default(容器运行时命名空间)。
    • 卷:添加以下卷(确保Agent可访问GitLab和K8s集群):
      • Host Path Volume:主机路径/run/containerd/containerd.sock,挂载路径/run/containerd/containerd.sock,勾选「Read Only」(只读挂载容器运行时 sock 文件)。
      • Host Path Volume:主机路径/etc/localtime,挂载路径/etc/localtime,勾选「Read Only」(同步主机时间)。
      • Persistent Volume Claim:申明值maven-cache-pvc,挂载路径/root/.m2/repository(Maven缓存,Java项目需用)。
      • Persistent Volume Claim:申明值nfs-pvc-dbb-live-api-test,挂载路径/opt/nfs/dbb-live-apitest(NFS存储,应用数据目录)。
    • Raw YAML for the Pod:添加以下YAML(配置容器安全上下文及容忍,允许调度到Master节点):spec:
      containers:
      name: jnlp
      securityContext:
      privileged: true # 特权模式(部分操作需高权限,如挂载卷)
      capabilities:
      add:
      SYS_ADMIN
      SETFCAP
      allowPrivilegeEscalation: true
      volumeMounts:
      name: containersstorage
      mountPath: /var/lib/containers
      name: devmapper
      mountPath: /dev/mapper
      name: containerdsocket
      mountPath: /run/containerd/containerd.sock
      name: localtime
      mountPath: /etc/localtime
      name: mavencache
      mountPath: /root/.m2/repository
      volumes:
      name: containersstorage
      emptyDir: {}
      name: devmapper
      hostPath:
      path: /dev/mapper
      name: containerdsocket
      hostPath:
      path: /run/containerd/containerd.sock
      name: localtime
      hostPath:
      path: /etc/localtime
      name: mavencache
      persistentVolumeClaim:
      claimName: mavencachepvc
      tolerations:
      key: "node-role.kubernetes.io/master"
      operator: "Exists"
      effect: "NoSchedule"
    • Image pull secrets:添加「swr-secret」(华为云容器仓库的拉取密钥,避免镜像拉取失败)。
    • Service Account:选择「jenkins」(使用之前创建的ServiceAccount)。
    • Run As User ID:0(以root用户运行容器,避免权限问题)。
    • Run As Group ID:0(以root组运行容器)。
  • 3. Jenkins 的插件

    Jenkins需安装以下核心插件,确保CI/CD流水线正常运行,插件列表及功能说明如下:

    插件名称功能说明
    Kubernetes Plugin 动态创建Jenkins Agent Pod
    Git Plugin 拉取Git仓库代码
    Pipeline 提供流水线(Pipeline as Code)支持
    Credentials Binding Plugin 绑定凭据(如GitLab私钥、K8s Token)
    Blue Ocean 提供现代化的流水线UI界面,便于查看与调试
    Git Client Plugin Git插件的依赖插件,提供Git客户端功能
    Credentials Plugin 管理Jenkins中的凭据(如密码、密钥)
    Pipeline: Declarative 支持声明式Pipeline语法(更简洁的流水线定义)
    SSH Agent Plugin 提供SSH代理,支持使用SSH密钥拉取代码
    Localization: Chinese (Simplified) Jenkins简体中文语言包,便于中文用户操作

    其他依赖插件(如Apache HttpComponents Client、ASM API等)会自动安装,无需手动操作。

    4. k8s环境对华为云镜像仓库的配置

    K8s集群需配置华为云容器仓库(SWR)的拉取密钥,确保可正常拉取Master和Agent镜像,执行以下命令查看密钥配置:

    # 查看默认命名空间的密钥(包含SWR拉取密钥)
    root@k8s-master:~# kubectl get secret
    NAME TYPE DATA AGE
    swr-registry-secret kubernetes.io/dockerconfigjson 1 4d22h # SWR拉取密钥1
    swr-secret kubernetes.io/dockerconfigjson 1 4d22h # SWR拉取密钥2

    # 查看test命名空间的密钥(用于应用部署)
    root@k8s-master:~# kubectl get secret -n test
    NAME TYPE DATA AGE
    nginx-ssl-liveapi Opaque 2 4d16h # Nginx SSL证书密钥

    # 查看jenkins命名空间的密钥(用于Jenkins Agent拉取镜像)
    root@k8s-master:~# kubectl get secret -n jenkins
    NAME TYPE DATA AGE
    swr-registry-secret kubernetes.io/dockerconfigjson 1 4d22h # SWR拉取密钥1
    swr-secret kubernetes.io/dockerconfigjson 1 4d22h # SWR拉取密钥2

    • 若未创建SWR密钥,可通过kubectl create secret docker-registry swr-secret –docker-server=swr.cn-east-3.myhuaweicloud.com –docker-username=用户名 –docker-password=密码 –namespace=jenkins命令创建。

    第二步:Ingress 配置

    Ingress是K8s集群的入口流量管理组件,本步骤需部署Ingress Nginx控制器、配置SSL证书、NodePort服务及NFS存储(用于Nginx配置文件共享),确保外部可通过域名访问PHP应用。

    1. Ingress Nginx控制器配置 (ingress-nginx-controller.yaml)

    该文件部署Ingress Nginx控制器(官方版本),包含命名空间、ServiceAccount、RBAC权限、Deployment及Webhook配置,关键参数已添加注释:

    # 1. 创建Ingress Nginx专用命名空间
    apiVersion: v1
    kind: Namespace
    metadata:
    labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    name: ingress-nginx # 命名空间名称

    # 2. 创建控制器ServiceAccount(Ingress控制器使用该账号访问K8s API)
    apiVersion: v1
    automountServiceAccountToken: true
    kind: ServiceAccount
    metadata:
    labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2 # Ingress控制器版本
    name: ingress-nginx
    namespace: ingress-nginx

    # 3. 创建Webhook ServiceAccount(用于Ingress资源验证)
    apiVersion: v1
    automountServiceAccountToken: true
    kind: ServiceAccount
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission
    namespace: ingress-nginx

    # 4. 创建控制器Role(命名空间内的权限)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx
    namespace: ingress-nginx
    rules:
    – apiGroups:
    – ""
    resources:
    – namespaces
    verbs:
    – get

    # 5. 创建控制器Role(扩展权限,包含更多资源操作)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx
    namespace: ingress-nginx
    rules:
    – apiGroups:
    – ""
    resources:
    – namespaces
    – configmaps
    – pods
    – secrets
    – endpoints
    verbs:
    – get
    – list
    – watch
    – apiGroups:
    – ""
    resources:
    – services
    verbs:
    – get
    – list
    – watch
    – apiGroups:
    – networking.k8s.io
    resources:
    – ingresses
    verbs:
    – get
    – list
    – watch
    – apiGroups:
    – networking.k8s.io
    resources:
    – ingresses/status
    verbs:
    – update
    – apiGroups:
    – networking.k8s.io
    resources:
    – ingressclasses
    verbs:
    – get
    – list
    – watch
    – apiGroups:
    – coordination.k8s.io
    resourceNames:
    – ingress-nginx-leader
    resources:
    – leases
    verbs:
    – get
    – update
    – apiGroups:
    – coordination.k8s.io
    resources:
    – leases
    verbs:
    – create
    – apiGroups:
    – ""
    resources:
    – events
    verbs:
    – create
    – patch
    – apiGroups:
    – discovery.k8s.io
    resources:
    – endpointslices
    verbs:
    – list
    – watch
    – get

    # 6. 创建Webhook Role(Webhook的命名空间内权限)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission
    namespace: ingress-nginx
    rules:
    – apiGroups:
    – ""
    resources:
    – secrets
    verbs:
    – get
    – create

    # 7. 创建控制器RoleBinding(绑定Role到ServiceAccount)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx
    namespace: ingress-nginx
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: Role
    name: ingress-nginx
    subjects:
    – kind: ServiceAccount
    name: ingress-nginx
    namespace: ingress-nginx

    # 8. 创建Webhook RoleBinding(绑定Webhook Role到ServiceAccount)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission
    namespace: ingress-nginx
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: Role
    name: ingress-nginx-admission
    subjects:
    – kind: ServiceAccount
    name: ingress-nginx-admission
    namespace: ingress-nginx

    # 9. 创建控制器ClusterRole(集群级别的权限)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx
    rules:
    – apiGroups:
    – ""
    resources:
    – configmaps
    – endpoints
    – nodes
    – pods
    – secrets
    – namespaces
    verbs:
    – list
    – watch
    – apiGroups:
    – coordination.k8s.io
    resources:
    – leases
    verbs:
    – list
    – watch
    – apiGroups:
    – ""
    resources:
    – nodes
    verbs:
    – get
    – apiGroups:
    – ""
    resources:
    – services
    verbs:
    – get
    – list
    – watch
    – apiGroups:
    – networking.k8s.io
    resources:
    – ingresses
    verbs:
    – get
    – list
    – watch
    – apiGroups:
    – ""
    resources:
    – events
    verbs:
    – create
    – patch
    – apiGroups:
    – networking.k8s.io
    resources:
    – ingresses/status
    verbs:
    – update
    – apiGroups:
    – networking.k8s.io
    resources:
    – ingressclasses
    verbs:
    – get
    – list
    – watch
    – apiGroups:
    – discovery.k8s.io
    resources:
    – endpointslices
    verbs:
    – list
    – watch
    – get

    # 10. 创建Webhook ClusterRole(Webhook的集群级别权限)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission
    rules:
    – apiGroups:
    – admissionregistration.k8s.io
    resources:
    – validatingwebhookconfigurations
    verbs:
    – get
    – update

    # 11. 创建控制器ClusterRoleBinding(绑定ClusterRole到ServiceAccount)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: ingress-nginx
    subjects:
    – kind: ServiceAccount
    name: ingress-nginx
    namespace: ingress-nginx

    # 12. 创建Webhook ClusterRoleBinding(绑定Webhook ClusterRole到ServiceAccount)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: ingress-nginx-admission
    subjects:
    – kind: ServiceAccount
    name: ingress-nginx-admission
    namespace: ingress-nginx

    # 13. 部署Ingress控制器Deployment
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-controller
    namespace: ingress-nginx
    spec:
    minReadySeconds: 0 # 就绪探针成功后立即认为可用
    revisionHistoryLimit: 10 # 保留10个历史版本,便于回滚
    selector:
    matchLabels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    strategy:
    rollingUpdate:
    maxUnavailable: 1 # 滚动更新时最大不可用副本数
    type: RollingUpdate # 滚动更新策略
    template:
    metadata:
    labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    spec:
    containers:
    – args: # 控制器启动参数
    – /nginx-ingress-controller
    – –election-id=ingress-nginx-leader # leader选举ID(避免多控制器冲突)
    – –controller-class=k8s.io/ingress-nginx # 控制器类(与Ingress的ingressClassName匹配)
    – –configmap=$(POD_NAMESPACE)/ingress-nginx-controller # 配置文件CM
    – –validating-webhook=:8443 # Webhook端口
    – –validating-webhook-certificate=/usr/local/certificates/cert # Webhook证书
    – –validating-webhook-key=/usr/local/certificates/key # Webhook密钥
    env:
    – name: POD_NAME
    valueFrom:
    fieldRef:
    fieldPath: metadata.name # 注入Pod名称
    – name: POD_NAMESPACE
    valueFrom:
    fieldRef:
    fieldPath: metadata.namespace # 注入Pod命名空间
    – name: LD_PRELOAD
    value: /usr/local/lib/libmimalloc.so # 内存分配优化库
    image: registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.13.2 # 控制器镜像(阿里云镜像,加速下载)
    lifecycle:
    preStop: # 停止前执行的命令(优雅关闭)
    exec:
    command:
    – /wait-shutdown
    livenessProbe: # 存活探针(检测控制器是否存活)
    failureThreshold: 5 # 失败5次认为容器不可用
    httpGet:
    path: /healthz
    port: 10254
    scheme: HTTP
    initialDelaySeconds: 10 # 启动10秒后开始探测
    periodSeconds: 10 # 每10秒探测一次
    successThreshold: 1 # 成功1次认为存活
    timeoutSeconds: 1 # 探测超时时间1秒
    name: controller
    ports:
    – containerPort: 80
    hostPort: 80 # 主机端口(直接使用主机80端口,外部可直接访问)
    name: http
    protocol: TCP
    – containerPort: 443
    hostPort: 443 # 主机端口(直接使用主机443端口)
    name: https
    protocol: TCP
    – containerPort: 8443
    name: webhook
    protocol: TCP
    readinessProbe: # 就绪探针(检测控制器是否就绪)
    failureThreshold: 3 # 失败3次认为未就绪
    httpGet:
    path: /healthz
    port: 10254
    scheme: HTTP
    initialDelaySeconds: 10
    periodSeconds: 10
    successThreshold: 1
    timeoutSeconds: 1
    resources: # 资源限制
    requests:
    cpu: 100m
    memory: 90Mi
    securityContext: # 安全上下文
    allowPrivilegeEscalation: true # 允许权限提升(需绑定主机端口)
    capabilities:
    add:
    – NET_BIND_SERVICE # 添加绑定服务端口的能力
    drop:
    – ALL # 删除其他所有能力
    runAsUser: 101 # 运行用户ID(nginx用户)
    seccompProfile:
    type: RuntimeDefault # 使用默认的seccomp配置
    volumeMounts:
    – mountPath: /usr/local/certificates/
    name: webhook-cert # 挂载Webhook证书卷
    readOnly: true
    dnsPolicy: ClusterFirst # DNS策略(优先使用集群DNS)
    nodeSelector:
    kubernetes.io/os: linux # 仅调度到Linux节点
    serviceAccountName: ingress-nginx # 使用的ServiceAccount
    terminationGracePeriodSeconds: 300 # 优雅关闭时间(5分钟)
    volumes:
    – name: webhook-cert
    secret:
    secretName: ingress-nginx-admission # 引用Webhook证书密钥

    # 14. 创建Webhook证书生成Job(生成Webhook所需的证书)
    apiVersion: batch/v1
    kind: Job
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission-create
    namespace: ingress-nginx
    spec:
    template:
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    spec:
    containers:
    – args:
    – create
    – –host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc # Webhook主机名
    – –namespace=$(POD_NAMESPACE) # 命名空间
    – –secret-name=ingress-nginx-admission # 证书密钥名称
    env:
    – name: POD_NAMESPACE
    valueFrom:
    fieldRef:
    fieldPath: metadata.namespace
    image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.3.0 # 证书生成镜像
    name: create
    securityContext:
    allowPrivilegeEscalation: false
    nodeSelector:
    kubernetes.io/os: linux
    restartPolicy: OnFailure # 失败时重启
    securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 2000
    serviceAccountName: ingress-nginx-admission

    # 15. 创建Webhook证书补丁Job(更新Webhook配置中的证书)
    apiVersion: batch/v1
    kind: Job
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission-patch
    namespace: ingress-nginx
    spec:
    template:
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    spec:
    containers:
    – args:
    – patch
    – –webhook-name=ingress-nginx-admission # Webhook名称
    – –namespace=$(POD_NAMESPACE) # 命名空间
    – –patch-mutating=false # 不启用mutating webhook
    – –secret-name=ingress-nginx-admission # 证书密钥名称
    – –patch-failure-policy=Fail # 补丁失败时失败
    env:
    – name: POD_NAMESPACE
    valueFrom:
    fieldRef:
    fieldPath: metadata.namespace
    image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.3.0 # 证书补丁镜像
    name: patch
    securityContext:
    allowPrivilegeEscalation: false
    nodeSelector:
    kubernetes.io/os: linux
    restartPolicy: OnFailure
    securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 2000
    serviceAccountName: ingress-nginx-admission

    # 16. 创建IngressClass(定义Ingress控制器的类,供Ingress引用)
    apiVersion: networking.k8s.io/v1
    kind: IngressClass
    metadata:
    labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: nginx # IngressClass名称(与Jenkins Ingress的ingressClassName一致)
    spec:
    controller: k8s.io/ingress-nginx # 关联的Ingress控制器

    # 17. 创建ValidatingWebhookConfiguration(用于验证Ingress资源配置)
    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingWebhookConfiguration
    metadata:
    labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.2
    name: ingress-nginx-admission
    webhooks:
    – admissionReviewVersions:
    – v1 # 支持的AdmissionReview版本
    clientConfig:
    service:
    name: ingress-nginx-controller-admission # Webhook服务名称
    namespace: ingress-nginx # Webhook服务命名空间
    path: /networking/v1/ingresses # Webhook路径
    port: 443 # Webhook端口
    failurePolicy: Fail # Webhook失败时,Ingress创建失败
    matchPolicy: Equivalent # 匹配策略(等效匹配)
    name: validate.nginx.ingress.kubernetes.io # Webhook名称
    rules:
    – apiGroups:
    – networking.k8s.io
    apiVersions:
    – v1
    operations:
    – CREATE
    – UPDATE
    resources:
    – ingresses
    sideEffects: None # 无副作用(Webhook不修改资源)

    2. 证书的配置

    该文件创建Secret存储Nginx SSL证书(doubbjt.com域名),用于HTTPS访问,关键参数已添加注释:

    root@k8s-master:/data/service/double_nginx/2025-10-10-v3/ingress# cat secret-ssl.yaml
    apiVersion: v1
    data:
    # SSL私钥(base64编码,需替换为实际的私钥内容)
    doubbjt.com.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBNXdUQUNNVnBkTVZXbWZFQzhVUG1FR0Yyc3ZTZzVCZ0JIUmJXRGp6RnNtaHUxYjlSCjdPQ0V3WmE0WnAzQ2k2MHk5VjNKdDJSM2t1SUUzUEFkZlhENjUzSkhIUTM5Ymt0M3pvVkhIbm5rTjRMK3U0S2EKNnNqRThUY0EvQzVjL1NKWDZxbzFHdVBsVGFaVnZ3TFRGdVpNZEpPQUVIVi9pb200M1NyeUE4S1pia2NZRDNzdQovdEx0UUlHcWJ4OWpFR0NtVGlDN0FaTkk5bjg1Y1BTcFg1dzB6Ty9VVDFrUzBTNDBleTVXSGJCbWdtblNibEkzCmdxQlpUdytTV2tleXFhNElsRU5tRldOdFEwc2tIeVlXbEZmZHk5YUM0ajRTZVJrbW5HdzVHYzErMTdnNW1CUGsKaUJpU0hWMUlha0w4WnZwa1NMVXNpZlhVMGJ4Rkpyd284a0lVTlFJREFRQUJBb0lCQVFDTVRXdzdaM21HZTVyKwozMHJFTUZKM1BGb1VrVkZ0ZGM2blZna21tRzRRempBU0RLRzYxUmlMWWZnWUNsUHU5SDl3V1VkNjQ3OWpLM1Y4Cm9TK24wWWZ3TEkxWDdma3VsUkxzRURHREp0QWRqcWNhUmxYRHBhOUdOL3VYTEtrNDZ6U3R4alYrWVU0UC9LY3kKM0lwSzdBOGU5YjNmUVJ6MmpISURrR052T1M1cHRBZzNYS0xEazZKOFQ4SW5aZVNKRkZ2NXhOcWlHZFBpd21yWgpDTGtJYzgxdHlZZlI0dEtXeHNuQ0NXYmgrUFFQVkltRGpZYjhxNzdEMlRCTlR3NmZYTUZJRUh2bU1MQTdSTkNICmtCUUJldUhpaCtoVWhEaDZkU0JtMU5ydi9vN3FiTWJmQlFSb0xtbnNaUG0vQVh3SG11b21EU01LaHNIWDVUUm4KRlNraFpDRUJBb0dCQVBxMzBpam9vTVVWTitDN1M3WXQxaUZqRVl6MEVvYW84czd2SXdnZ29ocUVHV2JiV1R1bgp5V21CWTFmakVCTUk2YkQzc3VWYnV6aFJrMCt2NVF1TzJmb05sRlV0TWZPdlZQQS9aRGZLZUNDR0JKdGpUOG9TClBHRDJRekk5LytPTGpHNXJiUzVxcFpuQUxiNXFjN2VxRmJDNWRObU85bzJsbXVUN1IwL1Y1bGVWQW9HQkFPdmkKcjNSdUVLd2dBVll4SzVMQXB3U3hPQWhIWXZRMTBDVlViUHZxSENRR1kvdGh2ZDlid2s3d1d6Mk1USDVJZVNQcgpkTmR4cmdMN1dhNlFHSndoSDJoZVp4Z1JWeU1WT05uQzl1ajBKck1RRUdDclBodS9aSXJwN2s0NzBpUkRmWFR3CkNjM1FxUm1QV1NieTlycEVLV1Vmbi9pdCs5QzRMdTZiZlM2d2N5SWhBb0dBS1JZK3hwMTQyMU5pMk4xWXh2NHoKL1R5eS81cHYvc2xwSVNobllVbVh4eWltMFFoWlUwRm43SFpieFdkY2x3SDY2aGx0bzlDb21jNGtpZ2FWVHBPVwpmVGdFZXdEeEVySkZCU29lVGxaTDRXaVZFTEh4dlhmSGxVeEdMdEdpUWJQNHk0K25VTmdCVTNUL0NobC81VFZtCllKUEQ1bk5iVEZNN1V4TXEySmFCTFJFQ2dZQng0MTdISGlVYWxkRlBaaW9kanJxVS8yT0lTNzh4SFpKRGs4R1MKMTNPQ3lUc1lzaFpVY3g4YnNkeTdtTE9OL1R2UDVlemg0RmNkeFhHRllMa2hHTFRyWDF5Vk9Eb1VERGtJbXFOUAowaUdsTHNwWmdoUlluVTNUN0NKTXo0RWtWUm1MamlHTnA0TWRmaUtUTlpkK1UwNlJMblcwUVh4bFI2Z1JPQzEyCmoyYm5RUUtCZ1FDU1BlWlF0WTBTR0FoSVpRaExKTHR6eUwzWGdFNUxSN0hHVU9TTnBpSzRSZUZhTXNYbC85ZlIKbGtaT2grRlRHSWdUVVJ3WmI4LzcyYllVa3VVOTZiaFNUcjdvNXNTYW5QUmZUTXVBdEdSeVR5WWptTVp6Y3I1TApaL0o2aTg2a3k5NEF4RVNqTTk1bTFETGo0Q29CSG4xSm81akNtQmNlc2t3YVpjVThZS1ZIT2c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQ==
    # SSL证书(base64编码,需替换为实际的证书内容)
    doubbjt.com.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUdvekNDQll1Z0F3SUJBZ0lRRUZ4eHd2YXRMUk02L1JZNFpoa3BIVEFOQmdrcWhraUc5dzBCQVFzRkFEQmoKTVFzd0NRWURWUVFHRXdKRFRqRTJNRFFHQTFVRUNnd3RRbVZwYW1sdVp5QllhVzVqYUdGamFHRWdRM0psWkdsMApJRTFoYm1GblpXMWxiblFnUTI4dUxDQk1kR1F1TVJ3d0dnWURWUVFEREJOWVkyTWdWSEoxYzNRZ1QxWWdVMU5NCklFTkJNQjRYRFRJMU1EUXdNVEUwTURFd01Gb1hEVEkyTURRd01URTBNREExT1Zvd2ZqRUxNQWtHQTFVRUJoTUMKUTA0eEVqQVFCZ05WQkFnTUNlYTFtZWF4bitlY2dURVNNQkFHQTFVRUJ3d0o1cDJ0NWJlZTViaUNNUzh3TFFZRApWUVFLRENibWlwYm5pTGpuaUxnbzVyV1o1ckdmS2VTNmt1aUJsT2U5a2VhY2llbVprT1dGck9XUHVERVdNQlFHCkExVUVBd3dOS2k1a2IzVmlZbXAwTG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0MKZ2dFQkFPY0V3QWpGYVhURlZwbnhBdkZENWhCaGRyTDBvT1FZQVIwVzFnNDh4YkpvYnRXL1
    # PHP项目Kubernetes部署与Jenkins CI/CD流水线
    (接上部分证书配置内容)
    ```bash
    Vlemdo1cHRBZzNYS0xEazZKOFQ4SW5aZVNKRkZ2NXhOcWlHZFBpd21yWgpDTGtJYzgxdHlZZlI0dEtXeHNuQ0NXYmgrUFFQVkltRGpZYjhxNzdEMlRCTlR3NmZYTUZJRUh2bU1MQTdSTkNICmtCUUJldUhpaCtoVWhEaDZkU0JtMU5ydi9vN3FiTWJmQlFSb0xtbnNaUG0vQVh3SG11b21EU01LaHNIWDVUUm4KRlNraFpDRUJBb0dCQVBxMzBpam9vTVVWTitDN1M3WXQxaUZqRVl6MEVvYW84czd2SXdnZ29ocUVHV2JiV1R1bgp5V21CWTFmakVCTUk2YkQzc3VWYnV6aFJrMCt2NVF1TzJmb05sRlV0TWZPdlZQQS9aRGZLZUNDR0JKdGpUOG9TClBHRDJRekk5LytPTGpHNXJiUzVxcFpuQUxiNXFjN2VxRmJDNWRObU85bzJsbXVUN1IwL1Y1bGVWQW9HQkFPdmkKcjNSdUVLd2dBVll4SzVMQXB3U3hPQWhIWXZRMTBDVlViUHZxSENRR1kvdGh2ZDlid2s3d1d6Mk1USDVJZVNQcgpkTmR4cmdMN1dhNlFHSndoSDJoZVp4Z1JWeU1WT05uQzl1ajBKck1RRUdDclBodS9aSXJwN2s0NzBpUkRmWFR3CkNjM1FxUm1QV1NieTlycEVLV1Vmbi9pdCs5QzRMdTZiZlM2d2N5SWhBb0dBS1JZK3hwMTQyMU5pMk4xWXh2NHoKL1R5eS81cHYvc2xwSVNobllVbVh4eWltMFFoWlUwRm43SFpieFdkY2x3SDY2aGx0bzlDb21jNGtpZ2FWVHBPVwpmVGdFZXdEeEVySkZCU29lVGxaTDRXaVZFTEh4dlhmSGxVeEdMdEdpUWJQNHk0K25VTmdCVTNUL0NobC81VFZtCllKUEQ1bk5iVEZNN1V4TXEySmFCTFJFQ2dZQng0MTdISGlVYWxkRlBaaW9kanJxVS8yT0lTNzh4SFpKRGs4R1MKMTNPQ3lUc1lzaFpVY3g4YnNkeTdtTE9OL1R2UDVlemg0RmNkeFhHRllMa2hHTFRyWDF5Vk9Eb1VERGtJbXFOUAowaUdsTHNwWmdoUlluVTNUN0NKTXo0RWtWUm1MamlHTnA0TWRmaUtUTlpkK1UwNlJMblcwUVh4bFI2Z1JPQzEyCmoyYm5RUUtCZ1FDU1BlWlF0WTBTR0FoSVpRaExKTHR6eUwzWGdFNUxSN0hHVU9TTnBpSzRSZUZhTXNYbC85ZlIKbGtaT2grRlRHSWdUVVJ3WmI4LzcyYllVa3VVOTZiaFNUcjdvNXNTYW5QUmZUTXVBdEdSeVR5WWptTVp6Y3I1TApaL0o2aTg2a3k5NEF4RVNqTTk1bTFETGo0Q29CSG4xSm81akNtQmNlc2t3YVpjVThZS1ZIT2c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlFenpDQ0E3ZWdBd0lCQWdJUkFQSkVDQzJycVEybGpITHA4cHFUUUs0d0RRWUpLb1pJaHZjTkFRRUxCUUF3CmZqRUxNQWtHQTFVRUJoTUNVRXd4SWpBZ0JnTlZCQW9UR1ZWdWFYcGxkRzhnVkdWamFHNXZiRzluYVdWeklGTXUKUVM0eEp6QWxCZ05WQkFzVEhrTmxjblIxYlNCRFpYSjBhV1pwWTJGMGFXOXVJRUYxZEdodmNtbDBlVEVpTUNBRwpBMVVFQXhNWlEyVnlkSFZ0SUZSeWRYTjBaV1FnVG1WMGQyOXlheUJEUVRBZUZ3MHlNakEzTURFd056UTRORGRhCkZ3MHlOekEyTXpBd056UTRORGRhTUdNeEN6QUpCZ05WQkFZVEFrTk9NVFl3TkFZRFZRUUtEQzFDWldscWFXNW4KSUZocGJtTm9ZV05vWVNCRGNtVmthWFFnVFdGdVlXZGxiV1Z1ZENCRGJ5NHNJRXgwWkM0eEhEQWFCZ05WQkFNTQpFMWhqWXlCVWNuVnpkQ0JQVmlCVFUwd2dRMEV3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUN6Mm54U1BFc01lUkJkdW5mK0taOENySklqOURVV0dWdTA1V1d5RVMrUWtNSHpWSENadDhjQjJCbHAKcXpISHFGc1hTREhWemNNeERlWEJidWRtUXdZYmc1TnBTd1k5dkpIUFRsNnZsQWxLNFVXTjQrb1BUZ1A5Ly9STgpONktMbUsza2paMlpjOUYza0ZLVmF6aVN3Vk5qVnZuZWFjb1VuejZibHdMTEJjNVFlbFE4b0RUMGVFdjBRQkFZCkkwVDhwSERnY3ZXUXFUQmFSck9iODBwbERoWWp1NDY0WGJWaHRPSHZGQ2lCSWtLbmZVQm5CZnB0V2pHTThLaXMKc3E0cjhZWDM1Qi9waW9qOWcxWVgzNE1MdCs1TDN2dldYRWIyYU1pd3dCMVozYlNlZWlVM044YU11U2I5RTlqcAp6ZDB1aHpSYW1lVTdqQUlBcjh1dXVuYjdHVUo3QWdNQkFBR2pnZ0ZoTUlJQlhUQVNCZ05WSFJNQkFmOEVDREFHCkFRSC9BZ0VBTUIwR0ExVWREZ1FXQkJUNm9NdkN4MTJCdFNDU0J5QUx0anR3T3drT1ZUQWZCZ05WSFNNRUdEQVcKZ0JRSWRzM0xCLzhrOXNYTjdidVF2T0tFTjBaMTl6QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd0lCRmhsb2RIUndjem92TDNkM2R5NWpaWEowZFcwdWNHd3ZRMUJUTUIwRwpBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFPQmdOVkhROEJBZjhFQkFNQ0JhQXdKUVlEClZSMFJCQjR3SElJTktpNWtiM1ZpWW1wMExtTnZiWUlMWkc5MVltSnFkQzVqYjIwd2dnRjhCZ29yQmdFRUFkWjUKQWdRQ0JJSUJiQVNDQVdnQlpnQjFBQm1HMU1jb3FtLyt1Z052ZUNwTkFaR3F6aTF5TVErdXpsMXdRUzBsVE1mVQpBQUFCbGZHb0Jxa0FBQVFEQUVZd1JBSWdjZitrUXV5L2ZkYXgzVkFsWHdPeGR5QnZxNmVJWGNLeHNZcTBZNGxZClUva0NJQ09KQ3NsTnFwUElwRkJTVVljTnFnQlF5ZkVOTnAxZ1Q0TnBYNVVpRW5ZK0FIVUFaQkhFYktRUzdLZUoKSEtJQ0xnQzhxMDhvQjlRZU5TZXI2djdWQThsOXpmQUFBQUdWOGFnR3d3QUFCQU1BUmpCRUFpQnNENGVBR3MrOQpjSVVmOVJ6RnlZMGp4eFI4a0JVSGt4R0pNTE5WRFBOb093SWdXY1Y5ejRQSGpGZWZFaUtQM1p6c283eDdqMTB6CmhUemdkMEVKWjU3alJBQUFkZ0FPVjVTODg2NnBQak1iTEprSHMvZVEzNXZDUFhFeUpkMGhxU1dzWWNWT0lRQUEKQVpYeHFBYXJBQUFFQXdCSE1FVUNJUURxaGNkNVZBWlhRMWsrd3VFREFOcDV0YjM1NmpPcW8wR0orMVpZajNXZgpKQUlnUWEwam5pcGgyN29mZ29WK0FmYVovaVQwTHVpUlNQOGh5ZFR4enh3QUUzNHdEUVlKS29aSWh2Y05BUUVMCkJRQURnZ0VCQUplOFcrU0UrMU5lRDFFN0RQQ2UvSUZkRzJxMjQwYmM4OVVtUGVpa2J6M1dXYU93ZWs0OEtrMVMKVlVjblNYM0VacVhqMENRcUgwRG14YlBSNXF6cFh6OXcyOWJBNzNzOC9jR3FUWEVSVUpkZlowcWNCR2lsSTFXUgoxZkh4SDdqZW00MHBxV0lPUzlKaVVxaDcwb3V0cjhjWk1vSjFvN0w0ZG1MNXFXalEzMW9PZ21DWEdJbm1IOU5mCk1rRk1YZ2JyNkliVEU0bnk1b2VFQllJeExOUVV2NGFwTTUvOVN2azVKU0lNMktQdDRqMENMcklKemM3VlhSdHoKWTcrU05qQWpuMlg4MnFUeXFyZWFoMVR5Z2xtNTE4MXZOVFFxa2NLTk9DN2RmQm9NS2R5TnZIQ2htTWNaV0V5WQpRdVNhaVYrRDVNR045aDBJL0I0L0Vqc2paSEZ6ZnhVPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlEdXpDQ0FxT2dBd0lCQWdJREJFVEFNQTBHQ1NxR1NJYjNEUUVCQlFVQU1INHhDekFKQmdOVkJBWVRBbEJNCk1TSXdJQVlEVlFRS0V4bFZibWw2WlhSdklGUmxZMmh1YjJ4dloybGxjeUJUTGtFdU1TY3dKUVlEVlFRTEV4NUQKWlhKMGRXMGdRMlZ5ZEdsbWFXTmhkR2x2YmlCQmRYUm9iM0pwZEhreElqQWdCZ05WQkFNVEdVTmxjblIxYlNCVQpjblZ6ZEdWa0lFNWxkSGR2Y21zZ1EwRXdIaGNOTURneE1ESXlNVEl3TnpNM1doY05Namt4TWpNeE1USXdOek0zCldqQitNUXN3Q1FZRFZRUUdFd0pRVERFaU1DQUdBMVVFQ2hNWlZXNXBlbVYwYnlCVVpXTm9ibTlzYjJkcFpYTWcKVXk1QkxqRW5NQ1VHQTFVRUN4TWVRMlZ5ZEhWdElFTmxjblJwWm1sallYUnBiMjRnUVhWMGFHOXlhWFI1TVNJdwpJQVlEVlFRREV4bERaWEowZFcwZ1ZISjFjM1JsWkNCT1pYUjNiM0pySUVOQk1JSUJJakFOQmdrcWhraUc5dzBCCkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTQvdDlvM0s2d3ZESkZJZjFhd0ZPNFc1QUI3cHRKMTEvOTFzdHMxckgKVVYrcnBES21ZWWUyYmcrRzBqQUNsL2pYYVZlaEdEbGRhbVI1eGdGWnJEd3hTamg4MGdUU1N5am9JRjg3QjZMTQpUWFBiODY1UHgxYlZXcWVXaWZyenEyalVJNFpaSjg4Sko3eXNibktESERCeTMrQ2k2ZExoZEhVWnZTcWVleFZVCkJCdlhRem10VlNqRjRocTc5TURrcmpoSk04eDJoWjg1UmRLa252SVNqRkg0Zk9RdGYvV3NYK3NXbjdFdDBick0Ka1VKM1RDWEprRGh2Mi9ETSs0NGVsMWsrMVdCTzVnVW83VWw1RTB1NlNOc3YrWExUT2NyK0g5ZzBjdlcwUU04eApBY1BzM2hFdEYxMGZ1RkRSWGhtbmFkNEhNeWpLVUpYNXAxVExWSVpRUmFuNVNRSURBUUFCbzBJd1FEQVBCZ05WCkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlFJZHMzTEIvOGs5c1hON2J1UXZPS0VOMFoxOXpBT0JnTlYKSFE4QkFmOEVCQU1DQVFZd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ2dFQkFLYW9yU0xPQVQybW8vOWkwRWlkaTE1eQpzSGhFNDl3Y3J3bjlJMGo2dlNyRXVWVUV0UkNqalNmZUM0SmowTzdlRERkNVFWc2lzckNhUVZ5bWNPRFUwSGZMCkk5TUE0R3hXTCtGcERRM1pxcjhoZ1ZEWkJxV28vNVUzMEtyKzRyUDFtUzFGaElybFFnblhkQUl2OTRuWW1lbTgKSjlSSGpib05SaHgzenhTa0hMbWtNY1NjS0hRRE5QOHpHU2FsNlExMHR6Nlh4bmJvSjVhalp0M2hydkpCVzhxWQpWb056Y09TR0d0SXhRYm92dmkwVFduWnZUdWhPZ1E0L1d3TWlvQksrWmxnUlNzc0R4TFFxS2kyV0YrQTVWTHhJCjAzWW5uWm90QnFiSjdEblNxOXVmbWdzbkFqVXBzVUNWNS9ub25GV0lHVWJXdHpUMWZzNDVtdGs0OFZIM1R5dz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    kind: Secret
    metadata:
    annotations:
    # 记录kubectl最后一次应用配置的内容(自动生成,无需手动修改)
    kubectl.kubernetes.io/last-applied-configuration: |
    {"apiVersion":"v1","data":{"doubbjt.com.key":"LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBNXdUQUNNVnBkTVZXbWZFQzhVUG1FR0Yyc3ZTZzVCZ0JIUmJXRGp6RnNtaHUxYjlSCjdPQ0V3WmE0WnAzQ2k2MHk5VjNKdDJSM2t1SUUzUEFkZlhENjUzSkhIUTM5Ymt0M3pvVkhIbm5rTjRMK3U0S2EKNnNqRThUY0EvQzVjL1NKWDZxbzFHdVBsVGFaVnZ3TFRGdVpNZEpPQUVIVi9pb200M1NyeUE4S1pia2NZRDNzdQovdEx0UUlHcWJ4OWpFR0NtVGlDN0FaTkk5bjg1Y1BTcFg1dzB6Ty9VVDFrUzBTNDBleTVXSGJCbWdtblNibEkzCmdxQlpUdytTV2tleXFhNElsRU5tRldOdFEwc2tIeVlXbEZmZHk5YUM0ajRTZVJrbW5HdzVHYzErMTdnNW1CUGsKaUJpU0hWMUlha0w4WnZwa1NMVXNpZlhVMGJ4Rkpyd284a0lVTlFJREFRQUJBb0lCQVFDTVRXdzdaM21HZTVyKwozMHJFTUZKM1BGb1VrVkZ0ZGM2blZna21tRzRRempBU0RLRzYxUmlMWWZnWUNsUHU5SDl3V1VkNjQ3OWpLM1Y4Cm9TK24wWWZ3TEkxWDdma3VsUkxzRURHREp0QWRqcWNhUmxYRHBhOUdOL3VYTEtrNDZ6U3R4alYrWVU0UC9LY3kKM0lwSzdBOGU5YjNmUVJ6MmpISURrR052T1M1cHRBZzNYS0xEazZKOFQ4SW5aZVNKRkZ2NXhOcWlHZFBpd21yWgpDTGtJYzgxdHlZZlI0dEtXeHNuQ0NXYmgrUFFQVkltRGpZYjhxNzdEMlRCTlR3NmZYTUZJRUh2bU1MQTdSTkNICmtCUUJldUhpaCtoVWhEaDZkU0JtMU5ydi9vN3FiTWJmQlFSb0xtbnNaUG0vQVh3SG11b21EU01LaHNIWDVUUm4KRlNraFpDRUJBb0dCQVBxMzBpam9vTVVWTitDN1M3WXQxaUZqRVl6MEVvYW84czd2SXdnZ29ocUVHV2JiV1R1bgp5V21CWTFmakVCTUk2YkQzc3VWYnV6aFJrMCt2NVF1TzJmb05sRlV0TWZPdlZQQS9aRGZLZUNDR0JKdGpUOG9TClBHRDJRekk5LytPTGpHNXJiUzVxcFpuQUxiNXFjN2VxRmJDNWRObU85bzJsbXVUN1IwL1Y1bGVWQW9HQkFPdmkKcjNSdUVLd2dBVll4SzVMQXB3U3hPQWhIWXZRMTBDVlViUHZxSENRR1kvdGh2ZDlid2s3d1d6Mk1USDVJZVNQcgpkTmR4cmdMN1dhNlFHSndoSDJoZVp4Z1JWeU1WT05uQzl1ajBKck1RRUdDclBodS9aSXJwN2s0NzBpUkRmWFR3CkNjM1FxUm1QV1NieTlycEVLV1Vmbi9pdCs5QzRMdTZiZlM2d2N5SWhBb0dBS1JZK3hwMTQyMU5pMk4xWXh2NHoKL1R5eS81cHYvc2xwSVNobllVbVh4eWltMFFoWlUwRm43SFpieFdkY2x3SDY2aGx0bzlDb21jNGtpZ2FWVHBPVwpmVGdFZXdEeEVySkZCU29lVGxaTDRXaVZFTEh4dlhmSGxVeEdMdEdpUWJQNHk0K25VTmdCVTNUL0NobC81VFZtCllKUEQ1bk5iVEZNN1V4TXEySmFCTFJFQ2dZQng0MTdISGlVYWxkRlBaaW9kanJxVS8yT0lTNzh4SFpKRGs4R1MKMTNPQ3lUc1lzaFpVY3g4YnNkeTdtTE9OL1R2UDVlemg0RmNkeFhHRllMa2hHTFRyWDF5Vk9Eb1VERGtJbXFOUAowaUdsTHNwWmdoUlluVTNUN0NKTXo0RWtWUm1MamlHTnA0TWRmaUtUTlpkK1UwNlJMblcwUVh4bFI2Z1JPQzEyCmoyYm5RUUtCZ1FDU1BlWlF0WTBTR0FoSVpRaExKTHR6eUwzWGdFNUxSN0hHVU9TTnBpSzRSZUZhTXNYbC85ZlIKbGtaT2grRlRHSWdUVVJ3WmI4LzcyYllVa3VVOTZiaFNUcjdvNXNTYW5QUmZUTXVBdEdSeVR5WWptTVp6Y3I1TApaL0o2aTg2a3k5NEF4RVNqTTk1bTFETGo0Q29CSG4xSm81akNtQmNlc2t3YVpjVThZS1ZIT2c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQ==","doubbjt.com.pem":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUdvekNDQll1Z0F3SUJBZ0lRRUZ4eHd2YXRMUk02L1JZNFpoa3BIVEFOQmdrcWhraUc5dzBCQVFzRkFEQmoKTVFzd0NRWURWUVFHRXdKRFRqRTJNRFFHQTFVRUNnd3RRbVZwYW1sdVp5QllhVzVqYUdGamFHRWdRM0psWkdsMApJRTFoYm1GblpXMWxiblFnUTI4dUxDQk1kR1F1TVJ3d0dnWURWUVFEREJOWVkyTWdWSEoxYzNRZ1QxWWdVMU5NCklFTkJNQjRYRFRJMU1EUXdNVEUwTURFd01Gb1hEVEkyTURRd01URTBNREExT1Zvd2ZqRUxNQWtHQTFVRUJoTUMKUTA0eEVqQVFCZ05WQkFnTUNlYTFtZWF4bitlY2dURVNNQkFHQTFVRUJ3d0o1cDJ0NWJlZTViaUNNUzh3TFFZRApWUVFLRENibWlwYm5pTGpuaUxnbzVyV1o1ckdmS2VTNmt1aUJsT2U5a2VhY2llbVprT1dGck9XUHVERVdNQlFHCkExVUVBd3dOS2k1a2IzVmlZbXAwTG1OdmJUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0MKZ2dFQkFPY0V3QWpGYVhURlZwbnhBdkZENWhCaGRyTDBvT1FZQVIwVzFnNDh4YkpvYnRXL1ZlemdoTUdXdUdhZAp3b3V0TXZWZHliZGtkNUxpQk56d0hYMXcrdWR5UngwTi9XNUxkODZGUng1NTVEZUMvcnVDbXVySXhQRTNBUHd1ClhQMGlWK3FxTlJyajVVMm1WYjhDMHhibVRIU1RnQkIxZjRxSnVOMHE4Z1BDbVc1SEdBOTdMdjdTN1VDQnFtOGYKWXhCZ3BrNGd1d0dUU1BaL09YRDBxVitjTk16djFFOVpFdEV1TkhzdVZoMndab0pwMG01U040S2dXVThQa2xwSApzcW11Q0pSRFpoVmpiVU5MSkI4bUZwUlgzY3ZXZ3VJK0Vua1pKcHhzT1JuTmZ0ZTRPWmdUNUlnWWtoMWRTR3BDCi9HYjZaRWkxTEluMTFORzhSU2E4S1BKQ0ZEVUNBd0VBQWFPQ0F6WXdnZ015TUF3R0ExVWRFd0VCL3dRQ01BQXcKUXdZRFZSMGZCRHd3T2pBNG9EYWdOSVl5YUhSMGNEb3ZMM2hwYm1Ob1lXTm9ZVEp2ZGk1amNtd3VZMlZ5ZDhWdApMbkJzTDNocGJtTm9ZV05vWVRKdmRpNWpjbXd3ZVFZSUt3WUJCUVVIQVFFRWJUQnJNQzhHQ0NzR0FRVUZCekFCCmhpTm9kSFJ3T2k4dmVHbHVZMmhoWTJoaE1tOTJMbTlqYzNBdFkyVnlkSFZ0TG1OdmJUQTRCZ2dyQmdFRkJRY3cKQW9Zc2FIUjBjRG92TDNKbGNHOXphWFJ2Y25rdVkyVnlkSFZ0TG5Cc0wzaHBibU5vWVdOb1lUSnZkaTVqWlhJdwpId1lEVlIwakJCZ3dGb0FVK3FETHdzZGRnYlVna2djZ0M3WTdjRHNKRGxVd0hRWURWUjBPQkJZRUZBdTN0Rnh3CllITEdoVHlLdkd1THpjemlRcTJ6TUV3R0ExVWRJQVJGTUVNd0NBWUdaNEVNQVFJQ01EY0dEQ3FFYUFHRzluY0MKQlFFV0FqQW5NQ1VHQ0NzR0FRVUZCd0lCRmhsb2RIUndjem92TDNkM2R5NWpaWEowZFcwdWNHd3ZRMUJUTUIwRwpBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFPQmdOVkhROEJBZjhFQkFNQ0JhQXdKUVlEClZSMFJCQjR3SElJTktpNWtiM1ZpWW1wMExtTnZiWUlMWkc5MVltSnFkQzVqYjIwd2dnRjhCZ29yQmdFRUFkWjUKQWdRQ0JJSUJiQVNDQVdnQlpnQjFBQm1HMU1jb3FtLyt1Z052ZUNwTkFaR3F6aTF5TVErdXpsMXdRUzBsVE1mVQpBQUFCbGZHb0Jxa0FBQVFEQUVZd1JBSWdjZitrUXV5L2ZkYXgzVkFsWHdPeGR5QnZxNmVJWGNLeHNZcTBZNGxZClUva0NJQ09KQ3NsTnFwUElwRkJTVVljTnFnQlF5ZkVOTnAxZ1Q0TnBYNVVpRW5ZK0FIVUFaQkhFYktRUzdLZUoKSEtJQ0xnQzhxMDhvQjlRZU5TZXI2djdWQThsOXpmQUFBQUdWOGFnR3d3QUFCQU1BUmpCRUFpQnNENGVBR3MrOQpjSVVmOVJ6RnlZMGp4eFI4a0JVSGt4R0pNTE5WRFBOb093SWdXY1Y5ejRQSGpGZWZFaUtQM1p6c283eDdqMTB6CmhUemdkMEVKWjU3alJBQUFkZ0FPVjVTODg2NnBQak1iTEprSHMvZVEzNXZDUFhFeUpkMGhxU1dzWWNWT0lRQUEKQVpYeHFBYXJBQUFFQXdCSE1FVUNJUURxaGNkNVZBWlhRMWsrd3VFREFOcDV0YjM1NmpPcW8wR0orMVpZajNXZgpKQUlnUWEwam5pcGgyN29mZ29WK0FmYVovaVQwTHVpUlNQOGh5ZFR4enh3QUUzNHdEUVlKS29aSWh2Y05BUUVMCkJRQURnZ0VCQUplOFcrU0UrMU5lRDFFN0RQQ2UvSUZkRzJxMjQwYmM4OVVtUGVpa2J6M1dXYU93ZWs0OEtrMVMKVlVjblNYM0VacVhqMENRcUgwRG14YlBSNXF6cFh6OXcyOWJBNzNzOC9jR3FUWEVSVUpkZlowcWNCR2lsSTFXUgoxZkh4SDdqZW00MHBxV0lPUzlKaVVxaDcwb3V0cjhjWk1vSjFvN0w0ZG1MNXFXalEzMW9PZ21DWEdJbm1IOU5mCk1rRk1YZ2JyNkliVEU0bnk1b2VFQllJeExOUVV2NGFwTTUvOVN2azVKU0lNMktQdDRqMENMcklKemM3VlhSdHoKWTcrU05qQWpuMlg4MnFUeXFyZWFoMVR5Z2xtNTE4MXZOVFFxa2NLTk9DN2RmQm9NS2R5TnZIQ2htTWNaV0V5WQpRdVNhaVYrRDVNR045aDBJL0I0L0Vqc2paSEZ6ZnhVPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlEdXpDQ0FxT2dBd0lCQWdJREJFVEFNQTBHQ1NxR1NJYjNEUUVCQlFVQU1INHhDekFKQmdOVkJBWVRBbEJNCk1TSXdJQVlEVlFRS0V4bFZibWw2WlhSdklGUmxZMmh1YjJ4dloybGxjeUJUTGtFdU1TY3dKUVlEVlFRTEV4NUQKWlhKMGRXMGdRMlZ5ZEdsbWFXTmhkR2x2YmlCQmRYUm9iM0pwZEhreElqQWdCZ05WQkFNVEdVTmxjblIxYlNCVQpjblZ6ZEdWa0lFNWxkSGR2Y21zZ1EwRXdIaGNOTURneE1ESXlNVEl3TnpNM1doY05Namt4TWpNeE1USXdOek0zCldqQitNUXN3Q1FZRFZRUUdFd0pRVERFaU1DQUdBMVVFQ2hNWlZXNXBlbVYwYnlCVVpXTm9ibTlzYjJkcFpYTWcKVXk1QkxqRW5NQ1VHQTFVRUN4TWVRMlZ5ZEhWdElFTmxjblJwWm1sallYUnBiMjRnUVhWMGFHOXlhWFI1TVNJdwpJQVlEVlFRREV4bERaWEowZFcwZ1ZISjFjM1JsWkNCT1pYUjNiM0pySUVOQk1JSUJJakFOQmdrcWhraUc5dzBCCkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTQvdDlvM0s2d3ZESkZJZjFhd0ZPNFc1QUI3cHRKMTEvOTFzdHMxckgKVVYrcnBES21ZWWUyYmcrRzBqQUNsL2pYYVZlaEdEbGRhbVI1eGdGWnJEd3hTamg4MGdUU1N5am9JRjg3QjZMTQpUWFBiODY1UHgxYlZXcWVXaWZyenEyalVJNFpaSjg4Sko3eXNibktESERCeTMrQ2k2ZExoZEhVWnZTcWVleFZVCkJCdlhRem10VlNqRjRocTc5TURrcmpoSk04eDJoWjg1UmRLa252SVNqRkg0Zk9RdGYvV3NYK3NXbjdFdDBick0Ka1VKM1RDWEprRGh2Mi9ETSs0NGVsMWsrMVdCTzVnVW83VWw1RTB1NlNOc3YrWExUT2NyK0g5ZzBjdlcwUU04eApBY1BzM2hFdEYxMGZ1RkRSWGhtbmFkNEhNeWpLVUpYNXAxVExWSVpRUmFuNVNRSURBUUFCbzBJd1FEQVBCZ05WCkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlFJZHMzTEIvOGs5c1hON2J1UXZPS0VOMFoxOXpBT0JnTlYKSFE4QkFmOEVCQU1DQVFZd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ2dFQkFLYW9yU0xPQVQybW8vOWkwRWlkaTE1eQpzSGhFNDl3Y3J3bjlJMGo2dlNyRXVWVUV0UkNqalNmZUM0SmowTzdlRERkNVFWc2lzckNhUVZ5bWNPRFUwSGZMCkk5TUE0R3hXTCtGcERRM1pxcjhoZ1ZEWkJxV28vNVUzMEtyKzRyUDFtUzFGaElybFFnblhkQUl2OTRuWW1lbTgKSjlSSGpib05SaHgzenhTa0hMbWtNY1NjS0hRRE5QOHpHU2FsNlExMHR6Nlh4bmJvSjVhalp0M2hydkpCVzhxWQpWb056Y09TR0d0SXhRYm92dmkwVFduWnZUdWhPZ1E0L1d3TWlvQksrWmxnUlNzc0R4TFFxS2kyV0YrQTVWTHhJCjAzWW5uWm90QnFiSjdEblNxOXVmbWdzbkFqVXBzVUNWNS9ub25GV0lHVWJXdHpUMWZzNDVtdGs0OFZIM1R5dz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="},"kind":"Secret","metadata":{"annotations":{},"creationTimestamp":null,"name":"nginx-ssl-liveapi","namespace":"test"}}
    creationTimestamp: "2025-10-10T03:33:30Z" # Secret创建时间
    name: nginx-ssl-liveapi # Secret名称(Nginx部署时引用该名称)
    namespace: test # Secret所在命名空间(与Nginx部署一致)
    resourceVersion: "123378" # Kubernetes资源版本(自动生成)
    uid: 7e111f02-7d9e-4fb8-9e15-215b399b50e1 # Secret唯一ID(自动生成)
    type: Opaque # Secret类型(Opaque表示通用密钥,存储任意二进制数据)

    3. Service-Nodeport配置

    该文件创建NodePort类型的Service,将Nginx容器的80/443端口映射到集群节点的30082/30444端口,实现外部通过节点IP+端口访问应用,关键参数已添加注释:

    # service-nodeport.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: custom-nginx-service # Service名称
    namespace: test # 所在命名空间(与Nginx部署一致)
    spec:
    type: NodePort # Service类型(NodePort,适合测试环境外部访问)
    selector:
    app: custom-nginx # 匹配Pod标签(与Nginx Deployment的Pod标签一致)
    ports:
    – name: http # 端口名称(HTTP协议)
    port: 80 # Service端口(集群内部访问端口)
    targetPort: 80 # Pod目标端口(Nginx容器的HTTP端口)
    nodePort: 30082 # 节点端口(外部通过节点IP:30082访问HTTP服务)
    – name: https # 端口名称(HTTPS协议)
    port: 443 # Service端口(集群内部访问端口)
    targetPort: 443 # Pod目标端口(Nginx容器的HTTPS端口)
    nodePort: 30444 # 节点端口(外部通过节点IP:30444访问HTTPS服务)

    4. NFS的信息(nginx配置在NFS上)

    NFS用于共享Nginx配置文件(如nginx.conf、虚拟主机配置)和SSL证书,确保多Nginx副本使用一致的配置。以下是NFS服务器的共享配置及目录结构:

    4.1 NFS共享配置(/etc/exports)

    该文件定义NFS服务器的共享目录及访问权限,关键参数已添加注释:

    # /etc/exports: NFS共享配置文件,格式为「共享目录 允许的客户端(选项)」
    # 示例说明:
    # /srv/homes hostname1(rw,sync,no_subtree_check) # 允许hostname1读写访问/srv/homes
    # /srv/nfs4 gss/krb5i(rw,sync,fsid=0) # NFSv4共享配置

    # 共享PHP应用数据目录,允许所有客户端访问(生产环境建议限制IP/网段)
    /opt/nfs/dbb-live-api-test/ *(rw,sync,no_root_squash,no_subtree_check)
    # 共享Nginx配置目录,允许所有客户端访问
    /opt/nfs/nginx *(rw,sync,no_root_squash,no_subtree_check)
    # 共享另一PHP应用数据目录,允许所有客户端访问
    /opt/nfs/dbb-live-api/ *(rw,sync,no_root_squash,no_subtree_check)

    # 选项说明:
    # rw:读写权限
    # sync:同步写入(数据实时写入磁盘,确保数据安全)
    # no_root_squash:客户端root用户映射为NFS服务器root用户(避免权限问题)
    # no_subtree_check:禁用子目录检查(提升性能,适合大目录共享)

    4.2 NFS目录结构(/opt/nfs/nginx)

    执行以下命令查看Nginx配置在NFS上的目录结构:

    root@vm01:~# cd /opt/nfs/nginx
    root@vm01:/opt/nfs/nginx# tree -L 2
    .
    ├── conf.d # Nginx虚拟主机配置目录
    │ ├── liveapi.doubbjt.com.conf # PHP应用(liveapi)的虚拟主机配置
    │ └── shopapi.doubbjt.com.conf # 另一PHP应用(shopapi)的虚拟主机配置
    ├── mime.types # Nginx MIME类型配置文件(定义文件类型与响应头的映射)
    ├── nginx.conf # Nginx主配置文件
    └── ssl # SSL证书目录
    ├── doubbjt.com.key # 域名私钥
    └── doubbjt.com.pem # 域名证书
    2 directories, 6 files

    4.3 虚拟主机配置(liveapi.doubbjt.com.conf)

    该文件是PHP应用(liveapi)的Nginx虚拟主机配置,支持HTTP/HTTPS,配置PHP-FPM反向代理及静态资源缓存,关键参数已添加注释:

    root@vm01:/opt/nfs/nginx# cat conf.d/liveapi.doubbjt.com.conf
    server {
    listen 80; # 监听HTTP 80端口
    listen 443 ssl http2; # 监听HTTPS 443端口,启用HTTP/2
    server_name testliveapi.doubbjt.com; # 绑定的域名(外部访问域名)
    index index.php index.html index.htm; # 默认首页文件顺序

    # SSL配置(HTTPS必需)
    ssl_certificate /etc/nginx/ssl/doubbjt.com.pem; # SSL证书路径(挂载自Secret)
    ssl_certificate_key /etc/nginx/ssl/doubbjt.com.key; # SSL私钥路径(挂载自Secret)
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; # 支持的TLS版本(禁用不安全的TLSv1.0)
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE; # 安全的加密套件
    ssl_prefer_server_ciphers on; # 优先使用服务器端加密套件
    ssl_session_cache shared:SSL:10m; # SSL会话缓存(10分钟)
    ssl_session_timeout 10m; # SSL会话超时时间(10分钟)

    # PHP文件处理:反向代理到PHP-FPM(127.0.0.1:9000)
    location ~ .*.(php|php5)?$
    {
    fastcgi_pass 127.0.0.1:9000; # PHP-FPM地址(同一Pod内的PHP容器)
    fastcgi_index index.php; # 默认PHP入口文件
    include fastcgi.conf; # 引入FastCGI配置(定义PHP环境变量)
    }

    # 静态资源处理:图片文件(GIF/JPG等),设置30天缓存
    location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
    {
    expires 30d; # 浏览器缓存30天
    error_log off; # 禁用错误日志(减少日志量)
    access_log /var/log/nginx/testliveapi.doubbjt.com.log; # 访问日志路径
    }

    # 静态资源处理:JS/CSS文件,设置12小时缓存
    location ~ .*\\.(js|css)?$
    {
    expires 12h; # 浏览器缓存12小时
    error_log off; # 禁用错误日志
    access_log /var/log/nginx/testliveapi.doubbjt.com.log; # 访问日志路径
    }

    # Let's Encrypt证书验证目录(申请SSL证书时使用,无需删除)
    location ^~ /.well-known/acme-challenge/ {
    root /www/wwwroot/testliveapi1.doubbjt.com; # 验证文件根目录
    allow all; # 允许所有客户端访问
    default_type "text/plain"; # 默认响应类型
    try_files $uri =404; # 找不到文件时返回404
    }

    # 访问日志配置:记录详细访问信息(使用main格式)
    access_log /var/log/nginx/liveapi.doubbjt.com.log main;
    # 错误日志配置:记录Nginx错误信息
    error_log /var/log/nginx/liveapi.doubbjt.com.error.log;
    }

    4.4 Nginx主配置(nginx.conf)

    该文件是Nginx的核心配置,定义工作进程、连接数、日志格式、Gzip压缩等全局参数,关键参数已添加注释:

    root@vm01:/opt/nfs/nginx# cat nginx.conf
    user root; # Nginx运行用户(root用户,避免权限问题)
    worker_processes auto; # 工作进程数(自动匹配CPU核心数)
    error_log /var/log/nginx/nginx_error.log crit; # 错误日志路径及级别(crit:仅记录严重错误)
    pid /var/log/nginx/nginx.pid; # Nginx进程PID文件路径
    worker_rlimit_nofile 51200; # 单个工作进程最大文件句柄数(提升并发能力)

    events
    {
    use epoll; # 使用epoll事件模型(Linux下高性能模型)
    worker_connections 51200; # 单个工作进程最大连接数
    multi_accept on; # 允许工作进程同时接受多个连接
    }

    http
    {
    # 日志格式定义(main格式,包含客户端IP、请求信息、响应时间等)
    log_format main
    '$remote_addr$remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" '
    'rt=$request_time '
    'upstream_addr="$upstream_addr" '
    'upstream_status=$upstream_status '
    'upstream_response_time=$upstream_response_time '
    'upstream_cache_status="$upstream_cache_status"';

    include mime.types; # 引入MIME类型配置
    default_type application/octet-stream; # 默认MIME类型(未知文件类型)

    server_names_hash_bucket_size 512; # 服务器名哈希表大小(支持长域名)
    client_header_buffer_size 32k; # 客户端请求头缓存大小
    large_client_header_buffers 4 32k; # 大请求头缓存(4个32k缓冲区)
    client_max_body_size 50m; # 客户端最大请求体大小(50M,支持大文件上传)

    sendfile on; # 启用sendfile(零拷贝传输,提升文件传输性能)
    tcp_nopush on; # 启用TCP_NOPUSH(合并小包,提升网络效率)
    keepalive_timeout 60; # TCP连接保持时间(60秒)
    tcp_nodelay on; # 启用TCP_NODELAY(禁用Nagle算法,降低延迟)

    # FastCGI相关配置(PHP-FPM通信优化)
    fastcgi_connect_timeout 300; # FastCGI连接超时时间(300秒)
    fastcgi_send_timeout 300; # FastCGI发送超时时间(300秒)
    fastcgi_read_timeout 300; # FastCGI读取超时时间(300秒)
    fastcgi_buffer_size 64k; # FastCGI缓冲区大小(64k)
    fastcgi_buffers 4 64k; # FastCGI缓冲区数量及大小(4个64k)
    fastcgi_busy_buffers_size 128k; # FastCGI繁忙缓冲区大小(128k)
    fastcgi_temp_file_write_size 256k; # FastCGI临时文件写入大小(256k)
    fastcgi_intercept_errors on; # 启用FastCGI错误拦截(传递PHP错误到Nginx)

    # Gzip压缩配置(提升传输效率)
    gzip on; # 启用Gzip压缩
    gzip_min_length 1k; # 最小压缩文件大小(1k以下不压缩)
    gzip_buffers 4 16k; # Gzip缓冲区(4个16k)
    gzip_http_version 1.1; # 支持的HTTP版本(1.1)
    gzip_comp_level 2; # 压缩级别(2级,平衡压缩率与性能)
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml; # 压缩的文件类型
    gzip_vary on; # 启用Vary头(告知浏览器是否支持压缩)
    gzip_proxied expired no-cache no-store private auth; # 对代理请求的压缩策略
    gzip_disable "MSIE [1-6]\\."; # 禁用IE6及以下版本的Gzip压缩(兼容性问题)

    # 连接限制配置(防止恶意请求)
    limit_conn_zone $binary_remote_addr zone=perip:10m; # 按IP限制连接(10M内存存储IP)
    limit_conn_zone $server_name zone=perserver:10m; # 按服务器名限制连接(10M内存)

    server_tokens off; # 禁用Nginx版本显示(提升安全性)
    access_log off; # 全局禁用访问日志(虚拟主机可单独启用)

    include /etc/nginx/conf.d/*.conf; # 引入虚拟主机配置(conf.d目录下的.conf文件)
    }

    4.5 MIME类型配置(mime.types)

    该文件定义文件扩展名与MIME类型的映射,确保Nginx返回正确的Content-Type响应头,内容如下:

    root@vm01:/opt/nfs/nginx# cat mime.types
    types {
    text/html html htm shtml; # HTML文件类型
    text/css css; # CSS文件类型
    text/xml xml; # XML文件类型
    image/gif gif; # GIF图片类型
    image/jpeg jpeg jpg; # JPEG图片类型
    application/javascript js; # JS文件类型
    application/atom+xml atom; # Atom订阅类型
    application/rss+xml rss; # RSS订阅类型
    text/mathml mml; # MathML文件类型
    text/plain txt; # 纯文本类型
    text/vnd.sun.j2me.app-descriptor jad; # J2ME应用描述文件
    text/vnd.wap.wml wml; # WAP WML文件类型
    text/x-component htc; # HTC组件类型
    image/png png; # PNG图片类型
    image/svg+xml svg svgz; # SVG图片类型
    image/tiff tif tiff; # TIFF图片类型
    image/vnd.wap.wbmp wbmp; # WAP WBMP图片类型
    image/webp webp; # WebP图片类型
    image/x-icon ico; # ICO图标类型
    image/x-jng jng; # JNG图片类型
    image/x-ms-bmp bmp; # BMP图片类型
    image/x-png png; # PNG图片类型(重复定义,确保兼容性)
    image/x-portable-anymap pnm; # PNM图片类型
    image/x-portable-bitmap pbm; # PBM图片类型
    image/x-portable-graymap pgm; # PGM图片类型
    image/x-portable-pixmap ppm; # PPM图片类型
    image/x-rgb rgb; # RGB图片类型
    image/x-xbitmap xbmp; # XBMP图片类型
    image/x-xpixmap xpm; # XPM图片类型
    image/x-xwindowdump xwd; # XWD图片类型
    application/java-archive jar war ear; # Java归档文件类型
    application/mac-binhex40 hqx; # Mac BinHex40文件类型
    application/msword doc; # Word文档类型
    application/pdf pdf; # PDF文件类型
    application/postscript ps eps ai; # PostScript文件类型
    application/rtf rtf; # RTF文档类型
    application/vnd.apple.mpegurl m3u8; # Apple M3U8流媒体类型
    application/vnd.ms-excel xls; # Excel文档类型
    application/vnd.ms-powerpoint ppt; # PowerPoint文档类型
    application/vnd.wap.wmlc wmlc; # WAP WMLC文件类型
    application/vnd.google-earth.kml+xml kml; # Google Earth KML类型
    application/vnd.google-earth.kmz kmz; # Google Earth KMZ类型
    application/x-7z-compressed 7z; # 7Z压缩文件类型
    application/x-cocoa cco; # Cocoa应用类型
    application/x-java-archive-delta jardiff; # Java归档差异文件类型
    application/x-java-jnlp-file jnlp; # Java Web Start文件类型
    application/x-makeself run; # Makeself自解压文件类型
    application/x-perl pl pm; # Perl脚本类型
    application/x-pilot prc pdb; # Palm Pilot文件类型
    application/x-rar-compressed rar; # RAR压缩文件类型
    application/x-redhat-package-manager rpm; # RPM包类型
    application/x-sea sea; # SEA自解压文件类型
    application/x-shockwave-flash swf; # Flash文件类型
    application/x-stuffit sit; # StuffIt压缩文件类型
    application/x-tcl tcl tk; # Tcl/Tk脚本类型
    application/x-x509-ca-cert der pem crt; # CA证书类型
    application/x-xpinstall xpi; # Firefox扩展类型
    application/xhtml+xml xhtml; # XHTML文件类型
    application/xspf+xml xspf; # XSPF播放列表类型
    application/zip zip; # ZIP压缩文件类型
    application/octet-stream bin exe dll; # 二进制文件类型(exe/dll)
    application/octet-stream deb; # DEB包类型
    application/octet-stream dmg; # DMG镜像类型
    application/octet-stream eot; # EOT字体类型
    application/octet-stream iso img; # ISO/IMG镜像类型
    application/octet-stream msi msp msm; # MSI安装包类型
    audio/midi mid midi kar; # MIDI音频类型
    audio/mpeg mp3; # MP3音频类型
    audio/x-realaudio ra; # RealAudio音频类型
    video/3gpp 3gpp 3gp; # 3GPP视频类型
    video/mp2t ts; # TS流媒体类型
    video/mp4 mp4; # MP4视频类型
    video/mpeg mpeg mpg; # MPEG视频类型
    video/quicktime mov; # QuickTime视频类型
    video/webm webm; # WebM视频类型
    video/x-flv flv; # FLV视频类型
    video/x-mng mng; # MNG视频类型
    video/x-ms-asf asx asf; # ASF视频类型
    video/x-ms-wmv wmv; # WMV视频类型
    video/x-msvideo avi; # AVI视频类型
    }

    5. Deployment-Nginx-NFS.yaml

    该文件通过Deployment部署Nginx容器,挂载NFS存储(配置文件)和Secret(SSL证书),实现配置共享和HTTPS支持,关键参数已添加注释:

    # deployment-nginx-nfs.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: custom-nginx # Deployment名称
    namespace: test # 所在命名空间
    labels:
    app: custom-nginx # Deployment标签(用于Service匹配)
    spec:
    replicas: 1 # 副本数(测试环境1个,生产环境可增加)
    selector:
    matchLabels:
    app: custom-nginx # 匹配Pod标签(与template.metadata.labels一致)
    template:
    metadata:
    labels:
    app: custom-nginx # Pod标签(用于Service和Deployment匹配)
    spec:
    containers:
    – name: nginx # 容器名称
    image: swr.cn-east-3.myhuaweicloud.com/bocheng-test/ubuntu:22.04_v1 # 基础镜像(含Nginx)
    imagePullPolicy: IfNotPresent # 镜像拉取策略(本地有则不拉取)
    # 容器启动命令:检查NFS配置,初始化或使用已有配置,然后启动Nginx
    command: ["/bin/bash", "-c"]
    args:
    – |
    set -ex # 执行错误时退出,显示详细命令
    # 检查NFS挂载目录是否有nginx.conf(首次启动时NFS目录为空,需初始化)
    if [ ! -f "/etc/nginx/nginx.conf" ]; then
    echo "📄 NFS 目录为空,首次初始化:从模板复制配置…"
    mkdir -p /etc/nginx/conf.d # 创建虚拟主机配置目录
    cp /tmp/config/nginx.conf /etc/nginx/nginx.conf # 复制主配置(来自ConfigMap)
    cp /tmp/config/liveapi.doubbjt.com.conf /etc/nginx/conf.d/ # 复制虚拟主机配置(来自ConfigMap)
    else
    echo "🔁 使用 NFS 中已有配置。" # NFS已有配置,直接使用
    fi
    # 创建Nginx运行必需的目录(使用tmpfs,避免数据持久化)
    mkdir -p /run/nginx /var/log/nginx /www/server/nginx/logs
    # 启动Nginx(daemon off:前台运行,确保容器不退出)
    echo "🚀 启动 Nginx 服务…"
    exec /usr/sbin/nginx -g "daemon off;"
    ports:
    – containerPort: 80 # 容器HTTP端口
    – containerPort: 443 # 容器HTTPS端口
    volumeMounts:
    – name: nginx-config-nfs # 挂载NFS存储(Nginx配置文件)
    mountPath: /etc/nginx # 挂载路径(Nginx配置目录)
    – name: config-volume # 挂载ConfigMap(配置模板,首次初始化用)
    mountPath: /tmp/config # 挂载路径(临时目录)
    readOnly: true # 只读挂载(避免修改模板)
    – name: ssl-certs # 挂载Secret(SSL证书)
    mountPath: /etc/nginx/ssl # 挂载路径(Nginx SSL目录)
    securityContext: # 安全上下文(赋予必要权限)
    runAsUser: 0 # 以root用户运行(修改NFS文件需root权限)
    allowPrivilegeEscalation: true # 允许权限提升
    capabilities:
    add:
    – NET_BIND_SERVICE # 添加绑定端口的能力(绑定80/443需该权限)
    volumes:
    – name: config-volume # ConfigMap卷(存储配置模板)
    configMap:
    name: custom-nginx-config # ConfigMap名称(对应后续的configmap配置)
    – name: nginx-config-nfs # NFS卷(存储Nginx配置文件)
    nfs:
    server: 192.168.122.189 # NFS服务器地址
    path: /opt/nfs/nginx # NFS共享目录(与NFS配置一致)
    – name: ssl-certs # Secret卷(存储SSL证书)
    secret:
    secretName: nginx-ssl-liveapi # Secret名称(对应之前的证书配置)

    6. Service-Nodeport.yaml(重复配置,与3.内容一致)

    root@k8smaster:/data/service/double_nginx/20251010v3/ingress# cat service-nodeport.yaml
    # service-nodeport.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: customnginxservice
    namespace: test
    spec:
    type: NodePort
    selector:
    app: customnginx
    ports:
    name: http
    port: 80
    targetPort: 80
    nodePort: 30082
    name: https
    port: 443
    targetPort: 443
    nodePort: 30444

    7. 依赖配置文件(ConfigMap)

    该文件创建ConfigMap存储Nginx配置模板(主配置和虚拟主机配置),用于Nginx容器首次启动时初始化NFS存储,关键参数已添加注释:

    # configmap:存储Nginx配置模板,避免NFS首次挂载为空
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: custom-nginx-config # ConfigMap名称(Nginx Deployment引用该名称)
    namespace: test # 所在命名空间(与Nginx部署一致)
    data:
    # Nginx主配置模板(与NFS上的nginx.conf内容一致)
    nginx.conf: |
    user root;
    worker_processes auto;
    error_log /var/log/nginx/nginx_error.log crit;
    pid /var/log/nginx/nginx.pid;
    worker_rlimit_nofile 51200;
    events
    {
    use epoll;
    worker_connections 51200;
    multi_accept on;
    }
    http
    {
    log_format main
    '$remote_addr – $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" '
    'rt=$request_time '
    'upstream_addr="$upstream_addr" '
    'upstream_status=$upstream_status '
    'upstream_response_time=$upstream_response_time '
    'upstream_cache_status="$upstream_cache_status"';
    include mime.types;
    default_type application/octet-stream;
    server_names_hash_bucket_size 512;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;
    client_max_body_size 50m;
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 60;
    tcp_nodelay on;
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 256k;
    fastcgi_intercept_errors on;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 2;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml;
    gzip_vary on;
    gzip_proxied expired no-cache no-store private auth;
    gzip_disable "MSIE [1-6]\\.";
    limit_conn_zone $binary_remote_addr zone=perip:10m;
    limit_conn_zone $server_name zone=perserver:10m;
    server_tokens off;
    access_log off;
    include /etc/nginx/conf.d/*.conf;
    }
    # 虚拟主机配置模板(与NFS上的liveapi.doubbjt.com.conf内容一致)
    liveapi.doubbjt.com.conf: |
    server {
    listen 80;
    listen 443 ssl http2;
    server_name testliveapi.doubbjt.com;
    index index.php index.html index.htm;
    # ✅ SSL 配置(保持不变)
    ssl_certificate /etc/nginx/ssl/doubbjt.com.pem;
    ssl_certificate_key /etc/nginx/ssl/doubbjt.com.key;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    location ~ .*.(php|php5)?$
    {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi.conf;
    }
    location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
    {
    expires 30d;
    error_log off;
    access_log /var/log/nginx/testliveapi.doubbjt.com.log;
    }
    location ~ .*\\.(js|css)?$
    {
    expires 12h;
    error_log off;
    access_log /var/log/nginx/testliveapi.doubbjt.com.log;
    }
    location ^~ /.well-known/acme-challenge/ {
    root /www/wwwroot/testliveapi1.doubbjt.com;
    allow all;
    default_type "text/plain";
    try_files $uri =404;
    }
    # ✅ 日志(使用你的 main 格式)
    access_log /var/log/nginx/liveapi.doubbjt.com.log main;
    error_log /var/log/nginx/liveapi.doubbjt.com.error.log;
    }

    第三步:Jenkins Pipeline

    Jenkins Pipeline通过代码定义CI/CD流程,支持部署

    PHP项目Kubernetes部署与Jenkins CI/CD流水线

    (接上部分Pipeline配置内容) 部署与回滚操作,本步骤提供两种实现方式(PV/PVC挂载NFS、直接登录NFS服务器拷贝),确保PHP项目代码可稳定部署到K8s集群。

    1. GitLab 配置部分

    PHP项目的Kubernetes部署清单模板存储在GitLab仓库(yyh-devops)中,便于版本控制和流水线拉取。仓库地址及模板内容如下:

    • GitLab仓库地址:https://gitlab.dbblive.com/kubernetes/yyh-devops/-/blob/master/dbbjt/php/dbb-live-api/k8s-deployment.yaml
    • 仓库用途:存放所有应用的Kubernetes部署清单模板,支持多环境(测试/生产)复用。
    k8s-deployment.yaml 模板内容

    该模板定义PHP应用的Deployment(含PHP-FPM和Nginx容器)和Service,使用占位符(如{APP_NAME})实现动态渲染,关键参数已添加注释:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: {APP_NAME} # 应用名称占位符(Pipeline中替换为实际名称)
    namespace: test # 部署命名空间(测试环境)
    labels:
    app: {APP_NAME} # 应用标签(与Service匹配)
    spec:
    replicas: 2 # 副本数(生产环境可根据负载调整)
    selector:
    matchLabels:
    app: {APP_NAME} # 匹配Pod标签
    template:
    metadata:
    labels:
    app: {APP_NAME} # Pod标签(用于Service和Deployment匹配)
    spec:
    # 初始化容器:构建PHP缓存(配置、路由、视图缓存)
    initContainers:
    – name: build-artisan-cache
    image: swr.cn-east-3.myhuaweicloud.com/bocheng-test/php-fpm-prod:base_v4 # PHP基础镜像
    env:
    – name: APP_ENV
    value: "production" # 应用环境(生产环境)
    – name: CACHE_DRIVER
    value: "file" # 缓存驱动(文件缓存)
    – name: SESSION_DRIVER
    value: "file" # 会话驱动(文件会话)
    # 执行缓存构建命令(Laravel框架常用命令)
    command: ["sh", "-c"]
    args:
    – |
    set -ex
    cd /var/www/html # 进入PHP项目根目录
    # 清理旧缓存
    rm -f bootstrap/cache/*.php
    rm -rf storage/framework/cache/*
    rm -rf storage/framework/views/*
    rm -rf storage/framework/sessions/*
    # 生成新缓存
    php artisan config:cache # 配置缓存
    php artisan route:cache # 路由缓存
    php artisan view:cache # 视图缓存
    php artisan event:cache # 事件缓存
    volumeMounts:
    – name: nfs-storage # 挂载NFS存储
    subPath: "{NFS_SUBPATH}" # NFS子路径(Pipeline中替换为构建号)
    mountPath: /var/www/html # 挂载到PHP项目根目录
    # 业务容器:PHP-FPM(处理PHP请求)和Nginx(处理静态资源和反向代理)
    containers:
    – name: php-fpm # PHP-FPM容器
    image: swr.cn-east-3.myhuaweicloud.com/bocheng-test/php-fpm-prod:base_v4 # PHP-FPM镜像
    ports:
    – containerPort: 9000 # PHP-FPM端口(Nginx反向代理目标端口)
    volumeMounts:
    – name: nfs-storage
    subPath: "{NFS_SUBPATH}"
    mountPath: /var/www/html
    resources: # 资源限制(避免占用过多集群资源)
    limits:
    memory: "512Mi"
    cpu: "500m"
    requests:
    memory: "512Mi"
    cpu: "500m"
    securityContext:
    runAsUser: 0 # 以root用户运行(修改文件权限需root)
    runAsGroup: 0
    – name: nginx # Nginx容器(处理静态资源和反向代理)
    image: swr.cn-east-3.myhuaweicloud.com/bocheng-test/nginx-laravel:base_v1 # Nginx镜像(适配Laravel框架)
    ports:
    – containerPort: 80 # Nginx HTTP端口
    volumeMounts:
    – name: nfs-storage
    subPath: "{NFS_SUBPATH}"
    mountPath: /var/www/html # 挂载PHP项目目录(静态资源目录)
    resources: # 资源限制
    limits:
    memory: "128Mi"
    cpu: "200m"
    requests:
    memory: "128Mi"
    cpu: "200m"
    securityContext:
    runAsUser: 0
    runAsGroup: 0
    # 卷配置:NFS存储(挂载PHP项目代码)
    volumes:
    – name: nfs-storage
    persistentVolumeClaim:
    claimName: nfs-pvc-{APP_NAME}-test # PVC名称(占位符,替换为实际PVC)

    # Service配置:ClusterIP类型,供Ingress或内部访问
    apiVersion: v1
    kind: Service
    metadata:
    name: {APP_NAME}-service # Service名称(占位符)
    namespace: test
    spec:
    selector:
    app: {APP_NAME} # 匹配Pod标签
    ports:
    – protocol: TCP
    port: 80 # Service端口(集群内部访问端口)
    targetPort: 80 # 目标端口(Nginx容器的80端口)
    type: ClusterIP # Service类型(ClusterIP,仅集群内部访问)

    2. Jenkins Pipeline 流水线部分

    流水线支持两种部署方式:PV/PVC挂载NFS(推荐,通过K8s原生存储机制)和登录NFS服务器拷贝(备选,适合无PV/PVC场景),均包含部署与回滚逻辑。

    1. 通过PV/PVC挂载NFS的方式

    该流水线通过PV/PVC挂载NFS存储,实现代码的持久化存储与版本管理,关键步骤已添加注释:

    pipeline {
    agent { label 'jnlp-slave' } # 指定Agent标签(使用之前配置的jnlp-slave模板)
    parameters {
    # 部署动作选择:Deploy(部署)或Rollback(回滚)
    choice(
    name: 'DEPLOY_ACTION',
    choices: ['Deploy', 'Rollback'],
    description: 'Choose action type: Deploy or Rollback'
    )
    # 回滚目标构建号:为空时回滚到上一次成功版本
    string(
    name: 'ROLLBACK_TO_BUILD',
    defaultValue: '',
    description: 'Enter build number to rollback (e.g. 15), leave empty to rollback to last successful version'
    )
    }
    environment {
    # GitLab部署仓库SSH地址(拉取K8s模板)
    DEPLOY_REPO_SSH = 'git@gitlab.dbblive.com:kubernetes/yyh-devops.git'
    }
    stages {
    # 阶段1:初始化变量(从Job名称获取应用名,定义NFS路径)
    stage('Initialize Variables') {
    steps {
    script {
    # 从Job名称获取应用名(Job名称需与应用名一致,如dbb-live-api)
    env.APP_NAME = env.JOB_BASE_NAME
    echo "Detected APP_NAME from Job Name: ${APP_NAME}"
    # NFS根目录(存储应用所有构建版本)
    env.NFS_ROOT = "/opt/nfs/${APP_NAME}-test"
    # 当前构建目录(NFS根目录下的子目录,按构建号区分)
    env.CURRENT_BUILD_DIR = "${NFS_ROOT}/${BUILD_NUMBER}"
    # 记录上一次成功构建号的文件路径
    env.LAST_SUCCESS_FILE = "${NFS_ROOT}/LAST_SUCCESS"
    # 基础代码目录(可选,存储基础版本代码)
    env.BASE_DIR = "${NFS_ROOT}/base"
    }
    }
    }

    # 阶段2:准备部署代码(仅Deploy动作执行)
    stage('Prepare Code for Deployment') {
    when {
    expression { params.DEPLOY_ACTION == 'Deploy' } # 仅当动作是Deploy时执行
    }
    steps {
    script {
    # 代码源目录(Jenkins工作目录,存放拉取的PHP项目代码)
    def sourceDir = "${WORKSPACE}"
    # 创建当前构建目录,复制代码到NFS,并赋予读写权限
    sh """
    mkdir -p ${CURRENT_BUILD_DIR}
    rsync -arvP ${sourceDir}/ ${CURRENT_BUILD_DIR}/|| true # 复制代码(忽略轻微错误)
    chmod -R u+rw ${CURRENT_BUILD_DIR} # 赋予Jenkins用户读写权限
    """
    # 进入当前构建目录,尝试拉取最新代码(若目录是Git仓库)
    dir("${CURRENT_BUILD_DIR}") {
    sh '''
    cd "${CURRENT_BUILD_DIR}"
    # 配置Git SSH参数(禁用严格主机密钥检查,避免首次连接确认)
    git config –global core.sshCommand "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
    git pull|| echo "Git pull skipped (maybe not a repo)" # 非Git仓库则跳过
    '''
    }
    }
    }
    }

    # 阶段3:处理回滚逻辑(仅Rollback动作执行)
    stage('Handle Rollback') {
    when { expression { params.DEPLOY_ACTION == 'Rollback' } } # 仅当动作是Rollback时执行
    steps {
    script {
    # 获取用户指定的回滚目标构建号(为空则读取LAST_SUCCESS文件)
    def targetBuild = params.ROLLBACK_TO_BUILD?.trim()
    if (!targetBuild) {
    def lastSuccessFile = "${NFS_ROOT}/LAST_SUCCESS"
    # 检查LAST_SUCCESS文件是否存在(记录上一次成功构建号)
    if (fileExists(lastSuccessFile)) {
    targetBuild = readFile(lastSuccessFile).trim()
    echo "No build number provided. Reading last successful build from ${lastSuccessFile}: ${targetBuild}"
    } else {
    # 无目标构建号且无LAST_SUCCESS文件,抛出错误
    error("ROLLBACK_TO_BUILD is empty and LAST_SUCCESS file not found at ${lastSuccessFile}. Cannot determine rollback target.")
    }
    }
    echo "Rolling back to build: ${targetBuild}"
    # 将目标构建号存入环境变量,供后续渲染模板使用
    env.TARGET_BUILD = targetBuild
    }
    }
    }

    # 阶段4:拉取部署模板(从GitLab拉取K8s模板文件)
    stage('Checkout Deployment Templates') {
    steps {
    script {
    echo "Cleaning workspace and cloning deployment repo via SSH"
    deleteDir() # 清理Jenkins工作目录,避免残留文件干扰
    }
    sh '''
    # 配置Git SSH参数(禁用严格主机密钥检查)
    git config –global core.sshCommand "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
    REPO_DIR="yyh-devops-repo" # 本地仓库目录名
    REPO_URL="${DEPLOY_REPO_SSH}" # GitLab仓库SSH地址
    echo "Cloning from ${REPO_URL} into ${REPO_DIR}"
    git clone "${REPO_URL}" "${REPO_DIR}" # 克隆仓库
    # 检查模板文件是否存在(路径需与GitLab仓库一致)
    INPUT_PATH="${REPO_DIR}/dbbjt/php/${APP_NAME}/k8s-deployment.yaml"
    if [ ! -f "$INPUT_PATH" ]; then
    echo "❌ Error: k8s-deployment.yaml not found at $INPUT_PATH"
    find "${REPO_DIR}" -type f # 列出仓库所有文件,便于排查路径问题
    exit 1 # 模板不存在,终止流水线
    fi
    echo "✅ Template repository cloned successfully"
    '''
    }
    }

    # 阶段5:渲染并应用Kubernetes清单(替换占位符,部署到K8s)
    stage('Render and Apply Kubernetes Manifest') {
    steps {
    script {
    # 确定NFS子路径:Deploy用当前构建号,Rollback用目标构建号
    env.NFS_SUBPATH = (params.DEPLOY_ACTION == 'Deploy') ? env.BUILD_NUMBER : env.TARGET_BUILD
    # 打印最终变量,便于调试
    echo "🎯 Final Variables:"
    echo " APP_NAME = ${APP_NAME}"
    echo " BUILD_NUMBER = ${BUILD_NUMBER}"
    echo " NFS_SUBPATH = ${NFS_SUBPATH}"
    echo " Action = ${params.DEPLOY_ACTION}"
    }
    sh '''
    set -e # 执行错误时立即退出
    set -u # 引用未定义变量时退出
    # 模板输入路径(GitLab克隆后的模板文件)
    INPUT="yyh-devops-repo/dbbjt/php/${APP_NAME}/k8s-deployment.yaml"
    # 渲染后的输出路径(Jenkins工作目录)
    OUTPUT="${WORKSPACE}/rendered.yaml"
    # 检查模板文件是否存在
    if [ ! -f "$INPUT" ]; then
    echo "❌ Template not found at $INPUT"
    exit 1
    fi
    # 复制模板到输出文件,替换占位符({APP_NAME}和{NFS_SUBPATH})
    cp "$INPUT" "$OUTPUT"
    sed -i "s|{APP_NAME}|${APP_NAME}|g" "$OUTPUT" # 替换应用名占位符
    sed -i "s|{NFS_SUBPATH}|${NFS_SUBPATH}|g" "$OUTPUT" # 替换NFS子路径占位符
    # 打印渲染后的清单,便于确认配置
    echo "=== 🔍 Rendered Manifest ==="
    cat "$OUTPUT"
    # 应用清单到K8s集群
    echo "=== 🚀 Applying to Kubernetes ==="
    kubectl apply -f "$OUTPUT"
    '''
    }
    }

    # 阶段6:更新成功标记(仅当流水线成功时执行,记录当前版本)
    stage('Update Success Marker') {
    when {
    expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' } # 流水线成功时执行
    }
    steps {
    script {
    # 确定最终版本:Deploy用当前构建号,Rollback用目标构建号
    def finalVersion = (params.DEPLOY_ACTION == 'Deploy') ? env.BUILD_NUMBER : (params.ROLLBACK_TO_BUILD ?: env.TARGET_BUILD)
    # 将最终版本写入LAST_SUCCESS文件,供后续回滚使用
    sh "echo ${finalVersion} > ${LAST_SUCCESS_FILE}"
    echo "Action succeeded. Updated LAST_SUCCESS=${finalVersion}"
    }
    }
    failure {
    echo "Action failed. LAST_SUCCESS was not updated." # 阶段失败时提示
    }
    always {
    deleteDir() # 无论成功失败,都清理Jenkins工作目录
    }
    }
    }
    }

    2. 登录NFS服务器去拷贝的方式

    该流水线通过SSH登录NFS服务器直接拷贝代码(无需PV/PVC挂载),适合无K8s存储插件的场景,关键步骤已添加注释:

    agent { label 'jnlp-slave' } # 指定Agent标签
    parameters {
    # 部署动作选择(同前)
    choice(
    name: 'DEPLOY_ACTION',
    choices: ['Deploy', 'Rollback'],
    description: 'Choose action type: Deploy or Rollback'
    )
    # 回滚目标构建号(同前)
    string(
    name: 'ROLLBACK_TO_BUILD',
    defaultValue: '',
    description: 'Enter build number to rollback (e.g. 15), leave empty to rollback to last successful version'
    )
    }
    environment {
    # GitLab部署仓库SSH地址(同前)
    DEPLOY_REPO_SSH = 'git@gitlab.dbblive.com:kubernetes/yyh-devops.git'
    # NFS服务器地址(需配置Agent免密登录NFS服务器)
    NFS_SERVER = '192.168.122.189' # 替换为实际NFS服务器IP
    }
    stages {
    # 阶段1:初始化变量(同前,增加NFS服务器地址)
    stage('Initialize Variables') {
    steps {
    script {
    env.APP_NAME = env.JOB_BASE_NAME
    echo "Detected APP_NAME from Job Name: ${APP_NAME}"
    # NFS根目录(带服务器地址,用于SSH拷贝)
    env.NFS_ROOT = "${NFS_SERVER}:/opt/nfs/${APP_NAME}-test"
    env.CURRENT_BUILD_DIR = "${NFS_ROOT}/${BUILD_NUMBER}"
    env.LAST_SUCCESS_FILE = "${NFS_ROOT}/LAST_SUCCESS"
    env.BASE_DIR = "${NFS_ROOT}/base"
    }
    }
    }

    # 阶段2:准备部署代码(通过SSH拷贝到NFS服务器)
    stage('Prepare Code for Deployment') {
    when {
    expression { params.DEPLOY_ACTION == 'Deploy' }
    }
    steps {
    script {
    def sourceDir = "${WORKSPACE}"
    # 通过SSH在NFS服务器创建目录,并用rsync拷贝代码
    sh """
    # 登录NFS服务器,创建当前构建目录
    ssh ${NFS_SERVER} "mkdir -p /opt/nfs/${APP_NAME}-test/${BUILD_NUMBER}"
    # 从Agent拷贝代码到NFS服务器(rsync支持增量拷贝,提升效率)
    rsync -arvP ${sourceDir}/ ${NFS_SERVER}:/opt/nfs/${APP_NAME}-test/${BUILD_NUMBER}/|| true
    # 登录NFS服务器,赋予目录读写权限
    ssh ${NFS_SERVER} "chmod -R u+rw /opt/nfs/${APP_NAME}-test/${BUILD_NUMBER}"
    """
    # (可选)登录NFS服务器,尝试拉取最新代码
    sh """
    ssh ${NFS_SERVER} "cd /opt/nfs/${APP_NAME}-test/${BUILD_NUMBER} && \\
    git config –global core.sshCommand 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' && \\
    git pull|| echo 'Git pull skipped'"
    """
    }
    }
    }

    # 阶段3:处理回滚逻辑(同前,读取NFS服务器上的LAST_SUCCESS文件)
    stage('Handle Rollback') {
    when { expression { params.DEPLOY_ACTION == 'Rollback' } }
    steps {
    script {
    def targetBuild = params.ROLLBACK_TO_BUILD?.trim()
    if (!targetBuild) {
    # 通过SSH读取NFS服务器上的LAST_SUCCESS文件
    def lastSuccessFile = "/opt/nfs/${APP_NAME}-test/LAST_SUCCESS"
    def fileExists = sh(
    script: "ssh ${NFS_SERVER} 'test -f ${lastSuccessFile} && echo 1 || echo 0'",
    returnStdout: true
    ).trim()
    if (fileExists == '1') {
    targetBuild = sh(
    script: "ssh ${NFS_SERVER} 'cat ${lastSuccessFile}'",
    returnStdout: true
    ).trim()
    echo "No build number provided. Reading last successful build from ${lastSuccessFile}: ${targetBuild}"
    } else {
    error("ROLLBACK_TO_BUILD is empty and LAST_SUCCESS file not found at ${lastSuccessFile}. Cannot determine rollback target.")
    }
    }
    echo "Rolling back to build: ${targetBuild}"
    env.TARGET_BUILD = targetBuild
    }
    }
    }

    # 阶段4:拉取部署模板(同前,克隆GitLab仓库)
    stage('Checkout Deployment Templates') {
    steps {
    script {
    echo "Cleaning workspace and cloning deployment repo via SSH"
    deleteDir()
    }
    sh '''
    git config –global core.sshCommand "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
    REPO_DIR="yyh-devops-repo"
    REPO_URL="${DEPLOY_REPO_SSH}"
    echo "Cloning from ${REPO_URL} into ${REPO_DIR}"
    git clone "${REPO_URL}" "${REPO_DIR}"
    INPUT_PATH="${REPO_DIR}/dbbjt/php/${APP_NAME}/k8s-deployment.yaml"
    if [ ! -f "$INPUT_PATH" ]; then
    echo "Template not found at $INPUT_PATH"
    exit 1
    fi
    # 渲染模板:替换占位符(同前)
    OUTPUT="${WORKSPACE}/rendered.yaml"
    cp "$INPUT_PATH" "$OUTPUT"
    sed -i "s|{APP_NAME}|${APP_NAME}|g" "$OUTPUT"
    # 确定NFS子路径(Deploy用当前构建号,Rollback用目标构建号)
    NFS_SUBPATH=$(if [ "${DEPLOY_ACTION}" = "Deploy" ]; then echo "${BUILD_NUMBER}"; else echo "${TARGET_BUILD}"; fi)
    sed -i "s|{NFS_SUBPATH}|${NFS_SUBPATH}|g" "$OUTPUT"
    # 打印并应用模板
    echo "Rendered Manifest"
    cat "$OUTPUT"
    echo "Applying to Kubernetes"
    kubectl apply -f "$OUTPUT"
    '''
    }
    }
    }
    # 流水线后置操作:更新LAST_SUCCESS文件(通过SSH写入NFS服务器)
    post {
    success {
    script {
    def finalVersion = (params.DEPLOY_ACTION == 'Deploy') ? env.BUILD_NUMBER : (params.ROLLBACK_TO_BUILD ?: env.TARGET_BUILD)
    # 通过SSH将最终版本写入NFS服务器的LAST_SUCCESS文件
    sh "ssh ${NFS_SERVER} 'echo ${finalVersion} > /opt/nfs/${APP_NAME}-test/LAST_SUCCESS'"
    echo "Action succeeded. Updated LAST_SUCCESS=${finalVersion}"
    }
    }
    failure {
    echo "Action failed. LAST_SUCCESS was not updated."
    }
    always {
    deleteDir() # 清理工作目录
    }
    }

    第四步:示例和验证

    完成Pipeline配置后,需通过Jenkins控制台输出、K8s集群状态及HTTP请求验证部署结果,确保PHP应用正常运行。

    1. Jenkins 控制台输出示例

    以dbb-live-api项目的第58次构建为例,控制台输出关键日志如下(仅展示核心阶段):

    [Pipeline] stage
    [Pipeline] {(Handle Rollback)
    Stage "Handle Rollback" skipped due to when conditional # 本次为Deploy动作,跳过回滚阶段
    [Pipeline] getContext
    [Pipeline]}
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] {(Fetch Deployment Template)
    [Pipeline] script
    [Pipeline]{
    [Pipeline] echo
    Cleaning workspace and cloning deployment repo via SSH # 清理工作目录
    [Pipeline] deleteDir
    [Pipeline]}
    [Pipeline] // script
    [Pipeline] sh
    + git config –global core.sshCommand ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
    + REPO_DIR=yyh-devops-repo
    + REPO_URL=git@gitlab.dbblive.com:kubernetes/yyh-devops.git
    + echo Cloning from git@gitlab.dbblive.com:kubernetes/yyh-devops.git into yyh-devops-repo
    Cloning from git@gitlab.dbblive.com:kubernetes/yyh-devops.git into yyh-devops-repo
    + git clone git@gitlab.dbblive.com:kubernetes/yyh-devops.git yyh-devops-repo
    Cloning into 'yyh-devops-repo'…
    Warning: Permanently added 'gitlab.dbblive.com' (ED25519) to the list of known hosts. # 首次连接GitLab,添加主机密钥
    + INPUT_PATH=yyh-devops-repo/dbbjt/php/dbb-live-api/k8s-deployment.yaml
    + [ ! -f yyh-devops-repo/dbbjt/php/dbb-live-api/k8s-deployment.yaml ] # 检查模板文件是否存在
    + echo Template repository cloned successfully
    Template repository cloned successfully # 模板拉取成功
    [Pipeline]}
    [Pipeline] // stage
    [Pipeline] stage
    [Pipeline] { (Render and Apply Kubernetes Manifest)
    [Pipeline] script
    [Pipeline]{
    [Pipeline] echo
    Final Variables: # 打印最终变量
    [Pipeline] echo
    APP NAME = dbb-live-api # 应用名
    [Pipeline] echo
    BUILD NUMBER = 58 # 构建号
    [Pipeline] echo

    2. K8s环境状态验证

    登录K8s Master节点,执行以下命令查看Pod状态(确保PHP应用和Nginx Pod正常运行):

    root@k8s-master:~# kubectl get pods -n test
    NAME READY STATUS RESTARTS AGE
    custom-nginx-75fc7b4476-r8d5x 1/1 Running 0 3h19m # Nginx Pod(运行正常)
    dbb-live-api-deployment-7c8f6cfbd5-fdjcv 2/2 Running 0 40m # PHP应用Pod(2个容器:PHP-FPM+Nginx,均运行正常)
    dbb-live-api-deployment-7c8f6cfbd5-fwdgp 2/2 Running 0 40m # PHP应用Pod(副本2,均运行正常)

    • READY 2/2:表示Pod内的2个容器(PHP-FPM和Nginx)均已就绪。
    • STATUS Running:表示Pod运行正常,无重启记录(RESTARTS=0)。

    3. 应用访问验证

    通过NodePort服务访问PHP应用的健康检查接口(如/api/v1/ping),验证应用是否正常响应:

    root@k8smaster:~# curl -I http://192.168.122.96:30082/api/v1/ping # 节点IP:NodePort/接口路径
    HTTP/1.1 200 OK # 响应码200,表示请求成功
    Server: nginx # 响应服务器为Nginx
    Date: Wed, 15 Oct 2025 08:50:25 GMT
    Content-Type: application/json # 响应类型为JSON(符合API预期)
    Connection: keepalive
    Cache-Control: nocache, private # 禁用缓存(API接口常用配置)
    Access-Control-Allow-Origin: * # 允许跨域访问(测试环境配置)
    X-Frame-Options: SAMEORIGIN # 防止点击劫持(安全头)
    X-Content-Type-Options: nosniff # 防止MIME类型嗅探(安全头)
    X-Permitted-Cross-Domain-Policies: none # 限制跨域策略(安全头)
    Referrer-Policy: noreferrer # 限制Referrer传递(安全头)
    Content-Security-Policy: defaultsrc 'self'; frameancestors 'self'; # 内容安全策略(安全头)
    Strict-Transport-Security: maxage=31536000 # 启用HSTS(强制HTTPS,安全头)

    • HTTP/1.1 200 OK:表示应用接口正常响应,无服务端错误。
    • 安全头:响应包含多个安全相关的HTTP头,提升应用安全性。

    第四步:最基础镜像制作

    基础镜像是PHP应用部署的基础,需包含Ubuntu 22.04、PHP 8.0、Composer及常用扩展,确保满足Laravel等PHP框架的运行需求。以下是镜像制作的完整步骤:

    1. 拉取基础镜像

    从Docker Hub拉取Ubuntu 22.04基础镜像(官方镜像,稳定性高):

    root@bocheng-System-Product-Name:~# docker pull ubuntu:22.04

    2. 创建并进入容器

    基于Ubuntu 22.04创建容器,进入容器环境进行配置:

    # 创建容器(-itd:交互式、终端、后台运行;–name:容器名)
    root@bocheng-System-Product-Name:~# docker run -itd –name base ubuntu:22.04 bash
    # 进入容器(exec:执行命令;-it:交互式终端)
    root@bocheng-System-Product-Name:~# docker exec -it base bash

    3. 容器内操作(安装依赖、配置PHP)

    在容器内执行以下命令,安装PHP 8.0、Composer及常用扩展,并配置PHP参数:

    3.1 检查初始环境(可选,确认容器基础状态)

    root@d3788c4ad31e:~# php -v # 初始状态无PHP,执行后会提示命令不存在
    root@d3788c4ad31e:~# composer -V # 初始状态无Composer,执行后会提示命令不存在

    3.2 配置系统源与时区

    # 设置环境变量:时区(Asia/Shanghai)、PHP内存限制、文件上传大小
    root@d3788c4ad31e:~# echo "设置时区与PHP默认内存限制"
    root@d3788c4ad31e:~# export DEBIAN_FRONTEND=noninteractive TZ=Asia/Shanghai PHP_MEMORY_LIMIT=256M UPLOAD_MAX_FILESIZE=50M POST_MAX_SIZE=100M

    # 替换Ubuntu软件源为阿里云(加速依赖安装)
    root@d3788c4ad31e:~# sed -i 's|http://archive.ubuntu.com/ubuntu/|http://mirrors.aliyun.com/ubuntu/|g' /etc/apt/sources.list && \\
    sed -i 's|http://security.ubuntu.com/ubuntu/|http://mirrors.aliyun.com/ubuntu/|g' /etc/apt/sources.list

    3.3 安装PHP 8.0及扩展

    # 更新APT缓存,安装软件源管理工具,添加PHP官方源
    root@d3788c4ad31e:~# apt-get update && \\
    apt-get install -y software-properties-common && \\
    add-apt-repository ppa:ondrej/php -y && \\ # Ondrej的PHP源,提供多个PHP版本
    apt-get update

    # 安装PHP 8.0及常用扩展(适配Laravel等框架)
    root@d3788c4ad31e:~# apt-get install -y \\
    php8.0-fpm \\ # PHP-FPM(FastCGI进程管理器,用于处理PHP请求)
    php8.0-cli \\ # PHP命令行工具(执行composer、artisan等命令)
    php8.0-common \\ # PHP核心扩展
    php8.0-curl \\ # 支持CURL请求(调用第三方API常用)
    php8.0-mbstring \\ # 支持多字节字符串(处理中文常用)
    php8.0-xml \\ # 支持XML解析(框架配置、API响应常用)
    php8.0-dom \\ # DOM扩展(XML操作常用)
    php8.0-zip \\ # 支持ZIP压缩/解压(Composer、文件上传常用)
    php8.0-pdo \\ # PDO扩展(数据库连接常用)
    php8.0-mysql \\ # MySQL PDO驱动(连接MySQL数据库)
    php8.0-redis \\ # Redis扩展(缓存、会话存储常用)
    php8.0-bcmath \\ # BCMath扩展(高精度数学计算常用)
    php8.0-gd \\ # GD扩展(图片处理常用)
    php8.0-opcache \\ # OPcache扩展(PHP代码缓存,提升性能)
    php8.0-intl \\ # Intl扩展(国际化处理常用)
    php8.0-tokenizer \\ # Tokenizer扩展(框架自动加载常用)
    php8.0-json \\ # JSON扩展(处理JSON数据常用)
    php8.0-fileinfo # Fileinfo扩展(获取文件信息常用)

    3.4 配置PHP参数(php.ini)

    # 修改php.ini配置:内存限制、文件上传大小、时区
    root@d3788c4ad31e:~# sed -i 's/memory_limit = .*/memory_limit = 256M/' /etc/php/8.0/fpm/php.ini && \\
    sed -i 's/upload_max_filesize = .*/upload_max_filesize = 50M/' /etc/php/8.0/fpm/php.ini && \\
    sed -i 's/post_max_size = .*/post_max_size = 100M/' /etc/php/8.0/fpm/php.ini && \\
    sed -i 's/;date.timezone.*/date.timezone = Asia\\/Shanghai/' /etc/php/8.0/fpm/php.ini

    3.5 启用OPcache优化(提升PHP性能)

    root@d3788c4ad31e:~# echo "启用OPcache优化"
    # 向OPcache配置文件添加优化参数
    root@d3788c4ad31e:~# echo "opcache.enable=1" >> /etc/php/8.0/fpm/conf.d/10-opcache.ini && \\
    echo "opcache.enable_cli=1" >> /etc/php/8.0/fpm/conf.d/10-opcache.ini && \\ # CLI模式启用OPcache
    echo "opcache.memory_consumption=256" >> /etc/php/8.0/fpm/conf.d/10-opcache.ini && \\ # OPcache内存大小(256M)
    echo "opcache.max_accelerated_files=20000" >> /etc/php/8.0/fpm/conf.d/10-opcache.ini && \\ # 最大缓存文件数
    echo "opcache.validate_timestamps=0" >> /etc/php/8.0/fpm/conf.d/10-opcache.ini # 禁用文件时间戳检查(生产环境推荐)

    3.6 配置PHP-FPM监听地址(www.conf)

    # 修改PHP-FPM监听地址为127.0.0.1:9000(Nginx反向代理目标地址)
    root@d3788c4ad31e:~# sed -i 's/listen = .*/listen = 127.0.0.1:9000/' /etc/php/8.0/fpm/pool.d/www.conf && \\
    sed -i 's/;listen.owner/listen.owner/' /etc/php/8.0/fpm/pool.d/www.conf && \\ # 启用监听用户
    sed -i 's/;listen.group/listen.group/' /etc/php/8.0/fpm/pool.d/www.conf && \\ # 启用监听组
    sed -i 's/;listen.mode/listen.mode/' /etc/php/8.0/fpm/pool.d/www.conf # 启用监听权限

    3.7 安装Composer(PHP依赖管理工具)

    # 下载Composer 2.0.14(指定版本,避免兼容性问题)
    root@d3788c4ad31e:~# curl -sS https://getcomposer.org/installer | php8.0 — –version=2.0.14 –install-dir=/usr/local/bin –filename=composer

    # 配置Composer阿里云镜像(加速依赖下载)
    root@d3788c4ad31e:~# composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

    3.8 验证安装结果

    # 验证PHP版本(应显示PHP 8.0.x)
    root@d3788c4ad31e:~# php -v
    PHP 8.0.x (fpm-fcgi) (built: ...)
    Copyright (c) The PHP Group
    Zend Engine v4.0.0, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.0, Copyright (c), by Zend Technologies

    # 验证Composer版本(应显示2.0.14)
    root@d3788c4ad31e:~# composer -V
    Composer version 2.0.14 2021-01-06 11:12:37

    # 验证PHP扩展(确保json、redis等扩展已安装)
    root@d3788c4ad31e:~# php -m | grep -i json
    json
    root@d3788c4ad31e:~# php -m | grep -i redis
    redis

    # 验证Nginx版本(后续安装,此处先确认)
    root@d3788c4ad31e:~# nginx -v
    nginx version: nginx/1.18.0 (Ubuntu)

    3.9 安装其他工具(Nginx、Supervisor等)

    # 安装Nginx(处理静态资源、反向代理)、Supervisor(进程管理)、vim(编辑)、git(代码拉取)、unzip(解压)
    root@d3788c4ad31e:~# apt-get install -y nginx supervisor vim git unzip

    3.10 清理缓存(减少镜像体积)

    # 清理APT缓存和临时文件,降低镜像大小
    root@d3788c4ad31e:~# apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

    4. 提交为自定义镜像

    退出容器,将配置好的容器提交为自定义镜像(推送到华为云容器仓库):

    # 退出容器
    root@d3788c4ad31e:~# exit

    # 提交容器为镜像(base为容器名,镜像标签含华为云仓库地址)
    root@bocheng-System-Product-Name:~# docker commit base swr.cn-east-3.myhuaweicloud.com/bocheng-test/ubuntu22.04:v1

    第五步:服务基础镜像制作

    基于第四步的基础镜像,分别制作Nginx(适配Laravel框架)和PHP-FPM(优化配置)的服务镜像,用于K8s部署。

    一、Nginx镜像制作(适配Laravel框架)

    Laravel框架的Nginx配置需支持URL重写(隐藏index.php)和安全头,以下是镜像制作的目录结构、配置文件及脚本:

    1. 目录结构

    执行以下命令查看Nginx镜像制作的目录结构:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/nginx# tree -L 1
    .
    ├── action.sh # 镜像构建与推送脚本
    ├── Dockerfile # Nginx镜像构建文件
    ├── laravel.conf # Laravel框架的虚拟主机配置
    └── nginx.conf # Nginx主配置文件
    0 directories, 4 files

    2. Nginx主配置(nginx.conf)

    该配置优化Nginx性能,支持Laravel框架的静态资源处理和反向代理,关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/nginx# cat nginx.conf
    user root; # Nginx运行用户(root,避免权限问题)
    worker_processes auto; # 工作进程数(自动匹配CPU核心)
    error_log /var/log/nginx/error.log warn; # 错误日志路径及级别(warn:记录警告及以上错误)
    worker_rlimit_nofile 51200; # 单个工作进程最大文件句柄数(提升并发)

    events {
    use epoll; # 事件模型(Linux高性能模型)
    worker_connections 51200; # 单个工作进程最大连接数
    multi_accept on; # 允许同时接受多个连接
    }

    http {
    include /etc/nginx/mime.types; # 引入MIME类型配置
    default_type application/octet-stream; # 默认MIME类型

    # 日志格式:包含客户端IP、请求时间、响应时间等(便于问题排查)
    log_format main '$remote_addr$remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for" '
    'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';

    access_log off; # 全局禁用访问日志(虚拟主机可单独启用)
    log_not_found off; # 禁用404日志(减少日志量)
    log_subrequest off; # 禁用子请求日志(减少日志量)

    # 临时目录配置(使用tmpfs,避免磁盘IO)
    client_body_temp_path /tmp/client_body;
    proxy_temp_path /tmp/proxy_temp;
    fastcgi_temp_path /tmp/fastcgi_temp;
    uwsgi_temp_path /tmp/uwsgi_temp;
    scgi_temp_path /tmp/scgi_temp;

    sendfile on; # 启用零拷贝传输(提升文件传输性能)
    tcp_nopush on; # 合并小包(提升网络效率)
    tcp_nodelay on; # 禁用Nagle算法(降低延迟)
    keepalive_timeout 65; # TCP连接保持时间(65秒)
    keepalive_requests 100; # 单个连接最大请求数(100次)
    types_hash_max_size 2048; # 类型哈希表大小(支持更多MIME类型)

    client_max_body_size 50m; # 客户端最大请求体大小(50M,支持大文件上传)
    client_header_buffer_size 16k; # 请求头缓存大小(16k)
    large_client_header_buffers 4 32k; # 大请求头缓存(4个32k缓冲区)

    # Gzip压缩配置(提升静态资源传输效率)
    gzip on;
    gzip_min_length 1k; # 最小压缩文件大小(1k以下不压缩)
    gzip_buffers 4 16k; # 压缩缓冲区(4个16k)
    gzip_http_version 1.1; # 支持HTTP 1.1
    gzip_comp_level 2; # 压缩级别(2级,平衡压缩率与性能)
    gzip_types # 压缩的文件类型(含JS、CSS、XML等)
    text/plain
    text/css
    text/xml
    text/javascript
    application/javascript
    application/x-javascript
    application/xml
    application/json
    image/svg+xml;
    gzip_vary on; # 启用Vary头(告知浏览器是否支持压缩)
    gzip_proxied expired no-cache no-store private auth; # 代理请求压缩策略
    gzip_disable "MSIE [1-6]\\."; # 禁用IE6及以下压缩(兼容性问题)

    # FastCGI配置(优化PHP-FPM通信)
    fastcgi_connect_timeout 300; # 连接超时(300秒)
    fastcgi_send_timeout 300; # 发送超时(300秒)
    fastcgi_read_timeout 300; # 读取超时(300秒)
    fastcgi_buffer_size 64k; # 缓冲区大小(64k)
    fastcgi_buffers 4 64k; # 缓冲区数量及大小(4个64k)
    fastcgi_busy_buffers_size 128k; # 繁忙缓冲区大小(128k)
    fastcgi_temp_file_write_size 256k; # 临时文件写入大小(256k)
    fastcgi_intercept_errors on; # 拦截PHP错误(传递到Nginx处理)

    # 连接限制(防止恶意请求)
    limit_conn_zone $binary_remote_addr zone=perip:10m; # 按IP限制(10M内存)
    limit_conn_zone $server_name zone=perserver:10m; # 按服务器名限制(10M内存)

    server_tokens off; # 禁用Nginx版本显示(提升安全性)

    include /etc/nginx/conf.d/*.conf; # 引入虚拟主机配置
    include /etc/nginx/sites-enabled/*; # 引入启用的站点配置(可选)
    }

    3. Laravel虚拟主机配置(laravel.conf)

    该配置支持Laravel框架的URL重写(隐藏index.php)、安全头及静态资源缓存,关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/nginx# cat laravel.conf
    # /etc/nginx/conf.d/default.conf(Laravel框架的虚拟主机配置)

    server {
    listen 80; # 监听HTTP 80端口
    root /var/www/html/public; # Laravel项目根目录(public目录,安全目录)
    index index.php index.html; # 默认首页(优先index.php)

    # 错误日志(记录Laravel相关错误,便于排查)
    error_log /var/log/nginx/laravel_error.log warn;
    access_log off; # 禁用访问日志(减少日志量)

    # URL重写:隐藏index.php(Laravel框架必需)
    location / {
    try_files $uri $uri/ /index.php?$query_string; # 尝试访问文件→目录→重写为index.php
    }

    # PHP文件处理:反向代理到PHP-FPM(127.0.0.1:9000)
    location ~ \\.php$ {
    fastcgi_pass 127.0.0.1:9000; # PHP-FPM地址(同一Pod内的PHP容器)
    fastcgi_index index.php; # 默认PHP入口文件
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # PHP脚本路径(必需)
    include fastcgi_params; # 引入FastCGI参数(定义环境变量)

    # 安全头:告知PHP当前请求是否为HTTPS(根据实际环境调整)
    fastcgi_param HTTPS off;
    }

    # 安全头配置(提升应用安全性,所有响应均添加)
    add_header X-Frame-Options "SAMEORIGIN" always; # 防止点击劫持
    add_header X-Content-Type-Options "nosniff" always; # 防止MIME类型嗅探
    add_header X-Permitted-Cross-Domain-Policies "none" always; # 限制跨域策略
    add_header Referrer-Policy "no-referrer" always; # 限制Referrer传递
    add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'self';" always; # 内容安全策略

    # 禁止访问.htaccess文件(Apache配置文件,Nginx无需处理)
    location ~ /\\.ht {
    deny all; # 拒绝所有访问
    }

    # 健康检查接口(可选,用于K8s存活探针)
    location = /health {
    try_files /health.php =200; # 尝试访问health.php,不存在则返回200
    }

    # 禁止访问敏感文件/目录(如.env、.git等,避免信息泄露)
    location ~ ^/(\\.user.ini|\\.htaccess|\\.git|\\.env|\\.svn|\\.project|LICENSE|README.md)
    {
    return 404; # 返回404,隐藏文件存在
    }

    # Let's Encrypt证书验证目录(申请SSL证书时使用,无需删除)
    location ~ \\.well-known{
    allow all; # 允许所有访问
    }

    # 禁止在证书验证目录放入敏感文件(防止恶意上传)
    if ( $uri ~ "^/\\.well-known/.*\\.(php|jsp|py|js|css|lua|ts|go|zip|tar\\.gz|rar|7z|sql|bak)$" ) {
    return 403; # 返回403,禁止访问敏感文件类型
    }

    # 静态资源处理:图片文件(GIF/JPG等),设置30天缓存
    location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
    {
    expires 30d; # 浏览器缓存30天
    error_log /dev/null; # 禁用错误日志
    access_log /dev/null; # 禁用访问日志
    }

    # 静态资源处理:JS/CSS文件,设置12小时缓存
    location ~ .*\\.(js|css)?$
    {
    expires 12h; # 浏览器缓存12小时
    error_log /dev/null; # 禁用错误日志
    access_log /dev/null; # 禁用访问日志
    }

    # (可选)Let's Encrypt证书验证配置(如需启用HTTPS,取消注释)
    # location ~ /\\.well-known/acme-challenge {
    # allow all;
    # }
    }

    4. Dockerfile(Nginx镜像构建文件)

    基于第四步的基础镜像(ubuntu22.04:v1),添加Nginx配置并设置启动命令,关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/nginx# cat Dockerfile
    # 基础镜像:第四步制作的ubuntu22.04:v1(含Nginx、PHP依赖)
    FROM swr.cn-east-3.myhuaweicloud.com/bocheng-test/ubuntu22.04:v1 AS nginx

    # 环境变量:禁用APT交互模式(安装依赖时无需手动确认)
    ENV DEBIAN_FRONTEND=noninteractive

    # 安装Nginx(基础镜像已安装,此处确保版本一致,可选)
    RUN apt-get update && \\
    apt-get install -y nginx && \\
    rm -rf /var/lib/apt/lists/* && \\ # 清理APT缓存
    mkdir -p /var/log/nginx && \\ # 创建Nginx日志目录
    touch /var/log/nginx/access.log /var/log/nginx/error.log # 创建日志文件(避免启动报错)

    # 删除Nginx默认站点配置(避免冲突,使用自定义配置)
    RUN rm -f /etc/nginx/sites-enabled/default

    # 复制自定义配置到容器:主配置和Laravel虚拟主机配置
    COPY nginx.conf /etc/nginx/nginx.conf
    COPY laravel.conf /etc/nginx/conf.d/laravel.conf

    # 设置工作目录为Laravel项目根目录
    WORKDIR /var/www/html
    # 暴露Nginx HTTP端口(80)
    EXPOSE 80

    # 启动命令:前台运行Nginx(确保容器不退出)
    CMD ["/usr/sbin/nginx", "-g", "daemon off;"]

    5. 镜像构建与推送脚本(action.sh)

    该脚本自动化构建Nginx镜像并推送到华为云容器仓库,关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/nginx# cat action.sh
    #!/bin/bash
    # 构建Nginx镜像:-f指定Dockerfile,-t指定镜像标签(含华为云仓库地址)
    docker build -f Dockerfile -t swr.cn-east-3.myhuaweicloud.com/bocheng-test/nginx-laravel:base_v2 .
    # 推送镜像到华为云仓库(需提前登录仓库:docker login swr.cn-east-3.myhuaweicloud.com)
    docker push swr.cn-east-3.myhuaweicloud.com/bocheng-test/nginx-laravel:base_v2

    二、PHP-FPM镜像制作(优化配置)

    基于第四步的基础镜像,优化PHP-FPM配置(如进程数、日志、安全参数),满足高并发场景需求,以下是目录结构、配置文件及脚本:

    1. 目录结构

    执行以下命令查看PHP-FPM镜像制作的目录结构:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/php-fpm# tree -L 3
    .
    ├── action.sh # 镜像构建与推送脚本
    ├── Dockerfile # PHP-FPM镜像构建文件
    ├── php-fpm # 旧配置目录(可选,用于备份)
    │ ├── php-fpm.conf
    │ └── pool.d
    │ └── www.conf
    ├── php-fpm.conf # PHP-FPM主配置(新配置)
    ├── php.ini # PHP配置(新配置)
    └── pool.d # PHP-FPM池配置目录(新配置)
    └── www.conf # www池配置(处理PHP请求的核心配置)

    2. PHP-FPM主配置(php-fpm.conf)

    该配置定义PHP-FPM的全局参数(如PID文件、日志路径),关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/php-fpm# cat php-fpm/php-fpm.conf
    # 引入池配置(所有池配置文件均在pool.d目录下)
    include=/etc/php/8.0/fpm/pool.d/*.conf

    [global]
    pid = /run/php/php8.0-fpm.pid # PHP-FPM进程PID文件路径
    error_log = /var/log/php-fpm/php8.0-fpm.log # 全局错误日志路径
    log_level = notice # 日志级别(notice:记录通知及以上错误)

    3. PHP-FPM池配置(www.conf)

    该配置定义www池的进程管理、监听地址、资源限制等,适配高并发场景,关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/php-fpm# cat php-fpm/pool.d/www.conf
    [www] # 池名称(默认www池,可自定义)
    listen = 0.0.0.0:9000 # 监听地址(0.0.0.0:9000,允许外部容器访问)
    listen.backlog = 8192 # 监听队列大小(8192,提升并发处理能力)

    user = www-data # 进程运行用户(www-data,低权限用户,提升安全性)
    group = www-data # 进程运行组(www-data)

    pm = static # 进程管理模式(static:固定进程数,适合高并发)
    pm.max_children = 200 # 最大子进程数(根据CPU核心和内存调整,200适合4核8G机器)
    pm.start_servers = 15 # 启动时的子进程数
    pm.min_spare_servers = 15 # 最小空闲子进程数(确保有足够进程处理请求)
    pm.max_spare_servers = 50 # 最大空闲子进程数(避免空闲进程过多占用资源)

    pm.status_path = /phpfpm_80_status # PHP-FPM状态查询路径(用于监控)

    request_terminate_timeout = 30 # 请求超时时间(30秒,防止慢请求占用进程)
    request_slowlog_timeout = 30 # 慢日志超时时间(30秒,超过则记录慢日志)
    slowlog = /var/log/php-fpm/slow.log # 慢日志路径(排查慢请求)
    # 限制PHP可访问的目录(open_basedir,提升安全性,避免访问敏感目录)
    php_admin_value[open_basedir] = /var/www/html:/tmp:/run

    4. PHP配置(php.ini)

    该配置优化PHP运行参数(如内存限制、安全参数、扩展启用),关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/php-fpm# cat php.ini
    [PHP]
    engine = On # 启用PHP引擎
    short_open_tag = Off # 禁用短标签(<? ?>,推荐使用<?php ?>)
    precision = 14 # 浮点数精度
    output_buffering = 4096 # 输出缓冲大小(4096字节,提升性能)
    zlib.output_compression = Off # 禁用Zlib输出压缩(Nginx已处理)
    implicit_flush = Off # 禁用隐式刷新(依赖输出缓冲)
    unserialize_callback_func = # 反序列化回调函数(未设置)
    serialize_precision = -1 # 序列化精度(-1表示自动)
    # 禁用危险函数(如exec、system,防止命令注入)
    disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,popen,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
    disable_classes = # 禁用类(未设置)
    zend.enable_gc = On # 启用Zend垃圾回收
    zend.exception_ignore_args = On # 异常信息忽略参数(避免敏感信息泄露)
    zend.exception_string_param_max_len = 0 # 异常字符串参数最大长度(0表示无限制)
    expose_php = Off # 禁用X-Powered-By头(隐藏PHP版本,提升安全性)
    max_execution_time = 15 # 脚本最大执行时间(15秒,防止长时间占用CPU)
    max_input_time = 30 # 接收请求数据的最大时间(30秒)
    memory_limit = 512M # PHP内存限制(512M,适配大型框架)
    # 错误处理:仅记录错误,不显示(生产环境推荐)
    error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_NOTICE
    display_errors = Off # 禁用错误显示(避免敏感信息泄露)
    display_startup_errors = Off # 禁用启动错误显示
    log_errors = On # 启用错误日志记录
    log_errors_max_len = 1024 # 单条错误日志最大长度(1024字节)
    ignore_repeated_errors = On # 忽略重复错误
    ignore_repeated_source = On # 忽略重复错误源
    report_memleaks = On # 报告内存泄漏
    variables_order = GPCS # 变量顺序(Get/Post/Cookie/Server)
    request_order = GP # 请求变量顺序(Get/Post)
    register_argc_argv = Off # 禁用$_argc和$_argv(减少内存占用)
    auto_globals_jit = On # 自动全局变量即时生成(提升性能)
    post_max_size = 50M # POST数据最大大小(50M,与upload_max_filesize匹配)
    auto_prepend_file = # 自动前置文件(未设置)
    auto_append_file = # 自动后置文件(未设置)
    default_mimetype = text/html # 默认MIME类型
    default_charset = UTF-8 # 默认字符集(UTF-8)
    doc_root = # 文档根目录(未设置,使用虚拟主机配置)
    user_dir = # 用户目录(未设置)
    enable_dl = Off # 禁用dl()函数(防止加载恶意扩展)
    cgi.fix_pathinfo = 0 # 修复CGI路径信息(0表示禁用,提升安全性)
    file_uploads = On # 启用文件上传
    upload_max_filesize = 50M # 单个文件最大上传大小(50M)
    max_file_uploads = 20 # 单次请求最大上传文件数(20个)
    allow_url_fopen = On # 允许通过URL打开文件(框架依赖)
    allow_url_include = Off # 禁用通过URL包含文件(防止远程代码执行)
    default_socket_timeout = 5 # 默认Socket超时时间(5秒)

    [Date]
    date.timezone = Asia/Shanghai # 时区(上海,避免时间错误)

    [Session]
    session.save_handler = redis # 会话存储驱动(Redis,适合分布式环境)
    # Redis会话存储路径(连接本地Redis,持久化1秒,超时2秒)
    session.save_path = tcp://127.0.0.1:6379?persistent=1&timeout=2
    session.use_strict_mode = 1 # 启用会话严格模式(防止会话固定攻击)
    session.use_cookies = 1 # 使用Cookie存储会话ID
    session.use_only_cookies = 1 # 仅使用Cookie存储会话ID(防止URL泄露)
    session.name = PHPSESSID # 会话ID名称
    session.auto_start = 0 # 禁用会话自动启动(框架手动启动)
    session.cookie_lifetime = 0 # Cookie生命周期(0表示会话结束后失效)
    session.cookie_path = / # Cookie路径(根目录)
    session.cookie_domain = # Cookie域名(未设置,自动匹配)
    session.cookie_httponly = 1 # 启用HttpOnly(防止JS获取Cookie,提升安全性)
    session.cookie_samesite = Lax # SameSite属性(Lax,防止CSRF攻击)
    session.serialize_handler = php # 会话序列化处理器
    session.gc_probability = 0 # 垃圾回收概率(0表示禁用,Redis自动清理)
    session.gc_divisor = 1000 # 垃圾回收除数
    session.gc_maxlifetime = 1440 # 会话最大生命周期(1440秒=24分钟)
    session.cache_limiter = nocache # 缓存限制(禁用缓存,适合动态内容)
    session.cache_expire = 180 # 缓存过期时间(180秒)
    session.use_trans_sid = 0 # 禁用URL传递会话ID(防止泄露)
    session.sid_length = 48 # 会话ID长度(48位,提升安全性)
    session.trans_sid_tags = a=href,area=href,frame=src,form= # 允许传递会话ID的标签
    session.sid_bits_per_character = 6 # 会话ID每字符位数(6位,支持64种组合)

    [Assertion]
    zend.assertions = -1 # 禁用断言(生产环境推荐)

    [opcache]
    opcache.enable = 1 # 启用OPcache(提升PHP性能)
    opcache.enable_cli = 0 # 禁用CLI模式OPcache(CLI每次执行脚本不同,无需缓存)
    opcache.memory_consumption = 512 # OPcache内存大小(512M,缓存更多文件)
    opcache.interned_strings_buffer = 64 # 字符串缓存大小(64M)
    opcache.max_accelerated_files = 80000 # 最大缓存文件数(80000,适配大型项目)
    opcache.validate_timestamps = 0 # 禁用文件时间戳检查(生产环境推荐,避免IO)
    opcache.revalidate_freq = 0 # 重验证频率(0表示不重验证)
    opcache.fast_shutdown = 1 # 启用快速关闭(提升进程退出速度)
    opcache.save_comments = 0 # 禁用保存注释(减少内存占用)
    opcache.load_comments = 0 # 禁用加载注释
    opcache.jit_buffer_size = 256M # JIT缓冲区大小(256M,PHP 8.0+支持)
    opcache.jit = 1255 # JIT模式(1255表示启用所有优化)

    [curl]
    curl.cainfo = /etc/pki/tls/certs/ca-bundle.crt # CURL CA证书路径(支持HTTPS)

    [openssl]
    openssl.cafile = /etc/pki/tls/certs/ca-bundle.crt # OpenSSL CA证书路径(支持HTTPS)

    [mysqlnd]
    mysqlnd.collect_statistics = Off # 禁用MySQL统计收集(提升性能)
    mysqlnd.collect_memory_statistics = Off # 禁用MySQL内存统计收集

    [PostgreSQL]
    pgsql.allow_persistent = On # 允许PostgreSQL持久连接
    pgsql.auto_reset_persistent = Off # 禁用持久连接自动重置
    pgsql.max_persistent = -1 # 最大持久连接数(-1表示无限制)
    pgsql.max_links = -1 # 最大连接数(-1表示无限制)
    pgsql.ignore_notice = 0 # 不忽略PostgreSQL通知
    pgsql.log_notice = 0 # 不记录PostgreSQL通知

    [bcmath]
    bcmath.scale = 0 # BCMath默认小数位数(0位)

    [Tidy]
    tidy.clean_output = Off # 禁用Tidy清理输出

    [CLI Server]
    cli_server.color = On # CLI服务器启用颜色输出

    [Pdo_mysql]
    pdo_mysql.default_socket = # PDO MySQL默认Socket(未设置,使用TCP连接)

    [ffi]
    ffi.enable = false # 禁用FFI(避免安全风险)

    [Extension]
    extension = zip.so # 启用ZIP扩展
    extension = fileinfo.so # 启用Fileinfo扩展
    extension = redis.so # 启用Redis扩展
    zend_extension = /usr/lib/php/20200930/opcache.so # 启用OPcache扩展(Zend扩展)

    5. Dockerfile(PHP-FPM镜像构建文件)

    基于第四步的基础镜像,替换PHP-FPM配置并设置启动命令,关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/php-fpm# cat Dockerfile
    # 基础镜像:第四步制作的ubuntu22.04:v1(含PHP 8.0、Composer)
    FROM swr.cn-east-3.myhuaweicloud.com/bocheng-test/ubuntu22.04:v1 AS php-fpm

    # 创建www-data用户和组(若基础镜像未创建,确保UID/GID为82,与Nginx一致)
    RUN groupadd -g 82 www-data || true && \\
    useradd -r -u 82 -g www-data www-data || true

    # 环境变量:禁用APT交互模式
    ENV DEBIAN_FRONTEND=noninteractive

    # 复制自定义配置到容器:PHP-FPM主配置、池配置、php.ini
    COPY php-fpm.conf /etc/php/8.0/fpm/php-fpm.conf
    COPY pool.d/www.conf /etc/php/8.0/fpm/pool.d/www.conf
    COPY php.ini /etc/php/8.0/fpm/php.ini

    # 创建PHP-FPM运行必需的目录(PID目录、日志目录)
    RUN mkdir -p /run/php /var/log && \\
    chmod -R 755 /var/log && \\ # 赋予日志目录读写权限
    mkdir -p /var/log/php-fpm/ # 创建PHP-FPM日志目录

    # 创建软链接:解决php-fpm命令路径问题(确保全局可调用)
    RUN ln -sf /usr/sbin/php-fpm8.0 /usr/local/bin/php-fpm && \\
    ln -sf /usr/sbin/php-fpm8.0 /usr/sbin/php-fpm

    # 设置工作目录为Laravel项目根目录
    WORKDIR /var/www/html
    # 暴露PHP-FPM端口(9000)
    EXPOSE 9000

    # 启动命令:前台运行PHP-FPM(–nodaemonize,确保容器不退出)
    CMD ["php-fpm", "–nodaemonize"]

    6. 镜像构建与推送脚本(action.sh)

    该脚本自动化构建PHP-FPM镜像并推送到华为云容器仓库,关键参数已添加注释:

    root@bocheng-System-Product-Name:/mnt/2025-10-10-v3/dockerfiles/php-fpm# cat action.sh
    #!/bin/bash
    # 构建PHP-FPM镜像:-f指定Dockerfile,-t指定镜像标签(含华为云仓库地址)
    docker build -f Dockerfile -t

    赞(0)
    未经允许不得转载:171主机测评 » PHP项目Kubernetes部署与Jenkins CI/CD流水线
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址