K8s实战|用Secret+ConfigMap部署带SSL的WordPress博客,MySQL与WordPress全流程拆解
摘要:本文基于 Kubernetes 原生资源(Secret、ConfigMap、Pod)完成一套带 HTTPS 加密的 WordPress 博客部署,涵盖 MySQL 凭据脱敏存储、自定义 SSL 镜像构建、自签证书注入、Apache vhost 配置挂载等核心环节,全程可复现。
一、实验架构总览
整个 Blog 应用由两个 Pod 组成:
| 组件 | 镜像 | 作用 | 关键配置来源 |
|---|---|---|---|
| MySQL | hub.laoma.cloud/library/mysql:latest |
数据库服务 | Secret mysql(root 密码、业务库账号密码) |
| WordPress | hub.laoma.cloud/library/wordpress:ssl(自定义构建) |
Web 博客 | Secret mysql(连库凭据)+ Secret www-tls(SSL证书)+ ConfigMap blog-config(Apache vhost) |
数据流向:浏览器 → WordPress Pod(443/HTTPS) → MySQL Pod(3306)。
二、部署 MySQL:凭据全部走 Secret
2.1 为什么要用 Secret?
MySQL 镜像启动时需要传入 root 密码、业务账号、业务密码、业务库名四组敏感信息。如果直接写在 Pod 的 yaml 明文里,谁都能 kubectl get pod -o yaml 看到,存在泄露风险。用 Secret 统一存放,Pod 通过 secretKeyRef 引用,配置文件里只出现 key 名,不出现真实值。
2.2 创建 Secret
先准备一个 env 文件,把四组变量写进去:
bash
# 编写环境变量文件
[root@master30 configure]# vim mysql-env.yaml
MYSQL_ROOT_PASSWORD=123
MYSQL_USER=blog
MYSQL_PASSWORD=123
MYSQL_DATABASE=blog
注意:这个文件虽然叫
.yaml,但内容其实是KEY=VALUE格式,配合--from-env-file使用。
用 kubectl create secret 一键生成:
bash
# 基于 env 文件创建 generic 类型 Secret
[root@master30 configure]# kubectl create secret generic mysql --from-env-file=./mysql-env.yaml
secret/mysql created
# 验证 Secret 已生成,DATA 为 4 说明有 4 个键值对
[root@master30 configure]# kubectl get secrets
NAME TYPE DATA AGE
mysql Opaque 4 6s
2.3 编写 MySQL Pod
bash
[root@master30 configure]# vim pod-mysql.yaml
yaml
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
containers:
- image: hub.laoma.cloud/library/mysql:latest
imagePullPolicy: IfNotPresent
name: mysql
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql
key: MYSQL_ROOT_PASSWORD
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql
key: MYSQL_USER
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql
key: MYSQL_PASSWORD
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql
key: MYSQL_DATABASE
逐行要点说明:
imagePullPolicy: IfNotPresent:节点本地有镜像就不拉,省带宽。- 四个
env全部通过valueFrom.secretKeyRef引用 Secretmysql里对应的 key,yaml 里看不到真实密码。 MYSQL_DATABASE=blog这个环境变量的作用是:MySQL 容器首次初始化时自动创建名为blog的数据库。MYSQL_USER=blog+MYSQL_PASSWORD=123:自动创建业务账号blog,并自动授予blog库的全部权限。
2.4 启动并验证 MySQL
bash
# 应用 Pod
[root@master30 configure]# kubectl apply -f pod-mysql.yaml
# 查看 Pod 状态和分配到的 IP
[root@master30 configure]# kubectl get pods mysql -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 1/1 Running 0 25s 10.224.19.2 worker31.laoma.cloud <none> <none>
记下 MySQL Pod 的 IP:10.224.19.2(后面 WordPress 要连这个地址)。
用 mysql 客户端验证业务账号能正常登录并看到自动创建的 blog 库:
bash
# 用 blog 账号远程连接 MySQL Pod,查看数据库列表
[root@master30 configure]# mysql -u blog -p123 -h 10.224.19.2 -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| blog |
| information_schema |
| performance_schema |
+--------------------+
看到 blog 库说明 MySQL 初始化成功,业务账号权限正常。
⚠️ 小提示:命令行里直接
-p123会有 Warning,生产环境建议用-p回车后交互输入,或用~/.my.cnf配置。
三、构建带 SSL 支持的 WordPress 镜像
3.1 为什么要自己构建?
官方 wordpress 镜像基于 Apache,但默认没有开启 SSL 模块 (mod_ssl)。如果直接用官方镜像挂载 443 端口,Apache 会因为找不到 SSLEngine 指令对应的模块而启动失败。所以需要在官方镜像基础上启用 ssl、headers、rewrite 三个模块。
3.2 编写 Dockerfile
bash
[root@hub ~]# docker pull hub.laoma.cloud/library/wordpress:latest
[root@hub ~]# vim Dockerfile
dockerfile
FROM hub.laoma.cloud/library/wordpress
RUN a2enmod ssl && a2enmod headers && a2enmod rewrite
EXPOSE 80 443
逐行解释:
FROM:基于公司 Harbor 里的官方 wordpress 镜像。a2enmod ssl:开启 Apache SSL 模块(HTTPS 必备)。a2enmod headers:开启 HTTP 头控制模块(后续做安全头、跳转常用)。a2enmod rewrite:开启 URL 重写模块(WordPress 固定链接、伪静态必备)。EXPOSE 80 443:声明两个端口,80 跑 HTTP,443 跑 HTTPS。
3.3 构建并推送私有仓库
bash
# 构建镜像,打上 ssl 标签
[root@hub ~]# docker build -t hub.laoma.cloud/library/wordpress:ssl .
# 登录hub
[root@hub ~]# docker login hub.laoma.cloud -uadmin
# 推送到 Harbor 私有仓库,供 K8s 节点拉取
[root@hub ~]# docker push hub.laoma.cloud/library/wordpress:ssl
四、准备 SSL 证书
4.1 生成自签证书(三步走)
实验环境用自签证书即可,生产环境建议用 Let's Encrypt 或商业 CA。
bash
# 第 1 步:生成 2048 位 RSA 私钥
[root@master30 configure]# openssl genrsa -out www.key 2048
# 第 2 步:生成证书签名请求(CSR)
# -subj 里 CN 必须填网站域名,浏览器校验时会用到
[root@master30 configure]# openssl req -new -key www.key -out www.csr \
-subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=www.laoma.cloud/emailAddress=admin@laoma.cloud"
# 第 3 步:用私钥自签名,生成有效期 10 年(3650 天)的证书
[root@master30 configure]# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
4.2 把证书存成 TLS 类型 Secret
K8s 专门提供了 kubernetes.io/tls 类型的 Secret 来存证书,一条命令搞定:
bash
# 创建 TLS Secret,--cert 指向 crt,--key 指向 key
[root@master30 configure]# kubectl create secret tls www-tls --cert=./www.crt --key=./www.key
secret/www-tls created
# 验证:TYPE 为 kubernetes.io/tls,DATA 为 2(一个 crt 一个 key)
[root@master30 configure]# kubectl get secrets www-tls
NAME TYPE DATA AGE
www-tls kubernetes.io/tls 2 6s
💡 这种 tls 类型的 Secret 挂载到容器后,会自动生成
tls.crt和tls.key两个文件,路径正好对应 Apache 配置里写的/etc/ssl/certs/。
五、用 ConfigMap 提供 Apache vhost 配置
5.1 编写 SSL 虚拟主机配置
bash
[root@master30 configure]# vim wp-ssl.conf
apache
<VirtualHost *:443>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
# 注意证书位置:Secret 挂载后默认文件名就是 tls.crt / tls.key
SSLCertificateFile /etc/ssl/certs/tls.crt
SSLCertificateKeyFile /etc/ssl/certs/tls.key
<FilesMatch "\.(?:cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
</VirtualHost>
关键点:
SSLEngine on:开启 SSL。- 证书路径
/etc/ssl/certs/tls.crt和tls.key:后面我们把www-tls这个 Secret 挂载到/etc/ssl/certs,Secret 里的两个 key 会自动落成这两个文件名,路径完全对上。 SSLOptions +StdEnvVars:让 PHP 能通过$_SERVER['HTTPS']感知到当前是 HTTPS 访问,WordPress 才能正确生成 https 的链接。
5.2 创建 ConfigMap
bash
# 用 --from-file 把整个 conf 文件挂成 ConfigMap
[root@master30 configure]# kubectl create cm blog-config --from-file=wp-ssl.conf
# 验证
[root@master30 configure]# kubectl get cm blog-config
NAME DATA AGE
blog-config 1 7s
💡 ConfigMap 挂载到
/etc/apache2/sites-enabled目录后,文件名wp-ssl.conf会成为该目录下的一个配置文件,Apache 启动时自动加载。
六、部署 WordPress Pod
6.1 编写 Pod 定义
bash
[root@master30 configure]# vim pod-blog.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: blog
labels:
run: blog
spec:
containers:
- image: hub.laoma.cloud/library/wordpress:ssl
imagePullPolicy: IfNotPresent
name: wordpress
env:
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: mysql
key: MYSQL_USER
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql
key: MYSQL_PASSWORD
- name: WORDPRESS_DB_NAME
valueFrom:
secretKeyRef:
name: mysql
key: MYSQL_DATABASE
- name: WORDPRESS_DB_HOST
value: 10.224.19.2
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
volumeMounts:
- name: blog-config
mountPath: "/etc/apache2/sites-enabled"
- name: www-tls
mountPath: "/etc/ssl/certs"
volumes:
- name: blog-config
configMap:
name: blog-config
- name: www-tls
secret:
secretName: www-tls
配置逻辑拆解:
| 配置项 | 来源 | 作用 |
|---|---|---|
WORDPRESS_DB_USER |
Secret mysql → MYSQL_USER |
WordPress 连库用户名(blog) |
WORDPRESS_DB_PASSWORD |
Secret mysql → MYSQL_PASSWORD |
WordPress 连库密码(123) |
WORDPRESS_DB_NAME |
Secret mysql → MYSQL_DATABASE |
WordPress 使用的库名(blog) |
WORDPRESS_DB_HOST |
写死 10.224.19.2 |
MySQL Pod 的 Cluster IP(实验阶段直接写 IP,生产建议用 Service) |
blog-config 卷 |
ConfigMap | 挂载 Apache SSL vhost 配置到 sites-enabled |
www-tls 卷 |
Secret(tls) | 挂载证书和私钥到 /etc/ssl/certs |
6.2 启动并查看状态
bash
[root@master30 configure]# kubectl apply -f pod-blog.yaml
# 两个 Pod 都 Running 即正常
[root@master30 configure]# kubectl get pods
NAME READY STATUS RESTARTS AGE
blog 1/1 Running 0 3s
mysql 1/1 Running 0 10m
七、访问博客站点
Pod IP 是集群内部地址,外部无法直接访问。用 kubectl port-forward 把 Pod 的 443 端口转发到 master 节点的 8443 端口:
bash
# --address 指定监听地址,把 Pod 443 映射到本机 8443
[root@master30 configure]# kubectl port-forward pod/blog --address 10.1.8.30 8443:443
Forwarding from 10.1.8.30:8443 -> 443
然后浏览器访问:
https://10.1.8.30:8443/
因为是自签证书,浏览器会提示"您的连接不是私密连接",点"高级 → 继续前往"即可。看到 WordPress 安装向导页面就说明全链路打通了。
八、踩坑总结
- 官方 wordpress 镜像没有 mod_ssl :直接挂 443 会报
Invalid command 'SSLEngine',必须自己a2enmod ssl构建镜像。 - 证书路径要对得上 :tls 类型 Secret 挂载后文件名固定为
tls.crt和tls.key,Apache 配置里写的路径必须和挂载点拼起来一致。 - WORDPRESS_DB_HOST 不能写 localhost:WordPress 和 MySQL 是两个独立 Pod,必须写 MySQL Pod 的 IP(或 Service 名)。
- ConfigMap 挂载会覆盖原目录 :把
blog-config挂到/etc/apache2/sites-enabled后,原目录里的000-default.conf会被隐藏。如果发现 80 端口不响应,就是这个原因------本文我们只跑 443,所以不影响。 - Secret 更新不会自动滚动到 Pod:Secret/ConfigMap 修改后,已运行的 Pod 里的挂载文件不会实时更新,需要重建 Pod(生产环境建议用 Reloader 或滚动重启)。
九、整体流程图
浏览器
│ https://10.1.8.30:8443
▼
kubectl port-forward (master:8443 → blog:443)
│
▼
blog Pod (wordpress:ssl)
├── /etc/apache2/sites-enabled ← ConfigMap blog-config (wp-ssl.conf)
├── /etc/ssl/certs ← Secret www-tls (tls.crt + tls.key)
└── env ← Secret mysql (连库三件套)
│ TCP 3306
▼
mysql Pod (mysql:latest)
└── env ← Secret mysql (root密码 + blog账号 + blog库)
十、写在最后
这套实验把 K8s 里三个最常用的资源串了起来:Secret 管敏感凭据、ConfigMap 管配置文件、Pod 负责编排容器。掌握这个模式后,以后部署任何有状态应用(不只是 WordPress),思路都是一样的:
- 敏感信息 → Secret
- 普通配置文件 → ConfigMap
- 应用镜像里缺的模块 → 自己 Dockerfile 构建
- 证书 → tls 类型 Secret