Kubernetes 生产级实战:从零部署 LNMP 架构 ECShop 商城(完整指南)

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

ResourceQuotaquota.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

LimitRangelimitrange.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.clouddashboard.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 配置文件已尽量精简,可根据实际环境调整镜像版本和资源大小。如需完整代码包,可在评论区留言。

相关推荐
宋哥转AI1 小时前
Agent实战 #01:Dream-SaaS 整体架构拆解
架构
@insist1231 小时前
系统规划与管理师-计算与存储资源规划核心考点解析
架构·软考·系统规划与管理师·软件水平考试·系统规划与管理工程师
国科安芯1 小时前
基于ASM1042S2S的箭载通信网络抗辐射加固方案研究
服务器·网络·嵌入式硬件·fpga开发·架构·信号处理
AI 大模型学习不踩坑1 小时前
Harness 架构原理与工程实践详解
java·人工智能·神经网络·机器学习·计算机视觉·架构
磐链科技1 小时前
链游技术服务全景透视:从基建到工具链的实战指南
架构·web3·区块链
IT大白鼠2 小时前
K8S发展历史及未来展望
云原生·容器·kubernetes
weixin_439930642 小时前
Kubernetes(K8s)入门实践
云原生·容器·kubernetes·k8s
小明bishe1811 小时前
计算机毕业设计之基于JAVA的植物科普网站
java·spring boot·spring·架构·课程设计
聪明的一休丶12 小时前
VLLM v0.24.0 版本深度解析:新引擎、新架构与大规模服务全家桶升级
python·架构·vllm