Kubernetes 生产级实战:从零部署 LNMP 架构 ECShop 商城(完整指南)
手把手教你用 K8s 部署一个高可用的 LNMP 电商网站,涵盖存储、配额、健康检查、HPA、Ingress、Dashboard 全链路
前言
ECShop 是一款经典的 PHP 开源电商系统,LNMP(Linux + Nginx + MySQL + PHP)是 Web 应用最经典的架构之一。本文将带你 在 Kubernetes 集群中完整部署一套 ECShop 商城,不仅实现基础功能,还融入了生产环境必备的:
- 动态存储卷供应(NFS)
- 资源配额与限制(ResourceQuota + LimitRange)
- 有状态应用管理(MySQL StatefulSet)
- 健康探针(Liveness / Readiness)
- 自动扩缩容(HPA)
- 安全通信(Ingress + TLS)
- 图形化管理(Kubernetes Dashboard)
阅读本文前,你需要具备 Kubernetes 基础操作能力,了解 YAML 编写和 kubectl 使用。所有示例基于 K8s v1.30 + containerd + Calico。
整体架构
我们使用以下架构图展示各组件之间的关系:
#mermaid-svg-ZkEsWRIxRHXgcotN{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-ZkEsWRIxRHXgcotN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZkEsWRIxRHXgcotN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZkEsWRIxRHXgcotN .error-icon{fill:#552222;}#mermaid-svg-ZkEsWRIxRHXgcotN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZkEsWRIxRHXgcotN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZkEsWRIxRHXgcotN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZkEsWRIxRHXgcotN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZkEsWRIxRHXgcotN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZkEsWRIxRHXgcotN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZkEsWRIxRHXgcotN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZkEsWRIxRHXgcotN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZkEsWRIxRHXgcotN .marker.cross{stroke:#333333;}#mermaid-svg-ZkEsWRIxRHXgcotN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZkEsWRIxRHXgcotN p{margin:0;}#mermaid-svg-ZkEsWRIxRHXgcotN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZkEsWRIxRHXgcotN .cluster-label text{fill:#333;}#mermaid-svg-ZkEsWRIxRHXgcotN .cluster-label span{color:#333;}#mermaid-svg-ZkEsWRIxRHXgcotN .cluster-label span p{background-color:transparent;}#mermaid-svg-ZkEsWRIxRHXgcotN .label text,#mermaid-svg-ZkEsWRIxRHXgcotN span{fill:#333;color:#333;}#mermaid-svg-ZkEsWRIxRHXgcotN .node rect,#mermaid-svg-ZkEsWRIxRHXgcotN .node circle,#mermaid-svg-ZkEsWRIxRHXgcotN .node ellipse,#mermaid-svg-ZkEsWRIxRHXgcotN .node polygon,#mermaid-svg-ZkEsWRIxRHXgcotN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZkEsWRIxRHXgcotN .rough-node .label text,#mermaid-svg-ZkEsWRIxRHXgcotN .node .label text,#mermaid-svg-ZkEsWRIxRHXgcotN .image-shape .label,#mermaid-svg-ZkEsWRIxRHXgcotN .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZkEsWRIxRHXgcotN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZkEsWRIxRHXgcotN .rough-node .label,#mermaid-svg-ZkEsWRIxRHXgcotN .node .label,#mermaid-svg-ZkEsWRIxRHXgcotN .image-shape .label,#mermaid-svg-ZkEsWRIxRHXgcotN .icon-shape .label{text-align:center;}#mermaid-svg-ZkEsWRIxRHXgcotN .node.clickable{cursor:pointer;}#mermaid-svg-ZkEsWRIxRHXgcotN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZkEsWRIxRHXgcotN .arrowheadPath{fill:#333333;}#mermaid-svg-ZkEsWRIxRHXgcotN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZkEsWRIxRHXgcotN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZkEsWRIxRHXgcotN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZkEsWRIxRHXgcotN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZkEsWRIxRHXgcotN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZkEsWRIxRHXgcotN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZkEsWRIxRHXgcotN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZkEsWRIxRHXgcotN .cluster text{fill:#333;}#mermaid-svg-ZkEsWRIxRHXgcotN .cluster span{color:#333;}#mermaid-svg-ZkEsWRIxRHXgcotN 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-ZkEsWRIxRHXgcotN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZkEsWRIxRHXgcotN rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZkEsWRIxRHXgcotN .icon-shape,#mermaid-svg-ZkEsWRIxRHXgcotN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZkEsWRIxRHXgcotN .icon-shape p,#mermaid-svg-ZkEsWRIxRHXgcotN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZkEsWRIxRHXgcotN .icon-shape .label rect,#mermaid-svg-ZkEsWRIxRHXgcotN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZkEsWRIxRHXgcotN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZkEsWRIxRHXgcotN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZkEsWRIxRHXgcotN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-ZkEsWRIxRHXgcotN .infra>*{fill:#e6f7ff!important;stroke:#1890ff!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .infra span{fill:#e6f7ff!important;stroke:#1890ff!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .storage>*{fill:#f0f8fb!important;stroke:#23967c!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .storage span{fill:#f0f8fb!important;stroke:#23967c!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .app>*{fill:#fff7e6!important;stroke:#faad14!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .app span{fill:#fff7e6!important;stroke:#faad14!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .svc>*{fill:#fef0f0!important;stroke:#f5222d!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .svc span{fill:#fef0f0!important;stroke:#f5222d!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .gateway>*{fill:#f6ffed!important;stroke:#52c41a!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .gateway span{fill:#f6ffed!important;stroke:#52c41a!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .secret>*{fill:#f9f0ff!important;stroke:#722ed1!important;}#mermaid-svg-ZkEsWRIxRHXgcotN .secret span{fill:#f9f0ff!important;stroke:#722ed1!important;} Dashboard_GROUP
Nginx_GROUP
PHP_GROUP
MySQL_GROUP
right
NFS服务器
10.1.8.30 /ecshop
PV 动态卷制备
PVC-mysql
ConfigMap: nginx-config
PVC-ecshop
Secret: mysql账号密码
Secret: nginx-tls
通用TLS证书
Statefulset: mysql
Service: mysql HeadLess
Service: mysql ClusterIP
Deployment: php
Service: php ClusterIP
Deployment: nginx
Service: nginx ClusterIP
Deployment: kubernetes-dashboard
Service: kubernetes-dashboard ClusterIP
MetalLB 负载均衡
分配外部LB VIP
Ingress-Nginx Controller
集群入口网关
Ingress规则: shop.laoma.cloud
Ingress规则: dashboard.laoma.cloud
- 存储层:NFS 提供共享目录,动态卷制备(StorageClass)为 MySQL 和 ECShop 代码提供持久存储。
- 应用层:MySQL(StatefulSet)、PHP(Deployment)、Nginx(Deployment)各自独立,通过 Service 通信。
- 网关层:MetalLB 分配外部 IP,Ingress-Nginx 作为七层路由,TLS 证书保障 HTTPS。
- 可观测性:Metrics Server + HPA 实现自动扩缩容,Dashboard 提供 Web 管理界面。
一、环境准备
1.1 Kubernetes 集群
- 版本:v1.30
- 节点:1 Master + 2 Worker(IP:10.1.8.30/31/32)
- 网络插件:Calico
- 容器运行时:containerd
1.2 部署 NFS 服务器(在 Master 或独立节点)
bash
# 安装 NFS 服务端
apt install -y nfs-kernel-server
# 创建共享目录并设置权限
mkdir -m 777 /ecshop
# 编辑 /etc/exports
echo "/ecshop *(rw,sync,no_root_squash,no_all_squash,insecure)" >> /etc/exports
# 重启服务
systemctl restart nfs-server
Worker 节点安装 NFS 客户端:
bash
apt install -y nfs-common
二、存储类与命名空间资源配额
2.1 创建 NFS 动态卷制备器
使用 nfs-subdir-external-provisioner 实现动态供应。部署文件(nfs-provisioner.yaml):
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: kube-system
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: kube-system
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
volumes:
- name: nfs-client-root
nfs:
server: 10.1.8.30
path: /ecshop
containers:
- name: nfs-client-provisioner
image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 10.1.8.30
- name: NFS_PATH
value: /ecshop
创建 StorageClass:
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ecshop-storage
provisioner: fuseim.pri/ifs
reclaimPolicy: Delete
volumeBindingMode: Immediate
应用:
bash
kubectl apply -f nfs-provisioner.yaml
kubectl apply -f storageclass.yaml
2.2 创建命名空间与资源配额
bash
kubectl create ns ecshop
ResourceQuota (quota.yaml):
yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: ecshop-quota
namespace: ecshop
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "4"
limits.memory: 8Gi
requests.storage: 500Gi
LimitRange (limitrange.yaml):
yaml
apiVersion: v1
kind: LimitRange
metadata:
name: ecshop-limits
namespace: ecshop
spec:
limits:
- type: Container
max:
cpu: "1"
memory: 2Gi
min:
cpu: 100m
memory: 256Mi
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 200m
memory: 256Mi
应用:
bash
kubectl apply -f quota.yaml -f limitrange.yaml
三、部署 MySQL(StatefulSet)
3.1 创建 Secret 存储数据库凭证
yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: ecshop
type: Opaque
stringData:
root-password: "Laoma@123"
ecshop-user: "ecshop"
ecshop-password: "Laoma@123"
ecshop-database: "ecshop"
3.2 Headless Service 与 ClusterIP Service
Headless Service(用于 StatefulSet 网络标识):
yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
namespace: ecshop
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
ClusterIP Service(供 PHP 应用访问):
yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: ecshop
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
3.3 StatefulSet 配置
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: ecshop
spec:
serviceName: mysql-headless
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql-secret
key: ecshop-database
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: ecshop-user
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: ecshop-password
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
livenessProbe:
exec:
command: ["mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD}"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command: ["mysql", "-h", "127.0.0.1", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}", "-e", "SELECT 1"]
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: ecshop-storage
resources:
requests:
storage: 50Gi
四、部署 PHP(Deployment)
4.1 镜像准备
需要构建一个包含 ECShop 代码的 PHP-FPM 镜像。本文使用预构建镜像 php:7.2-fpm-ecshop(基于官方 php:7.2-fpm,复制 ECShop 源码到 /var/www/html)。
4.2 Deployment 与 Service
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: php
namespace: ecshop
spec:
replicas: 1
selector:
matchLabels:
app: php
template:
metadata:
labels:
app: php
spec:
containers:
- name: php-fpm
image: php:7.2-fpm-ecshop
ports:
- containerPort: 9000
volumeMounts:
- name: ecshop-code
mountPath: /var/www/html
livenessProbe:
tcpSocket:
port: 9000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
exec:
command: ["php", "-v"] # 简单验证 PHP 可执行
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: ecshop-code
persistentVolumeClaim:
claimName: ecshop-code-pvc
---
apiVersion: v1
kind: Service
metadata:
name: php
namespace: ecshop
spec:
selector:
app: php
ports:
- port: 9000
targetPort: 9000
PVC 用于存放 ECShop 代码(由 NFS 动态供应):
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ecshop-code-pvc
namespace: ecshop
spec:
accessModes: ["ReadWriteMany"]
storageClassName: ecshop-storage
resources:
requests:
storage: 10Gi
注意:PHP 和 Nginx 需要共享代码,因此使用
ReadWriteMany。
五、部署 Nginx(Deployment)
5.1 ConfigMap 存储 Nginx 配置
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: ecshop
data:
default.conf: |
server {
listen 80;
server_name shop.laoma.cloud;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
5.2 Deployment 与 Service
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: ecshop
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24
ports:
- containerPort: 80
volumeMounts:
- name: ecshop-code
mountPath: /var/www/html
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
livenessProbe:
httpGet:
path: /index.php
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /index.php
port: 80
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
volumes:
- name: ecshop-code
persistentVolumeClaim:
claimName: ecshop-code-pvc
- name: nginx-config
configMap:
name: nginx-config
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: ecshop
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
六、Ingress 与 TLS(外部访问)
6.1 部署 MetalLB(裸机 LB)
bash
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
配置 IP 地址池(ippool.yaml):
yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: ecshop-pool
namespace: metallb-system
spec:
addresses:
- 10.1.8.40-10.1.8.50
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: ecshop-l2
namespace: metallb-system
6.2 部署 Ingress-Nginx Controller
bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml
(若国内网络慢,请替换镜像源)
6.3 创建 TLS 证书 Secret
bash
# 生成自签名证书(生产环境应使用正式证书)
openssl req -x509 -newkey rsa:2048 -nodes -keyout tls.key -out tls.crt -days 365 -subj "/CN=*.laoma.cloud"
kubectl create secret tls nginx-tls --key=tls.key --cert=tls.crt -n ecshop
6.4 创建 Ingress 规则
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ecshop-ingress
namespace: ecshop
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.laoma.cloud
secretName: nginx-tls
rules:
- host: shop.laoma.cloud
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
七、HPA 自动扩缩容
7.1 部署 Metrics Server
(参考之前文章,需添加 --kubelet-insecure-tls)
7.2 创建 HPA 规则
yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-hpa
namespace: ecshop
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php
minReplicas: 2
maxReplicas: 5
metrics:
- resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
type: Resource
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
namespace: ecshop
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 2
maxReplicas: 5
metrics:
- resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
type: Resource
八、部署 Kubernetes Dashboard 并配置 Ingress
8.1 部署 Dashboard
bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.1/aio/deploy/recommended.yaml
8.2 创建管理员 ServiceAccount 并获取 Token
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: dashboard-admin
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dashboard-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: dashboard-admin
namespace: kubernetes-dashboard
bash
kubectl -n kubernetes-dashboard create token dashboard-admin
8.3 创建 Dashboard Ingress
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
tls:
- hosts:
- dashboard.laoma.cloud
secretName: nginx-tls # 复用同一个证书
rules:
- host: dashboard.laoma.cloud
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubernetes-dashboard
port:
number: 443
九、验证与测试
9.1 检查资源状态
bash
kubectl get all -n ecshop
kubectl get pvc -n ecshop
kubectl get hpa -n ecshop
kubectl get ingress -n ecshop
9.2 访问站点
配置本地 hosts(或 DNS),将 shop.laoma.cloud 和 dashboard.laoma.cloud 解析到 MetalLB 分配的 Ingress IP(例如 10.1.8.40)。
浏览器打开 https://shop.laoma.cloud,按照 ECShop 安装向导完成数据库连接配置(使用 Service mysql,端口 3306,用户名 ecshop,密码 Laoma@123,数据库 ecshop)。
安装成功后,商城即可运行。
9.3 测试 HPA
增加负载观察扩容:
bash
# 压测 PHP 服务(假设 Service ClusterIP)
ab -n 100000 -c 100 http://<php-svc-ip>:9000/
观察 HPA 状态:
bash
watch 'kubectl get hpa -n ecshop'
9.4 访问 Dashboard
浏览器打开 https://dashboard.laoma.cloud,使用上述 Token 登录。
十、总结与最佳实践
| 组件 | 关键技术点 |
|---|---|
| 存储 | NFS 动态供应 + StorageClass,MySQL 使用 ReadWriteOnce,代码使用 ReadWriteMany |
| 配额 | ResourceQuota 限制总资源,LimitRange 为 Pod 设置默认 requests/limits |
| 数据库 | StatefulSet 保障持久化身份,Secret 管理敏感信息,健康探针确保可用性 |
| 应用 | PHP 与 Nginx 分离,共享代码 PVC,Nginx 配置通过 ConfigMap 管理 |
| 网关 | MetalLB + Ingress-Nginx,TLS 证书统一管理,HTTPS 强制跳转 |
| 扩缩容 | Metrics Server + HPA 按 CPU 自动调整副本数,从容应对流量波动 |
| 运维 | Dashboard 提供 Web 管理,方便查看集群状态和资源 |
常见问题排查
- MySQL 启动失败:检查 NFS 权限和存储类是否正常;确保 Secret 中的密码正确。
- PHP 无法连接 MySQL :确认 Service 名称
mysql解析正确,检查数据库是否就绪。 - Ingress 返回 404:检查 Ingress 规则路径和后端 Service 端口匹配。
- HPA 不生效 :确认 Pod 设置了
resources且 Metrics Server 工作正常。
结语
至此,我们成功在 Kubernetes 中部署了一套完整的 LNMP 架构 ECShop 商城,并集成了存储卷、资源配额、健康检查、自动扩缩容、安全网关和图形化运维等生产级特性。这套架构模式可以复用于大多数 PHP/Java Web 应用,希望对你的云原生实战有所帮助。
如果觉得内容实用,欢迎 点赞、收藏、转发,也欢迎在评论区交流你的部署经验!
所有 YAML 配置文件已尽量精简,可根据实际环境调整镜像版本和资源大小。如需完整代码包,可在评论区留言。