文章目录
- [K8s 中部署 LNMP 架构 WordPress 博客(命令与配置逐条详解)](#K8s 中部署 LNMP 架构 WordPress 博客(命令与配置逐条详解))
-
- 项目需求
- 博客站点架构
- [步骤 1:创建命名空间](#步骤 1:创建命名空间)
- [步骤 2:部署 NFS 服务器](#步骤 2:部署 NFS 服务器)
-
- [2.1 部署 NFS 服务(NFS 服务器节点操作)](#2.1 部署 NFS 服务(NFS 服务器节点操作))
- [2.2 部署 NFS 客户端(所有 K8s 节点操作)](#2.2 部署 NFS 客户端(所有 K8s 节点操作))
- [步骤 3:部署 NFS Provisioner](#步骤 3:部署 NFS Provisioner)
-
- [3.1 创建 RBAC 权限](#3.1 创建 RBAC 权限)
- [3.2 部署 NFS Provisioner 应用](#3.2 部署 NFS Provisioner 应用)
- [3.3 创建 NFS StorageClass](#3.3 创建 NFS StorageClass)
- [步骤 4:部署 MySQL](#步骤 4:部署 MySQL)
-
- [4.1 创建 Secret](#4.1 创建 Secret)
- [4.2 创建 Service](#4.2 创建 Service)
- [4.3 部署 MySQL(StatefulSet)](#4.3 部署 MySQL(StatefulSet))
- [步骤 5:部署 PHP](#步骤 5:部署 PHP)
-
- [5.1 创建 PVC](#5.1 创建 PVC)
- [5.2 创建 Service(PHP)](#5.2 创建 Service(PHP))
- [5.3 部署 PHP(Deployment)](#5.3 部署 PHP(Deployment))
- [步骤 6:部署 Nginx](#步骤 6:部署 Nginx)
-
- [6.1 创建 Nginx 配置(ConfigMap)](#6.1 创建 Nginx 配置(ConfigMap))
- [6.2 创建 Nginx 证书(openssl 自签名证书)](#6.2 创建 Nginx 证书(openssl 自签名证书))
- [6.3 创建 Service(Nginx)](#6.3 创建 Service(Nginx))
- [6.4 部署 Nginx(Deployment)](#6.4 部署 Nginx(Deployment))
- [6.5 部署 WordPress 代码](#6.5 部署 WordPress 代码)
- [步骤 7:部署 LoadBalancer(MetalLB)](#步骤 7:部署 LoadBalancer(MetalLB))
- [步骤 8:部署 Ingress](#步骤 8:部署 Ingress)
- [步骤 9:初始化站点](#步骤 9:初始化站点)
- 部署小结
K8s 中部署 LNMP 架构 WordPress 博客(命令与配置逐条详解)
项目需求
在 K8s 的 wordpress 命名空间中部署 LNMP 架构 WordPress 博客,具体需求如下:
- 部署 NFS 服务器,提供共享目录
/wordpress - 部署 NFS 动态卷制备(NFS Provisioner),存储类型(StorageClass)为
wordpress - 部署 MySQL 应用:
- 使用 StatefulSet 部署,基于
mysql:5.7镜像,副本数 1 个 - 存储由 NFS 动态卷制备提供
- 使用 Secret 存储 MySQL 用户
root密码、WordPress 应用用户名wordpress、密码liujn@123、数据库wordpress - 额外配置一个 Service,用于暴露 MySQL 应用
- 使用 StatefulSet 部署,基于
- 部署 PHP 应用:
- 使用 Deployment 部署,基于
php:7.2-fpm镜像,副本数 2 个 - 使用 Service(ClusterIP 类型)暴露
- 使用 Deployment 部署,基于
- 部署 WordPress 应用:
- 使用 Deployment 部署,基于
nginx:1.24镜像,副本数 2 个 - 存储由 NFS 动态卷制备提供
- 使用 Ingress 暴露,域名
blog.liujn.cloud - 使用 ConfigMap 存储 Nginx 配置文件,使用 Secret 存储 Nginx 服务 TLS 自签名证书和私钥
- 使用 Deployment 部署,基于
- 部署 Metrics Server 监控 WordPress 相关资源
- 针对 PHP 和 WordPress 应用,配置 HPA 规则,最小 2 个 Pod,最大 5 个 Pod
博客站点架构
#mermaid-svg-gfqkWwxGFbCiahEZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gfqkWwxGFbCiahEZ .error-icon{fill:#552222;}#mermaid-svg-gfqkWwxGFbCiahEZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gfqkWwxGFbCiahEZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gfqkWwxGFbCiahEZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gfqkWwxGFbCiahEZ .marker.cross{stroke:#333333;}#mermaid-svg-gfqkWwxGFbCiahEZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gfqkWwxGFbCiahEZ p{margin:0;}#mermaid-svg-gfqkWwxGFbCiahEZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gfqkWwxGFbCiahEZ .cluster-label text{fill:#333;}#mermaid-svg-gfqkWwxGFbCiahEZ .cluster-label span{color:#333;}#mermaid-svg-gfqkWwxGFbCiahEZ .cluster-label span p{background-color:transparent;}#mermaid-svg-gfqkWwxGFbCiahEZ .label text,#mermaid-svg-gfqkWwxGFbCiahEZ span{fill:#333;color:#333;}#mermaid-svg-gfqkWwxGFbCiahEZ .node rect,#mermaid-svg-gfqkWwxGFbCiahEZ .node circle,#mermaid-svg-gfqkWwxGFbCiahEZ .node ellipse,#mermaid-svg-gfqkWwxGFbCiahEZ .node polygon,#mermaid-svg-gfqkWwxGFbCiahEZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gfqkWwxGFbCiahEZ .rough-node .label text,#mermaid-svg-gfqkWwxGFbCiahEZ .node .label text,#mermaid-svg-gfqkWwxGFbCiahEZ .image-shape .label,#mermaid-svg-gfqkWwxGFbCiahEZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-gfqkWwxGFbCiahEZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gfqkWwxGFbCiahEZ .rough-node .label,#mermaid-svg-gfqkWwxGFbCiahEZ .node .label,#mermaid-svg-gfqkWwxGFbCiahEZ .image-shape .label,#mermaid-svg-gfqkWwxGFbCiahEZ .icon-shape .label{text-align:center;}#mermaid-svg-gfqkWwxGFbCiahEZ .node.clickable{cursor:pointer;}#mermaid-svg-gfqkWwxGFbCiahEZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gfqkWwxGFbCiahEZ .arrowheadPath{fill:#333333;}#mermaid-svg-gfqkWwxGFbCiahEZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gfqkWwxGFbCiahEZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gfqkWwxGFbCiahEZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gfqkWwxGFbCiahEZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gfqkWwxGFbCiahEZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gfqkWwxGFbCiahEZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gfqkWwxGFbCiahEZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gfqkWwxGFbCiahEZ .cluster text{fill:#333;}#mermaid-svg-gfqkWwxGFbCiahEZ .cluster span{color:#333;}#mermaid-svg-gfqkWwxGFbCiahEZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gfqkWwxGFbCiahEZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gfqkWwxGFbCiahEZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-gfqkWwxGFbCiahEZ .icon-shape,#mermaid-svg-gfqkWwxGFbCiahEZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gfqkWwxGFbCiahEZ .icon-shape p,#mermaid-svg-gfqkWwxGFbCiahEZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gfqkWwxGFbCiahEZ .icon-shape .label rect,#mermaid-svg-gfqkWwxGFbCiahEZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gfqkWwxGFbCiahEZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gfqkWwxGFbCiahEZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gfqkWwxGFbCiahEZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} NFS服务器10.1.8.30 /wordpress
动态卷制备
PVC-mysql
PVC-wordpress
Secret: mysql存储专用账号密码
Statefulset: mysql副本数:1
Deployment: php副本数:2
Deployment: nginx副本数:2
Service: mysql 类型:ClusterIP
Service: php 类型:ClusterIP
Service: nginx 类型:ClusterIP
Ingress: blog.liujn.cloud
ConfigMap: nginx-config
Secret: nginx-tls
架构解读 :NFS 服务器提供共享目录 → NFS Provisioner 实现动态卷制备(PV)→ 检测到 PVC 请求后自动创建 PV 并绑定 → MySQL(StatefulSet)、PHP(Deployment)、Nginx(Deployment)分别挂载存储;外部请求经 Ingress(
blog.liujn.cloud)→ Nginx Service → Nginx Pod,并由 Nginx 反向代理 PHP 解析 PHP 文件,PHP 再连接 MySQL 读写数据,形成完整 LNMP 请求链路。
步骤 1:创建命名空间
原始命令:
bash
root@master30:~/wordpress# kubectl create ns wordpress
root@master30:~/wordpress# kubectl config set-context --current --namespace wordpress
命令详解:
-
kubectl create ns wordpress- 用途 :创建一个名为
wordpress的 Kubernetes 命名空间(Namespace),用于隔离本次部署的所有资源。 - 参数含义 :
ns是namespace的缩写;wordpress是要创建的命名空间名称。 - 预期作用:创建成功后,后续的 MySQL、PHP、Nginx、PVC、Ingress 等对象都会集中放到这个命名空间里,互不干扰、便于统一管理。
- 用途 :创建一个名为
-
kubectl config set-context --current --namespace wordpress- 用途 :把当前使用的 K8s 上下文(context)默认命名空间修改为
wordpress,等价于给所有后续kubectl命令"烙上"默认命名空间。 - 参数含义 :
--current表示只修改当前正在使用的上下文;--namespace wordpress指定新的默认命名空间。 - 预期作用 :以后执行
kubectl get pods等命令时,无需再手动加-n wordpress,默认就会查这个命名空间内的资源,大幅减少命令输入量。
- 用途 :把当前使用的 K8s 上下文(context)默认命名空间修改为
步骤 2:部署 NFS 服务器
2.1 部署 NFS 服务(NFS 服务器节点操作)
原始命令:
bash
## 安装 NFS 服务
root@master30:~/wordpress# apt install -y nfs-kernel-server
## 创建共享目录
root@master30:~/wordpress# mkdir -p -m 777 /wordpress
## 配置 NFS 共享(地址已改为10.1.8.30,允许所有节点访问)
root@master30:~/wordpress# echo "/wordpress *(rw,sync,no_root_squash,no_all_squash)" >> /etc/exports
## 启动服务
root@master30:~/wordpress# systemctl restart nfs-server
## 验证共享
root@master30:~/wordpress# showmount -e localhost
Export list for localhost:
/wordpress *
命令详解:
-
apt install -y nfs-kernel-server- 用途:在 NFS 服务器节点上安装内核级 NFS 服务端软件。
- 参数含义 :
-y表示自动应答"yes",跳过安装过程中的确认提示,实现无人值守安装。 - 预期作用:安装完成后系统就具备了对外提供 NFS 网络共享的能力。
-
mkdir -p -m 777 /wordpress- 用途 :创建
/wordpress目录,作为 NFS 对外共享的根目录。 - 参数含义 :
-p表示递归创建父目录(父目录不存在时自动创建,已存在也不报错);-m 777设置目录权限为rwxrwxrwx,即所有用户均可读、写、执行。 - 预期作用:后续 MySQL 数据、WordPress 代码都会落在这个目录里,宽权限保证 K8s 中不同用户身份的 Pod 都能读写。
- 用途 :创建
-
echo "/wordpress *(rw,sync,no_root_squash,no_all_squash)" >> /etc/exports- 用途 :把共享目录的导出规则追加写入 NFS 配置文件
/etc/exports。 - 参数含义 :
/wordpress是要共享的目录;*表示允许所有主机访问;rw表示读写权限;sync表示同步写入(数据先落盘再返回,保证一致性);no_root_squash表示客户端的 root 用户保持 root 权限(不建议生产使用,此处为实验简化);no_all_squash表示不把所有用户都映射为匿名用户(nobody);>>是追加重定向,不会覆盖文件原有内容。 - 预期作用 :该规则写入后,NFS 服务重启即生效,集群任意节点都能把
/wordpress挂载为自己的存储目录。
- 用途 :把共享目录的导出规则追加写入 NFS 配置文件
-
systemctl restart nfs-server- 用途 :重启 NFS 服务,使
/etc/exports中新增的导出规则生效。 - 参数含义 :
systemctl用于管理系统服务,restart表示先停止再启动nfs-server服务。 - 预期作用 :重启后 NFS 服务开始对外公布
/wordpress共享。
- 用途 :重启 NFS 服务,使
-
showmount -e localhost- 用途:在本机查看 NFS 服务端当前导出的共享列表,验证共享是否配置成功。
- 参数含义 :
showmount是查询 NFS 导出信息的工具;-e表示显示导出列表(exports);localhost指定要查询的目标主机(此处为 NFS 服务器本机)。 - 预期作用 :输出显示
/wordpress *,表示共享目录已正确导出并允许所有主机访问。
2.2 部署 NFS 客户端(所有 K8s 节点操作)
原始命令:
bash
root@worker31-32:~# apt install -y nfs-common
## 验证共享
root@worker31-32:~# showmount -e master30
Export list for master30:
/wordpress *
命令详解:
-
apt install -y nfs-common- 用途:在所有 K8s 节点(Worker 节点)上安装 NFS 客户端工具集。
- 参数含义 :
-y自动确认安装;nfs-common是 NFS 客户端软件,包含挂载、查询 NFS 共享所需的基础工具。 - 预期作用:安装后各节点即可挂载 NFS 服务器导出的目录,这是后续 PV 挂载到 Pod 的前提。
-
showmount -e master30- 用途 :从客户端节点查询 NFS 服务器(主机名
master30)上导出的共享列表。 - 参数含义 :
-e显示导出列表;master30是 NFS 服务器的 IP/hostname。 - 预期作用 :输出
/wordpress *,确认客户端能够正常访问 NFS 服务器,网络与 NFS 服务均已就绪。
- 用途 :从客户端节点查询 NFS 服务器(主机名
步骤 3:部署 NFS Provisioner
NFS 原生没有内置的动态存储制备器(Provisioner),因此这里自定义一个外部分配器(External Provisioner)来监听 PVC 请求并自动创建 NFS 上的目录作为 PV。
原始命令:
bash
# 部署 NFS provisioner到命名空间:kube-storage
root@master30:~/wordpress# kubectl create ns kube-storage
命令详解:
kubectl create ns kube-storage- 用途 :创建一个名为
kube-storage的命名空间,专门用来存放 NFS Provisioner 及其相关资源,与业务命名空间wordpress隔离。 - 参数含义 :
ns是namespace缩写;kube-storage为命名空间名称。 - 预期作用:Provisional 相关的 ServiceAccount、RBAC、Deployment、StorageClass 都集中在此命名空间内管理。
- 用途 :创建一个名为
3.1 创建 RBAC 权限
原始清单(nfs-rbac.yaml):
yaml
root@master30:~/wordpress# cat > nfs-rbac.yaml <<'EOF'
apiVersion: v1 # API 版本:ServiceAccount 属于核心 v1 组
kind: ServiceAccount # 对象类型:ServiceAccount,为 Pod 提供集群内身份
metadata:
name: nfs-client-provisioner # 服务账号名称
namespace: kube-storage # 归属命名空间
---
apiVersion: rbac.authorization.k8s.io/v1 # RBAC 授权 API 版本
kind: ClusterRole # 对象类型:集群级角色,定义一整套权限规则
metadata:
name: nfs-client-provisioner-runner # 角色名
rules:
- apiGroups: [""] # 规则针对的 API 组:[""] 表示核心组(v1)
resources: ["persistentvolumes"] # 授权操作的资源类型:PV(持久卷)
verbs: ["get", "list", "watch", "create", "delete"] # 允许的动作:制备器需要创建/删除 PV
- apiGroups: [""] # 核心组
resources: ["persistentvolumeclaims"] # 资源类型:PVC
verbs: ["get", "list", "watch", "update"] # 需监听 PVC 并更新其状态
- apiGroups: ["storage.k8s.io"] # 存储 API 组
resources: ["storageclasses"] # 资源类型:StorageClass
verbs: ["get", "list", "watch"] # 需读取 SC 配置信息
- apiGroups: [""] # 核心组
resources: ["events"] # 资源类型:集群事件
verbs: ["create", "update", "patch"] # 用于将事件写入集群
- apiGroups: [""] # 核心组
resources: ["endpoints"] # 资源类型:端点
verbs: ["get", "list", "watch", "create", "update", "patch"] # 维护 PV 关联的端点信息
---
apiVersion: rbac.authorization.k8s.io/v1 # RBAC 授权 API 版本
kind: ClusterRoleBinding # 对象类型:集群级角色绑定,把角色授予指定对象(跨命名空间生效)
metadata:
name: run-nfs-client-provisioner # 绑定名称
subjects:
- kind: ServiceAccount # 被授者身份类型
name: nfs-client-provisioner # 被授者名称,即上面创建的 ServiceAccount
namespace: kube-storage # 被授者所在命名空间
roleRef:
kind: ClusterRole # 绑定的角色类型
name: nfs-client-provisioner-runner # 绑定的角色名,引用上面定义的 ClusterRole
apiGroup: rbac.authorization.k8s.io # 角色所属 API 组
EOF
root@master30:~/wordpress# kubectl apply -f nfs-rbac.yaml
命令详解:
-
cat > nfs-rbac.yaml <<'EOF' ... EOF- 用途 :在终端中把
EOF之间的多行内容写入文件nfs-rbac.yaml(>是覆盖写)。 - 参数含义 :
<<'EOF'是 here-document 语法,'EOF'加引号表示内容里不做变量展开(保证$等特殊字符原样写入);cat接力把标准输入写入文件。 - 预期作用:生成包含 ServiceAccount、ClusterRole、ClusterRoleBinding 三个对象的 YAML 文件。
- 用途 :在终端中把
-
kubectl apply -f nfs-rbac.yaml- 用途:声明式创建/更新该文件定义的所有资源对象。
- 参数含义 :
apply会根据清单文件把资源"应用"到集群,重复执行会比对差异做增量更新;-f指定清单文件路径。 - 预期作用:创建 NFS Provisioner 的运行身份与权限。
3.2 部署 NFS Provisioner 应用
原始清单(nfs-provisioner.yaml):
yaml
root@master30:~/wordpress# cat > nfs-provisioner.yaml <<'EOF'
apiVersion: apps/v1 # API 版本:Deployment 属于 apps 组的 v1(稳定版)
kind: Deployment # 对象类型:Deployment,无状态工作负载控制器,负责维持 Pod 副本数
metadata:
name: nfs-client-provisioner # Deployment 名称
namespace: kube-storage # 归属命名空间
spec: # 期望状态定义
replicas: 1 # 期望副本数 1 个(Provisioner 只需一个实例)
strategy: # 更新策略
type: Recreate # 先删旧再建新,适合有状态、不可并发的组件,避免新旧实例同时操作 NFS
selector: # 选择器:Deployment 通过它管理自己的 Pod
matchLabels:
app: nfs-client-provisioner # 匹配 Pod 标签
template: # Pod 模板
metadata:
labels:
app: nfs-client-provisioner # Pod 标签,需与 selector 一致
spec:
serviceAccountName: nfs-client-provisioner # 以该 ServiceAccount 身份运行(即 3.1 授权的账号)
volumes: # 声明卷
- name: nfs-client-root # 卷名:nfs-client-root
nfs: # 卷类型为 NFS
server: 10.1.8.30 # 同上 NFS 服务器 IP
path: /wordpress # 核心:共享路径同步改为 /wordpress
containers: # 容器定义
- name: nfs-client-provisioner # 容器名
image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 # 官方子目录外置制备器镜像
volumeMounts: # 卷挂载
- name: nfs-client-root # 挂载上面声明的 NFS 卷
mountPath: /persistentvolumes # 挂载到容器内路径,制备器在此路径下创建子目录充当 PV
env: # 环境变量
- name: PROVISIONER_NAME # 制备器唯一标识名
value: fuseim.pri/ifs # 必须和 StorageClass 中的 provisioner 完全一致
- name: NFS_SERVER # 传给制备器的 NFS 服务器地址
value: 10.1.8.30 # 替换为你的 NFS 服务器 IP
- name: NFS_PATH # 传给制备器的共享根路径
value: /wordpress # 核心:共享路径改为 /wordpress
EOF
# 部署 NFS Provisioner 应用
root@master30:~/wordpress# kubectl apply -f nfs-provisioner.yaml
# 查看 NFS Provisioner 应用
root@master30:~/wordpress# kubectl get deployments.apps -n kube-storage
NAME READY UP-TO-DATE AVAILABLE AGE
nfs-client-provisioner 1/1 1 1 1m
命令详解:
cat > nfs-provisioner.yaml <<'EOF' ... EOF------ 同上,用 here-document 生成 Deployment 清单文件。kubectl apply -f nfs-provisioner.yaml------ 声明式创建 NFS Provisioner 的 Deployment。kubectl get deployments.apps -n kube-storage- 用途 :查看
kube-storage命名空间下的 Deployment 状态。 - 参数含义 :
deployments.apps指定资源类型;-n指定命名空间。 - 预期作用 :输出
nfs-client-provisioner 1/1 ...表示副本数要求 1 个、就绪 1 个,部署成功。
- 用途 :查看
3.3 创建 NFS StorageClass
原始清单(wordpress-StorageClass.yaml):
yaml
root@master30:~/wordpress# cat > wordpress-StorageClass.yaml <<'EOF'
apiVersion: storage.k8s.io/v1 # API 版本:StorageClass 属于 storage.k8s.io 组的 v1
kind: StorageClass # 对象类型:定义一类相同特性的动态存储
metadata:
name: wordpress # SC 名称,PVC 通过 storageClassName 引用它
namespace: kube-storage # 归属命名空间(SC 实际是集群级资源,此处标归属便于管理)
provisioner: fuseim.pri/ifs # 指定由哪个动态制备器创建 PV,必须和 Provisioner 名称(PROVISIONER_NAME)一致
parameters:
onDelete: retain # 制备器级参数:删除 PVC 时保留 NFS 上的数据(不删除底层目录)
archiveOnDelete: "false" # 制备器级参数:删除 PVC 时不归档(归档会重命名为 archived- 前缀,这里关闭)
# 按需设置回收策略
#reclaimPolicy: Retain # 如需保留数据可改用 Retain,此处默认注释掉
reclaimPolicy: Delete # 回收策略:删除 PVC 后同时删除对应的 PV
allowVolumeExpansion: true # 允许后续在线扩容 PV 容量
volumeBindingMode: Immediate # 卷绑定模式:PVC 创建后立即绑定 PV(延迟绑定对应 WaitForFirstConsumer)
EOF
# 创建 StorageClass
root@master30:~/wordpress# kubectl apply -f wordpress-StorageClass.yaml
root@master30:~/wordpress# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
wordpress fuseim.pri/ifs Delete Immediate true 11s
命令详解:
cat > wordpress-StorageClass.yaml <<'EOF' ... EOF------ here-document 生成 StorageClass 清单。kubectl apply -f wordpress-StorageClass.yaml------ 创建名为wordpress的 StorageClass。kubectl get sc- 用途 :查看集群中所有 StorageClass(
sc是storageclass缩写)。 - 参数含义:无附加参数时展示全部 SC 的摘要表。
- 预期作用 :输出显示
wordpress的 PROVISIONER 为fuseim.pri/ifs、回收策略 Delete、绑定模式 Immediate、支持扩容 true,配置已生效。
- 用途 :查看集群中所有 StorageClass(
步骤 4:部署 MySQL
4.1 创建 Secret
用 Kubernetes Secret 存储 MySQL root 密码、WordPress 专用用户及密码、数据库名,替代明文配置,提升安全性。
这里定义:
- MySQL root 密码:
Root@123(可按需修改) - WordPress 专用数据库用户:
wordpress - WordPress 专用用户密码:
Wordpress@123(可按需修改) - WordPress 专用数据库:
wordpress
原始命令:
bash
# 创建 Secret 存储数据库敏感信息(base64编码,避免明文)
root@master30:~/wordpress# kubectl create secret generic mysql \
--namespace wordpress \
--from-literal=mysql-root-password=Root@123 \
--from-literal=wordpress-db-user=wordpress \
--from-literal=wordpress-db-password=Wordpress@123
# 验证 Secret
root@master30:~/wordpress# kubectl get secrets
NAME TYPE DATA AGE
mysql Opaque 3 10s
命令详解:
-
kubectl create secret generic mysql- 用途 :创建一个名为
mysql的通用类型(generic/Opaque)Secret,存放键值对形式的敏感数据。 - 参数含义 :
secret资源类型;generic表示通用 Secret(非 TLS、非 Registry 凭证);mysql为 Secret 名称。 --namespace wordpress:把 Secret 创建在wordpress命名空间。--from-literal=键=值:直接在命令行指定一个键值条目(literal 字面量),K8s 内部以 base64 编码存储。- 预期作用 :创建后 Secret 中包含 3 个键:
mysql-root-password、wordpress-db-user、wordpress-db-password。\为行继续符,把一条长命令拆成多行书写。
- 用途 :创建一个名为
-
kubectl get secrets- 用途:列出当前默认命名空间(wordpress)下的 Secret。
- 参数含义 :无参数,展示所有 Secret 摘要;
secrets与secret等价,get支持复数。 - 预期作用 :输出
mysql / Opaque / 3,表示 Secret 类型为 Opaque、含 3 条数据,创建成功。
4.2 创建 Service
创建两个 Service:
mysql-headless(无头服务,为 StatefulSet 管理的 Pod 提供固定网络标识)、mysql(普通 ClusterIP 服务,为 Pod 提供负载均衡入口)。
原始清单(mysql-service.yaml):
yaml
root@master30:~/wordpress# cat > mysql-service.yaml <<'EOF'
apiVersion: v1 # API 版本:Service 属于核心 v1 组
kind: Service # 对象类型:服务,为 Pod 提供稳定访问入口
metadata:
name: mysql-headless # 服务名
namespace: wordpress # 命名空间
spec:
type: ClusterIP # 服务类型:仅集群内部可达的虚拟 IP
selector:
app: mysql ## 匹配 StatefulSet 管理的 Pod 标签
clusterIP: None ## 无头服务,ClusterIP 设为 None(不分配集群 IP,DNS 按 服务名.命名空间.svc.cluster.local 解析出所有后端 Pod IP,供 StatefulSet 稳定网络标识使用)
ports:
- port: 3306 # 服务对外端口(集群内访问的端口)
targetPort: 3306 # 转发到 Pod 容器的目标端口
name: mysql-port ## 端口名称,便于识别
---
apiVersion: v1 # API 版本
kind: Service # 对象类型:服务
metadata:
name: mysql # 服务名
namespace: wordpress # 命名空间
spec:
type: ClusterIP # 服务类型:集群内部访问
selector:
app: mysql ## 匹配 StatefulSet 管理的 Pod 标签
ports:
- port: 3306 # 服务对外端口
targetPort: 3306 # 转发到容器内端口
name: mysql-port ## 端口名称,便于识别
EOF
# 创建服务
root@master30:~/wordpress# kubectl apply -f mysql-service.yaml
# 没有 CLUSTER-IP
root@master30:~/wordpress# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.110.201.224 <none> 3306/TCP 4s
mysql-headless ClusterIP None <none> 3306/TCP 4s
命令详解:
cat > mysql-service.yaml <<'EOF' ... EOF------ here-document 生成含两个 Service 的清单(---分隔多个对象)。kubectl apply -f mysql-service.yaml------ 一次性创建mysql-headless和mysql两个 Service。kubectl get svc- 用途 :查看当前命名空间所有 Service(
svc为缩写)。 - 参数含义:无参数默认查看当前 context 命名空间。
- 预期作用 :
mysql有 ClusterIP10.110.201.224,mysql-headless的 CLUSTER-IP 为None(无头,无固定集群 IP,DNS 直接返回 Pod IP 列表)。
- 用途 :查看当前命名空间所有 Service(
为什么 MySQL 需要无头服务 :StatefulSet 要求提供
serviceName,无头服务为每个 Pod 提供稳定的 DNS 名称(如mysql-0.mysql-headless.wordpress.svc.cluster.local),保证有状态应用的有序、可识别访问。
4.3 部署 MySQL(StatefulSet)
原始清单(mysql-statefulset.yaml):
yaml
root@master30:~/wordpress# vim mysql-statefulset.yaml
apiVersion: apps/v1 # API 版本:StatefulSet 属于 apps 组的 v1
kind: StatefulSet # 对象类型:有状态工作负载控制器,保证 Pod 有序、稳定,配合 PVC 模板持久化数据
metadata:
name: mysql # 名称
namespace: wordpress # 命名空间
spec:
serviceName: mysql-headless # 必须指向关联的无头服务,用于给每个 Pod 分配稳定网络标识
replicas: 1 # 副本数 1(MySQL 单实例,避免多副本数据冲突)
selector:
matchLabels:
app: mysql # 选择器:管理带 app=mysql 标签的 Pod
template:
metadata:
labels:
app: mysql # Pod 标签,与 selector 及 Service selector 一致
spec:
containers:
- name: mysql # 容器名
image: mysql:5.7 # 镜像
ports:
- containerPort: 3306 # 容器监听端口(MySQL 默认端口)
name: mysql # 端口名称,便于标识
env: # 环境变量(镜像约定)
- name: MYSQL_ROOT_PASSWORD # 镜像约定环境变量:MySQL root 密码
valueFrom: # 值来源:从 Secret 中取
secretKeyRef:
name: mysql # 引用的 Secret 名
key: mysql-root-password # 取的键:将 Secret 中的该键注入为 root 密码
- name: MYSQL_USER # 镜像约定环境变量:创建的应用专用用户
valueFrom:
secretKeyRef:
name: mysql # Secret 名
key: wordpress-db-user # 取的键:wordpress 用户名
- name: MYSQL_PASSWORD # 镜像约定环境变量:该用户密码
valueFrom:
secretKeyRef:
name: mysql # Secret 名
key: wordpress-db-password # 取的键:wordpress 用户密码
- name: MYSQL_DATABASE # 镜像约定环境变量:自动创建的数据库名
value: "wordpress" # 直接写死库名 wordpress
volumeMounts:
- name: mysql-data ## 挂载存储卷,名称与 volumeClaimTemplates 一致
mountPath: /var/lib/mysql ## MySQL 数据存储路径,数据落盘到该路径实现持久化
volumeClaimTemplates: ## PVC 模板,StatefulSet 专属,为每个 Pod 自动生成一个独立 PVC
- metadata:
name: mysql-data # PVC 名称前缀,最终 PVC 名为 mysql-data-mysql-0
spec:
accessModes: [ "ReadWriteOnce" ] # 访问模式 RWO:单节点读写(MySQL 单副本适用)
storageClassName: "wordpress" ## 指定引用哪个 StorageClass,触发 NFS 动态制备
resources:
requests:
storage: 10Gi ## 申请容量,需与底层 PV 容量匹配
命令详解:
vim mysql-statefulset.yaml------ 使用 vim 编辑器手动创建/编辑 StatefulSet 清单文件(编辑后需kubectl apply生效)。kubectl apply -f mysql-statefulset.yaml------ 创建 StatefulSetmysql。kubectl get statefulsets.apps------ 查看 StatefulSet 状态,输出mysql 1/1 ...表示期望 1 个副本且就绪 1 个(apps后缀显式指定 API 组)。kubectl get pods------ 查看 Pod,输出mysql-0 Running,StatefulSet 创建的 Pod 名带有序号后缀(mysql-0)。kubectl get pvc- 用途:查看 PVC 绑定状态。
- 参数含义:无参数查看当前命名空间所有 PVC。
- 预期作用 :输出
mysql-data-mysql-0 Bound ... 10Gi RWO wordpress,表示由volumeClaimTemplates自动生成的 PVC(名称 = 模板名 + Pod 名)已由 NFS 动态制备成功绑定,容量 10Gi,访问模式 RWO,存储类wordpress。
验证数据库:
bash
root@master30:~/wordpress# apt install -y mysql-client
root@master30:~/wordpress# mysql -uwordpress -pWordpress@123 -h 10.110.201.224 -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| information_schema |
| wordpress |
+--------------------+
命令详解:
apt install -y mysql-client------ 在 master 节点安装 MySQL 客户端,用于远程连接验证数据库。mysql -uwordpress -pWordpress@123 -h 10.110.201.224 -e 'show databases;'- 用途 :使用
wordpress用户连接集群内 MySQL Service(ClusterIP10.110.201.224)并列出数据库。 - 参数含义 :
-uwordpress指定用户名;-pWordpress@123指定密码(-p后直接拼接,注意命令行明文密码会有安全告警);-h指定主机;-e在非交互模式执行单条 SQL。 - 预期作用 :输出显示
information_schema与wordpress两个库,说明 MySQL 正常启动、专用用户与数据库均创建成功。
- 用途 :使用
步骤 5:部署 PHP
5.1 创建 PVC
PHP 和 Nginx 使用同一份共享存储(存放 WordPress 代码与上传文件),这里手动创建一个名为
wordpress的 PVC,采用 RWX(多节点读写)访问模式。
原始清单(wordpress-pvc.yaml):
yaml
root@master30:~/wordpress# vim wordpress-pvc.yaml
apiVersion: v1 # API 版本:PVC 属于核心 v1 组
kind: PersistentVolumeClaim # 对象类型:存储请求对象(声明式),申请一块 PV 供 Pod 使用
metadata:
name: wordpress # PVC 名称,Deployment 通过 claimName 引用
namespace: wordpress # 命名空间(引用它的 Pod 须在同一命名空间)
spec:
accessModes:
- ReadWriteMany # 访问模式 RWX:允许多个节点/多个 Pod 同时读写,PHP 与 Nginx 跨副本共享同一存储的必要条件
resources:
requests:
storage: 10Gi # 申请容量 10Gi
storageClassName: wordpress # 指定使用 wordpress StorageClass,触发 NFS 动态制备自动创建 PV
命令详解:
vim wordpress-pvc.yaml------ 用 vim 编辑 PVC 清单文件。kubectl apply -f wordpress-pvc.yaml------ 创建 PVCwordpress。kubectl get pvc wordpress- 用途 :单独查看名为
wordpress的 PVC 状态。 - 参数含义 :
pvc类型 + PVC 名称。 - 预期作用 :输出
wordpress Bound pvc-... 10Gi RWX wordpress,STATUS 为 Bound 表示已成功绑定动态创建的 PV。
- 用途 :单独查看名为
5.2 创建 Service(PHP)
原始清单(php-service.yaml):
yaml
root@master30:~/wordpress# vim php-service.yaml
---
apiVersion: v1 # API 版本:Service 属于核心 v1 组
kind: Service # 对象类型:服务
metadata:
name: php # 服务名,Nginx 通过 php.wordpress.svc.cluster.local 访问
namespace: wordpress # 命名空间
spec:
type: ClusterIP # 服务类型:集群内访问的虚拟 IP 服务
selector:
app: php # 选择带 app=php 标签的 PHP Pod 作为后端
ports:
- port: 9000 # 服务端口 9000(PHP-FPM 默认监听端口)
targetPort: 9000 # 转发到容器内 9000 端口
bash
root@master30:~/wordpress# kubectl apply -f php-service.yaml
root@master30:~/wordpress# kubectl get svc php
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
php ClusterIP 10.104.30.180 <none> 9000/TCP 21s
命令详解:
vim php-service.yaml------ 编辑 PHP Service 清单。kubectl apply -f php-service.yaml------ 创建 Servicephp。kubectl get svc php------ 查看指定服务,输出php ClusterIP 10.104.30.180 ... 9000/TCP,服务就绪。
5.3 部署 PHP(Deployment)
提示 :镜像为
php:7.2-fpm-mysql(含 mysqli 扩展的定制镜像),需找老师获取或自行构建。
原始清单(php-deploy.yaml):
yaml
root@master30:~/wordpress# vim php-deploy.yaml
apiVersion: apps/v1 # API 版本:Deployment 属于 apps 组的 v1
kind: Deployment # 对象类型:Deployment 控制器,负责滚动更新与副本保障
metadata:
name: php # Deployment 名称
namespace: wordpress # 命名空间
spec:
replicas: 2 # 副本数 2,提供 PHP 高可用
selector:
matchLabels:
app: php # 选择器:管理 app=php 的 Pod
template:
metadata:
labels:
app: php # Pod 标签,与 Service/Deployment selector 一致
spec:
containers:
- name: php # 容器名
image: php:7.2-fpm-mysql # 镜像(含 mysqli 扩展的定制版)
ports:
- containerPort: 9000 # PHP-FPM 监听 9000,等待 Nginx fastcgi 转发
volumeMounts:
- name: wordpress-data # 挂载卷名
mountPath: /usr/share/nginx/html ## PHP 网站根目录(与 Nginx 的 root 目录一致,共享同一存储)
volumes:
- name: wordpress-data # 声明卷
persistentVolumeClaim:
claimName: wordpress # 引用上面创建的 PVC,实现 PHP 与 Nginx 共享代码目录
bash
root@master30:~/wordpress# kubectl apply -f php-deploy.yaml
## 验证
root@master30:~/wordpress# kubectl get deploy php
NAME READY UP-TO-DATE AVAILABLE AGE
php 2/2 2 0 49s
命令详解:
vim php-deploy.yaml------ 编辑 PHP Deployment 清单。kubectl apply -f php-deploy.yaml------ 创建 Deploymentphp。kubectl get deploy php- 用途:查看 PHP 部署状态。
- 参数含义 :
deploy是deployment缩写。 - 预期作用 :输出
php 2/2 2 0,表示期望/就绪副本均为 2(AVAILABLE 列的 0 为瞬时采样,以 READY 为准)。
步骤 6:部署 Nginx
6.1 创建 Nginx 配置(ConfigMap)
原始清单(nginx-configmap.yaml):
yaml
root@master30:~/wordpress# vim nginx-configmap.yaml
apiVersion: v1 # API 版本:ConfigMap 属于核心 v1 组
kind: ConfigMap # 对象类型:用于保存非敏感的文本配置
metadata:
name: nginx # ConfigMap 名称
namespace: wordpress # 指定wordpress命名空间
data:
nginx.conf: | # 键名 nginx.conf;| 为 YAML 块标量符号,其后缩进内容按原样多行文本保存
worker_processes auto; # 工作进程数设为自动(按 CPU 核数自动生成),提高并发处理能力
events {
worker_connections 1024; # 每个工作进程最多同时保持 1024 个连接
}
http {
include /etc/nginx/mime.types; # 引入 MIME 类型映射文件,让浏览器正确识别文件类型
default_type application/octet-stream; # 未匹配到 MIME 类型时返回默认二进制流类型
sendfile on; # 开启高效文件传输(zero-copy),提升静态文件响应速度
keepalive_timeout 65; # 长连接超时 65 秒,减少频繁建连开销
# Wordpress 站点配置
server {
listen 80; # 监听 HTTP 80 端口
listen 443 ssl; # 监听 HTTPS 443 端口并启用 SSL
server_name localhost; # 虚拟主机名(生产应替换为实际域名,如 blog.liujn.cloud)
# TLS 配置
ssl_certificate /etc/nginx/tls/tls.crt; # TLS 证书路径(由 Secret 挂载而来)
ssl_certificate_key /etc/nginx/tls/tls.key; # TLS 证书私钥路径
# 网站根目录
root /usr/share/nginx/html; # 网站根目录(与 PHP 共用同一存储中的 WordPress 代码)
index index.php index.html; # 默认首页优先找 index.php,其次 index.html
# PHP 转发配置(指向wordpress命名空间的PHP Service)
location ~ \.php$ { # 正则匹配以 .php 结尾的请求,交给 PHP-FPM 处理
fastcgi_pass php.wordpress.svc.cluster.local:9000; # 跨命名空间访问,把 PHP 请求转发到 PHP Service 9000 端口,格式:服务名.命名空间.svc.cluster.local
fastcgi_index index.php; # 指定 fastcgi 默认索引页
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 拼出 PHP 脚本真实物理路径传给 PHP-FPM($document_root=根目录,$fastcgi_script_name=请求的脚本名)
include fastcgi_params; # 引入 fastcgi 标准参数文件(含请求头、URI 等常见参数)
}
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|css|js)$ { # ~* 忽略大小写匹配图片/CSS/JS 等静态资源
expires 30d; # 静态资源客户端缓存 30 天
add_header Cache-Control "public, max-age=2592000"; # 额外添加缓存响应头,max-age 2592000 秒 = 30 天,允许公共缓存
}
}
}
bash
root@master30:~/wordpress# kubectl apply -f nginx-configmap.yaml
root@master30:~/wordpress# kubectl get cm nginx
NAME DATA AGE
nginx 1 27s
命令详解:
vim nginx-configmap.yaml------ 编辑 ConfigMap 清单。kubectl apply -f nginx-configmap.yaml------ 创建 ConfigMapnginx。kubectl get cm nginx- 用途 :查看 ConfigMap 是否创建成功(
cm为configmap缩写)。 - 参数含义 :
nginx为名称。 - 预期作用 :输出
nginx 1 27s,表示该 ConfigMap 含 1 条数据(即nginx.conf)。
- 用途 :查看 ConfigMap 是否创建成功(
6.2 创建 Nginx 证书(openssl 自签名证书)
原始命令:
bash
## 生成自签名证书(替换域名)
root@master30:~/wordpress# openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt \
-subj "/CN=wordpress.liujn.cloud/O=wordpress"
## 创建 Secret 存储证书
root@master30:~/wordpress# kubectl create secret tls nginx-tls \
--namespace wordpress \
--cert=tls.crt \
--key=tls.key
root@master30:~/wordpress# kubectl get secret nginx-tls
NAME TYPE DATA AGE
nginx-tls kubernetes.io/tls 2 2s
命令详解:
-
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=wordpress.liujn.cloud/O=wordpress"- 用途 :一次性生成自签名 SSL 证书(
tls.crt)及其私钥(tls.key)。 - 参数含义 :
req:openssl 的证书请求/生成子命令;-x509:直接输出自签名 X.509 证书(而非仅 CSR 请求);-nodes:no DES,不加密私钥文件,使 Nginx 启动时无需输入密码即可读取;-days 365:证书有效期 365 天;-newkey rsa:2048:同时生成新的 2048 位 RSA 密钥;-keyout tls.key:私钥输出文件;-out tls.crt:证书输出文件;-subj:免交互指定证书主体,/CN=wordpress.liujn.cloud为通用名(域名),/O=wordpress为组织名。
- 预期作用 :当前目录生成
tls.key与tls.crt两个文件,供 Nginx/Ingress 启用 HTTPS。
- 用途 :一次性生成自签名 SSL 证书(
-
kubectl create secret tls nginx-tls --namespace wordpress --cert=tls.crt --key=tls.key- 用途:创建 TLS 类型 Secret,把证书与私钥上传到集群供 Pod 使用。
- 参数含义 :
secret tls指定专门存储证书的 Secret 类型;nginx-tls为名称;--cert指定证书文件;--key指定私钥文件。 - 预期作用 :集群内生成
kubernetes.io/tls类型 Secret,含 2 条数据(tls.crt、tls.key)。
-
kubectl get secret nginx-tls- 用途:查看 TLS Secret。
- 参数含义 :
nginx-tls为 Secret 名称。 - 预期作用 :输出
nginx-tls kubernetes.io/tls 2,确认证书 Secret 创建成功。
关于自签名证书提示 :生产环境应使用 CA 签发的受信证书;自签名证书浏览器会提示不受信任,需手动信任或使用
-k(curl 忽略证书校验)。
6.3 创建 Service(Nginx)
原始清单(nginx-service.yaml):
yaml
root@master30:~/wordpress# vim nginx-service.yaml
---
apiVersion: v1 # API 版本:Service 属于核心 v1 组
kind: Service # 对象类型:服务
metadata:
name: nginx # 服务名
namespace: wordpress # 命名空间
spec:
type: ClusterIP # 服务类型:集群内部访问
selector:
app: nginx # 选择 app=nginx 的 Pod 作为后端
ports:
- port: 80 # 服务对外端口 80
targetPort: 80 # 转发到容器 80 端口
name: http # 端口命名 http,便于识别
- port: 443 # 服务对外端口 443
targetPort: 443 # 转发到容器 443 端口
name: https # 端口命名 https
bash
root@master30:~/wordpress# kubectl apply -f nginx-service.yaml
root@master30:~/wordpress# kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.100.41.121 <none> 80/TCP,443/TCP 4s
命令详解:
vim nginx-service.yaml------ 编辑 Nginx Service 清单。kubectl apply -f nginx-service.yaml------ 创建 Servicenginx。kubectl get svc nginx------ 查看服务,输出nginx ClusterIP 10.100.41.121 ... 80/TCP,443/TCP,同时暴露 80 与 443 端口。
6.4 部署 Nginx(Deployment)
原始清单(nginx-deploy.yaml):
yaml
root@master30:~/wordpress# vim nginx-deploy.yaml
apiVersion: apps/v1 # API 版本:Deployment 属于 apps 组的 v1
kind: Deployment # 对象类型:Deployment 控制器
metadata:
name: nginx # Deployment 名称
namespace: wordpress # 命名空间
spec:
replicas: 2 # 副本数 2,为 Nginx/入口提供高可用
selector:
matchLabels:
app: nginx # 选择器:管理 app=nginx 的 Pod
template:
metadata:
labels:
app: nginx # Pod 标签
spec:
containers:
- name: nginx # 容器名
image: nginx:1.24 # 镜像
ports:
- containerPort: 80 # 暴露容器 80(HTTP)端口
- containerPort: 443 # 暴露容器 443(HTTPS)端口
volumeMounts:
- name: nginx # 挂载 ConfigMap 卷
mountPath: /etc/nginx/nginx.conf ## 把配置作为单个文件挂载到主配置文件路径(整文件替换)
subPath: nginx.conf # 只挂载 ConfigMap 中的 nginx.conf 这一个 key,避免覆盖 /etc/nginx 目录下其他文件
- name: tls-cert # 挂载证书 Secret 卷
mountPath: /etc/nginx/tls ## 证书挂载目录(与 nginx.conf 中 ssl_certificate 路径对应)
readOnly: true # 证书目录只读,保障安全
- name: wordpress-data # 挂载共享存储卷
mountPath: /usr/share/nginx/html ## WordPress 代码根目录(与 PHP 共享同一 PVC)
volumes:
- name: nginx # 卷名
configMap:
name: nginx # 卷类型为 ConfigMap,引用名为 nginx 的 ConfigMap
- name: tls-cert # 卷名
secret:
secretName: nginx-tls # 卷类型为 Secret,引用 nginx-tls
- name: wordpress-data # 卷名
persistentVolumeClaim:
claimName: wordpress # 卷类型为 PVC,引用 wordpress 共享存储
bash
root@master30:~/wordpress# kubectl apply -f nginx-deploy.yaml
root@master30:~/wordpress# kubectl get deploy nginx
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 0 53s
命令详解:
vim nginx-deploy.yaml------ 编辑 Nginx Deployment 清单。kubectl apply -f nginx-deploy.yaml------ 创建 Deploymentnginx。kubectl get deploy nginx------ 查看 Nginx 部署,输出nginx 2/2表示 2 个副本全部就绪。
测试 Nginx:
bash
# 查看物理位置名称
root@master30:~/wordpress# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-0 Bound pvc-c40fcf08-0f68-481f-b266-29b03eca925b 10Gi RWO wordpress <unset> 15m
wordpress Bound pvc-c62430f7-2518-4ba3-99c7-2466853a1526 10Gi RWX wordpress <unset> 10m
# 存储服务器准备测试页面
root@master30:~/wordpress# echo Hello World From Nginx > /wordpress/wordpress-wordpress-pvc-c62430f7-2518-4ba3-99c7-2466853a1526/index.html
root@master30:~/wordpress# echo "<?php phpinfo(); ?>" > /wordpress/wordpress-wordpress-pvc-c62430f7-2518-4ba3-99c7-2466853a1526/phpinfo.php
root@master30:~/wordpress# kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.100.41.121 <none> 80/TCP,443/TCP 3m40s
# http 站点
root@master30:~/wordpress# curl http://10.100.41.121/index.html
Hello World From Nginx
root@master30:~/wordpress# curl https://10.100.41.121/index.html -k
Hello World From Nginx
# https 站点
root@master30:~/wordpress# curl http://10.100.41.121/phpinfo.php -s|grep -o 'PHP Version 7.2'
PHP Version 7.2
root@master30:~/wordpress# curl https://10.100.41.121/phpinfo.php -sk|grep -o 'PHP Version 7.2'
PHP Version 7.2
命令详解:
-
echo Hello World From Nginx > /wordpress/wordpress-wordpress-pvc-c62430f7-2518-4ba3-99c7-2466853a1526/index.html- 用途 :在 NFS 共享目录里写入测试首页
index.html(该目录名即 PVCwordpress在 NFS 上对应的物理子目录,格式为命名空间-PVC名-PVC UID)。 - 参数含义 :
echo输出字符串;>重定向覆盖写入文件。 - 预期作用:文件落到共享存储,PHP/Nginx Pod 都能看到,用于验证存储链路。
- 用途 :在 NFS 共享目录里写入测试首页
-
echo "<?php phpinfo(); ?>" > .../phpinfo.php- 用途:写入一个测试 PHP 文件,输出 PHP 版本信息,用于验证 PHP-FPM 转发链路。
- 参数含义 :
<?php phpinfo(); ?>是 PHP 内建函数,输出当前 PHP 配置与版本信息。 - 预期作用:可通过 curl 验证 Nginx→PHP-FPM 链路是否打通。
-
curl http://10.100.41.121/index.html- 用途 :通过 Nginx Service 的 ClusterIP(HTTP)请求
index.html。 - 参数含义 :
curl发起 HTTP 请求;10.100.41.121为 Service ClusterIP。 - 预期作用 :返回
Hello World From Nginx,说明 HTTP 请求链路与共享存储挂载正常。
- 用途 :通过 Nginx Service 的 ClusterIP(HTTP)请求
-
curl https://10.100.41.121/index.html -k- 用途:通过 HTTPS 请求同上内容。
- 参数含义 :
-k忽略自签名证书校验(否则 curl 会因证书不受信而报错)。 - 预期作用 :同样返回
Hello World From Nginx,说明 443/TLS 链路正常。
-
curl http://10.100.41.121/phpinfo.php -s | grep -o 'PHP Version 7.2'- 用途:请求 PHP 测试页并从输出中提取 PHP 版本信息。
- 参数含义 :
-s静默模式(不显示进度条/错误);grep -o只输出匹配到的部分字符串;'PHP Version 7.2'为匹配内容。 - 预期作用 :返回
PHP Version 7.2,证明 HTTP 下 Nginx→PHP-FPM 转发成功。
-
curl https://10.100.41.121/phpinfo.php -sk | grep -o 'PHP Version 7.2'- 用途:HTTPS 场景同 5 验证。
- 参数含义 :
-s静默;-k忽略证书校验。 - 预期作用 :返回
PHP Version 7.2,HTTPS 下 PHP 解析亦正常。
6.5 部署 WordPress 代码
原始命令:
bash
## 进入 NFS 服务器(IP:10.1.8.30),下载 Wordpress 源码
root@master30:~/wordpress# unzip wordpress-4.9.4-zh_CN.zip
root@master30:~/wordpress# cp -a wordpress/* /wordpress/wordpress-wordpress-pvc-c62430f7-2518-4ba3-99c7-2466853a1526/
## 修改配置文件(连接 MySQL)
root@master30:~/wordpress# cp /wordpress/wordpress-wordpress-pvc-c62430f7-2518-4ba3-99c7-2466853a1526/wp-config{-sample,}.php
## 配置数据库信息
root@master30:~/wordpress# vim /wordpress/wordpress-wordpress-pvc-c62430f7-2518-4ba3-99c7-2466853a1526/wp-config.php
/** WordPress数据库的名称 */
define('DB_NAME', 'wordpress');
/** MySQL数据库用户名 */
define('DB_USER', 'wordpress');
/** MySQL数据库密码 */
define('DB_PASSWORD', 'Wordpress@123');
/** MySQL主机 */
define('DB_HOST', 'mysql');
命令详解:
-
unzip wordpress-4.9.4-zh_CN.zip- 用途:解压 WordPress 中文版源码压缩包。
- 参数含义 :
unzip为解压命令;wordpress-4.9.4-zh_CN.zip为已下载到当前目录的 WordPress 4.9.4 中文语言包压缩文件。 - 预期作用 :解压出
wordpress目录,内含完整博客程序源码。
-
cp -a wordpress/* /wordpress/wordpress-wordpress-pvc-.../- 用途:把解压出的 WordPress 全部源码复制到 NFS 上的 PVC 物理目录(PHP/Nginx 共享挂载)。
- 参数含义 :
cp -a递归复制并保留文件权限、时间戳等属性;*通配所有文件与子目录;目标路径即 6.4 中echo写入测试文件所在的物理目录。 - 预期作用:PHP/Nginx Pod 通过挂载即可直接读取 WordPress 代码。
-
cp /wordpress/.../wp-config{-sample,}.php- 用途 :把示例配置文件
wp-config-sample.php复制为wp-config.php(大括号展开{a,b}等价于两个路径参数:wp-config-sample.php wp-config.php)。 - 参数含义 :
cp复制;源文件为wp-config-sample.php,目标为wp-config.php。 - 预期作用:生成 WordPress 实际使用的数据库配置文件,供下一步编辑。
- 用途 :把示例配置文件
-
vim .../wp-config.php------ 编辑 WordPress 数据库配置,把DB_NAME/DB_USER/DB_PASSWORD/DB_HOST改为实际值:define('DB_NAME', 'wordpress');------ 数据库名,对应 MySQL 里创建的wordpress库(4.3 中的MYSQL_DATABASE)。define('DB_USER', 'wordpress');------ 数据库用户名,对应 4.1 Secret 中的wordpress-db-user。define('DB_PASSWORD', 'Wordpress@123');------ 数据库密码,对应 Secret 中的wordpress-db-password。define('DB_HOST', 'mysql');------ 数据库主机,写 Service 名mysql(同命名空间内 K8s DNS 自动解析到 MySQL Service 的 ClusterIP)。
步骤 7:部署 LoadBalancer(MetalLB)
裸金属/自建 K8s 集群没有云厂商的负载均衡器,使用 MetalLB 提供"LoadBalancer 类型 Service"的外部 IP 分配能力,为后续 Ingress Controller 提供外部入口地址。
原始命令:
bash
root@master30:~/wordpress# tar -xf metallb-0.14.8.tar.gz
root@master30:~/wordpress# kubectl apply -f metallb-0.14.8/config/manifests/metallb-native.yaml
# 等待着所有pod正常运行再进行下一步
root@master30:~/wordpress# kubectl get all -n metallb-system
NAME READY STATUS RESTARTS AGE
pod/controller-786f9df989-98bjh 1/1 Running 0 85s
pod/speaker-gthhx 1/1 Running 0 85s
pod/speaker-jwj25 1/1 Running 0 85s
pod/speaker-s5zvq 1/1 Running 0 85s
# 配置地址池
root@master30:~/wordpress# cat << 'EOF' > ippool.yaml
apiVersion: metallb.io/v1beta1 # MetalLB 自定义资源 API 版本
kind: IPAddressPool # 资源类型:IP 地址池,MetalLB 从这里取 IP 分配给 LoadBalancer Service
metadata:
name: first-pool # 地址池名
namespace: metallb-system # 命名空间
spec:
addresses:
- 10.1.8.40-10.1.8.80 # 可分配的 IP 段,从 10.1.8.40 到 10.1.8.80(与集群节点同网段,确保路由可达)
EOF
root@master30:~/wordpress# kubectl apply -f ippool.yaml
# 配置 lay2
root@master30:~/wordpress# cat << 'EOF' > L2.yaml
apiVersion: metallb.io/v1beta1 # MetalLB 自定义资源 API 版本
kind: L2Advertisement # 资源类型:二层通告,MetalLB 通过 ARP 在二层广播来宣告 VIP
metadata:
name: example # 通告名
namespace: metallb-system # 命名空间
EOF
root@master30:~/wordpress# kubectl apply -f L2.yaml
命令详解:
-
tar -xf metallb-0.14.8.tar.gz- 用途:解压 MetalLB 0.14.8 版本源码/资源包。
- 参数含义 :
tar归档工具;-xf表示解压(-xextract,-f指定归档文件)。 - 预期作用 :解压出包含
config/manifests/metallb-native.yaml等文件的目录。
-
kubectl apply -f metallb-0.14.8/config/manifests/metallb-native.yaml- 用途:应用 MetalLB 官方原生清单(CRD、Controller、Speaker 等)。
- 参数含义 :
apply -f声明式应用清单文件。 - 预期作用 :在
metallb-system命名空间部署 MetalLB 全部组件。
-
kubectl get all -n metallb-system- 用途:查看 MetalLB 命名空间下所有资源状态。
- 参数含义 :
get all列出常见资源(Pod 等);-n指定命名空间。 - 预期作用:输出显示 1 个 controller 与 3 个 speaker(每个节点一个)均 Running,MetalLB 正常工作后再进行下一步。
-
cat << 'EOF' > ippool.yaml ... EOF- 用途 :生成 MetalLB 地址池清单
ippool.yaml(<<'EOF'无变量展开)。 - 预期作用 :定义可对外分配的 IP 地址范围。字段含义见上方 YAML 内
#注释。
- 用途 :生成 MetalLB 地址池清单
-
kubectl apply -f ippool.yaml------ 应用地址池。 -
cat << 'EOF' > L2.yaml ... EOF------ 生成 L2 通告清单L2.yaml。字段含义见上方 YAML 内#注释。 -
kubectl apply -f L2.yaml------ 应用二层通告配置。至此 MetalLB 配置完成,可按 L2 模式对外分配地址。
步骤 8:部署 Ingress
Nginx Ingress Controller 是集群的南北向流量入口:外部请求 → Ingress(域名规则) → Nginx Service → Nginx Pod;同时配合 MetalLB 拿到外部 IP。
原始命令:
bash
root@master30:~/wordpress# tar -xf ingress-nginx-controller-v1.11.2.tar.gz
root@master30:~/wordpress# kubectl apply -f ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml
root@master30:~/wordpress# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.107.255.83 10.1.8.40 80:30973/TCP,443:32344/TCP 14s
service/ingress-nginx-controller-admission ClusterIP 10.106.37.158 <none> 443/TCP 8m15s
root@master30:~/wordpress# cat > blog-ingress.yaml <<'EOF'
apiVersion: networking.k8s.io/v1 # API 版本:Ingress 属于 networking.k8s.io 组的 v1(稳定版 API)
kind: Ingress # 资源类型:HTTP/S 层(七层)路由规则,把外部请求按域名/路径转发到集群内 Service
metadata:
name: blog # Ingress 名称
namespace: wordpress # 命名空间
annotations: # 注解:给 Ingress Controller 的附加指令
nginx.ingress.kubernetes.io/ssl-passthrough: "true" # SSL 透传:Ingress 不解密,直接把 TLS 流量原样转发给后端(后端 Nginx Pod 自己完成 TLS 终止,依赖 6.2 的证书)
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # 声明后端协议为 HTTPS:Ingress 以 HTTPS 方式连接后端 Service 的 443 端口
spec:
ingressClassName: nginx # 指定由哪个 Ingress Controller 处理(这里是 Nginx Ingress Controller)
rules:
- host: blog.liujn.cloud # 域名规则,访问该域名时命中此规则(客户端需配置 DNS 解析)
http:
paths:
- path: / # 匹配的 URL 路径,/ 匹配全部路径
pathType: Prefix # 路径匹配类型:前缀匹配(以 / 为前缀的请求均命中)
backend:
service:
name: nginx # 转发目标后端 Service 名 nginx
port:
number: 443 # 转发到后端 Service 的 443 端口(对应 nginx-service 里的 https 端口)
EOF
root@master30:~/wordpress# kubectl apply -f blog-ingress.yaml
root@master30:~/wordpress# kubectl describe ingress blog
Name: blog
Labels: <none>
Namespace: wordpress
Address:
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
blog.liujn.cloud
/ nginx:443 (10.224.113.140:443,10.224.19.12:443)
Annotations: nginx.ingress.kubernetes.io/backend-protocol: HTTPS
nginx.ingress.kubernetes.io/ssl-passthrough: true
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 6s nginx-ingress-controller Scheduled for sync
命令详解:
-
tar -xf ingress-nginx-controller-v1.11.2.tar.gz- 用途:解压 Nginx Ingress Controller v1.11.2 资源包。
- 参数含义 :
tar -xf解压指定归档。 - 预期作用 :解压出含
deploy/static/provider/cloud/deploy.yaml的部署清单。
-
kubectl apply -f ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml- 用途:部署 Nginx Ingress Controller(cloud provider 清单,配合 MetalLB 获取外部 IP)。
- 参数含义 :
apply -f应用清单;路径选择 provider/cloud 版本以支持 LoadBalancer 类型。 - 预期作用 :在
ingress-nginx命名空间部署 controller 及配套服务。
-
kubectl get svc -n ingress-nginx- 用途:查看 Ingress Controller 的服务状态。
- 参数含义 :
svc服务类型;-n命名空间。 - 预期作用 :输出显示
ingress-nginx-controller类型为LoadBalancer,EXTERNAL-IP 为10.1.8.40(由 MetalLB 从10.1.8.40-10.1.8.80池分配),80:30973/TCP,443:32344/TCP表示外部 80/443 映射到节点的 30973/32344 端口。
-
cat > blog-ingress.yaml <<'EOF' ... EOF------ 用 here-document 生成 Ingress 规则清单,各字段含义见上方 YAML 内#注释。 -
kubectl apply -f blog-ingress.yaml------ 创建 Ingressblog。 -
kubectl describe ingress blog- 用途:查看 Ingress 的详细配置、后端与事件。
- 参数含义 :
describe显示详请。 - 预期作用 :输出显示规则
blog.liujn.cloud / → nginx:443(后端为两个 Nginx Pod 的 443 端口,10.224.x.x 为 Pod IP),Annotations 已生效,事件显示已同步。
步骤 9:初始化站点
Windows 访问需提前配置域名解析(如将
blog.liujn.cloud解析到 MetalLB 分配的外部 IP10.1.8.40)。
访问 https://blog.liujn.cloud,进入 WordPress 博客站点页面:

点击 现在安装,继续:

后续过程省略(按提示完成站点标题、管理员账号、邮箱即可进入后台)。
部署小结
至此,已完整部署一套基于 K8s 的 LNMP 架构 WordPress 博客,核心链路总结如下:
- 存储层 :NFS 服务器(
/wordpress共享目录)→ NFS Provisioner 监听 PVC → StorageClasswordpress动态创建 PV; - 数据层:MySQL 以 StatefulSet 部署,数据经 PVC(RWO)持久化到 NFS,账号密码通过 Secret 注入;
- 应用层:PHP-FPM(Deployment ×2)与 Nginx(Deployment ×2)共享同一 PVC(RWX),PHP 代码与站点文件统一存储;
- 入口层 :Ingress(
blog.liujn.cloud)→ Nginx Service → Nginx Pod,Nginx 经fastcgi_pass转发 PHP 请求至 PHP Service → PHP-FPM 容器,PHP 再连接 MySQL 完成数据读写; - 安全:Nginx 的 TLS 证书/私钥以 Secret 保存,配置以 ConfigMap 保存,数据库密码以 Secret 保存;
- 扩展能力:MetalLB 为入口提供外部 IP,Ingress Controller 统一北向流量,后续可按项目需求 6、7 追加 Metrics Server 与 HPA(最小 2、最大 5 副本),实现基于 CPU/内存指标的自动扩缩容。
注意事项:
- 自签名证书仅为实验用途,生产环境建议使用 Let's Encrypt 等受信证书,并在 Ingress 层配置证书校验;
- NFS 共享权限使用了
no_root_squash,生产环境应改回root_squash并细化访问白名单;- MySQL 仅单副本,生产建议配合备份/从库方案;
ssl-passthrough+ Nginx Pod 自身终止 TLS 是本例采用的方式(适合自签名证书场景),也可改为由 Ingress Controller 统一终止 TLS 以便复用证书管理能力。
K8s 部署 LNMP 架构的文章到此结束。