K8s 部署 ECShop 商城完整实操|容器化搭建电商网站

依据实际操作日志整理(master30 / worker31 / worker32),与题目 12 项一一对应;
域名按本人要求使用 *.ningcode.cn(镜像仓库为内网 hub.laoma.cloud),如需更换域名,替换对应 hosts、证书 CN 和 ingress 中的 host 即可。

环境信息

主机 IP 角色
master30 10.1.8.30 控制节点 + NFS 服务器
worker31 10.1.8.31 工作节点(NFS 客户端)
worker32 10.1.8.32 工作节点(NFS 客户端,mysql-0 所在)
共享目录 10.1.8.30:/ecshop NFS 动态卷后端
镜像仓库 hub.laoma.cloud(192.168.46.201) 内网 registry
业务域名 shop.ningcode.cn / dashboard.ningcode.cn Ingress 访问

1. 部署 NFS 服务器,提供共享目录 /ecshop

1.1 master30 安装 NFS 服务端并创建共享目录

apt install -y nfs-kernel-server
mkdir -m 777 /ecshop
echo hellow > /ecshop/index.html        # 测试文件,验证挂载
vim /etc/exports                        # 添加共享配置
cat /etc/exports
# /etc/exports 增加一行
/ecshop *(rw)
exportfs -r
exportfs -v
# /ecshop       	<world>(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
systemctl restart nfs-server

1.2 worker31 / worker32 安装 NFS 客户端

# worker31、worker32 分别执行
apt install -y nfs-common

2. 部署 NFS 动态卷制备,存储类型 ecshop

创建命名空间并切换默认命名空间(题目 3 的资源均在 ecshop 命名空间):

kubectl create ns ecshop
kubectl config set-context --current --namespace ecshop

编写 ecshop-nfs.yaml(ServiceAccount + RBAC + Provisioner Deployment + StorageClass):

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner
  namespace: ecshop
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-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-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: ecshop
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-subdir-provisioner
  namespace: ecshop
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-provisioner
  template:
    metadata:
      labels:
        app: nfs-provisioner
    spec:
      serviceAccountName: nfs-provisioner
      containers:
      - name: nfs-provisioner
        image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
        imagePullPolicy: IfNotPresent
        env:
        - name: PROVISIONER_NAME
          value: ecshop/nfs-provisioner
        - name: NFS_SERVER
          value: 10.1.8.30      # 替换成你的 master 节点 NFS 服务器 IP
        - name: NFS_PATH
          value: /ecshop
        volumeMounts:
        - name: nfs-client-root
          mountPath: /persistentvolumes
      volumes:
      - name: nfs-client-root
        nfs:
          server: 10.1.8.30     # NFS 服务器 IP,同上
          path: /ecshop
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ecshop
provisioner: ecshop/nfs-provisioner
reclaimPolicy: Delete
volumeBindingMode: Immediate

应用并验证:

kubectl apply -f ecshop-nfs.yaml
# serviceaccount/nfs-provisioner created
# clusterrole.rbac.authorization.k8s.io/nfs-provisioner-runner created
# clusterrolebinding.rbac.authorization.k8s.io/run-nfs-provisioner created
# deployment.apps/nfs-subdir-provisioner created
# storageclass.storage.k8s.io/ecshop created

kubectl get sc
# NAME     PROVISIONER              RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
# ecshop   ecshop/nfs-provisioner   Delete          Immediate           false                  60s

创建测试 PVC 验证动态制备:

cat > test-pvc.yaml <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
  namespace: ecshop
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: ecshop
  resources:
    requests:
      storage: 1Gi
EOF
kubectl apply -f test-pvc.yaml
kubectl get pvc -n ecshop      # test-pvc 状态为 Bound

3. 商城相关资源部署在 ecshop 命名空间

kubectl create ns ecshop
kubectl config set-context --current --namespace ecshop

后续 mysql、php、nginx、nfs-provisioner 等资源全部创建在 ecshop 命名空间。


4. 为 ecshop 命名空间设置 Quota

kubectl create quota ecshop-quota -n ecshop \
  --hard=cpu=4,memory=8G,requests.cpu=4,requests.memory=8G,requests.storage=500G
# resourcequota/ecshop-quota created

注意:第一次写 request.cpu 会报错
must be a standard resource for quota,正确的资源名是 requests.cpu / requests.memory

验证:

kubectl get resourcequota -n ecshop
kubectl describe resourcequota ecshop-quota -n ecshop

5. 为 ecshop 命名空间设置 LimitRange

编写 LimitRange.yaml

apiVersion: v1
kind: LimitRange
metadata:
  name: limit-ecshop
  namespace: ecshop
spec:
  limits:
  - type: Container
    defaultRequest:        # requests 默认值
      cpu: 200m
      memory: 256Mi
    default:               # limits 默认值
      cpu: 500m
      memory: 512Mi
    min:                   # 最小
      cpu: 100m
      memory: 256Mi
    max:                   # 最大
      cpu: "1"
      memory: 2048Mi

应用并验证:

kubectl apply -f LimitRange.yaml
# limitrange/limit-ecshop created

kubectl describe limitranges -n ecshop
# Name:       limit-ecshop
# Namespace:  ecshop
# Type        Resource  Min    Max  Default Request  Default Limit  Max Limit/Request Ratio
# ----        --------  ---    ---  ---------------  -------------  -----------------------
# Container   cpu       100m   1    200m             500m           -
# Container   memory    256Mi  2Gi  256Mi            512Mi          -

说明:2Gi 即题目要求的 2048Mi,随后 php/nginx/mysql Pod 创建时会被 LimitRanger 自动注入上述 requests/limits(日志中 Pod 注解可见 LimitRanger plugin set)。


6. 部署 mysql 应用(StatefulSet + Secret + Probe + Service)

6.1 Secret(root 密码、业务用户 ecshop、数据库 ecshop)

echo -n "ningcode@123" | base64
# bmluZ2NvZGVAMTIz

编写 mysql-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
  namespace: ecshop
type: Opaque
data:
  # echo -n "Root@123456" | base64  修改为你想要的 root 密码
  mysql-root-password: Um9vdEAxMjM0NTY=
  # 业务用户 ecshop
  mysql-user: ZWNzaG9w
  mysql-password: bmluZ2NvZGVAMTIz
  mysql-database: ZWNzaG9w

6.2 StatefulSet + Service

编写 mysql-sts.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: ecshop
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: "mysql-headless"     # sts 绑定 headless 服务
  replicas: 1
  minReadySeconds: 10
  template:
    metadata:
      labels:
        app: mysql
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mysql
        image: hub.laoma.cloud/library/mysql:5.7     # 从内网仓库拉取 mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: mysql-root-password
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: mysql-user
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: mysql-password
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: mysql-database
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
        # 健康探针
        livenessProbe:
          exec:
            command: ["mysqladmin","-uroot","-p$(MYSQL_ROOT_PASSWORD)","ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command: ["mysql","-uroot","-p$(MYSQL_ROOT_PASSWORD)","-e","SELECT 1"]
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: ecshop        # NFS 动态卷
      resources:
        requests:
          storage: 2Gi
---
# Headless Service 给 StatefulSet 内部网络使用
apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
  namespace: ecshop
spec:
  selector:
    app: mysql
  clusterIP: None
  ports:
  - port: 3306
    targetPort: mysql
---
# 业务访问用 service,普通 ClusterIP,供 ecshop 商城连接 mysql
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: ecshop
spec:
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: mysql

应用并验证:

kubectl apply -f mysql-secret.yaml
# secret/mysql-secret created
kubectl apply -f mysql-sts.yaml
# statefulset.apps/mysql created
# service/mysql-headless created
# service/mysql configured

kubectl get sts,pod,svc,pvc -n ecshop
# statefulset.apps/mysql   1/1
# pod/mysql-0              1/1 Running   (调度到 worker32.ningcode.cn)
# service/mysql            ClusterIP  None  3306/TCP
# service/mysql-headless   ClusterIP  None  3306/TCP
# pvc/mysql-data-mysql-0   Bound  2Gi  RWO  ecshop

验证业务账号可登录:

kubectl exec -it mysql-0 -n ecshop -- mysql -uecshop -pningcode@123
# mysql: [Warning] Using a password on the command line interface can be insecure.
# Welcome to the MySQL monitor.
# Server version: 5.7.44 MySQL Community Server (GPL)

说明:镜像最初写 mysql:5.7 拉取困难,后改为内网仓库 hub.laoma.cloud/library/mysql:5.7(日志注释“从内网下镜像”)后正常启动。


7. 部署 php 应用(Deployment + ClusterIP Service)

7.1 商城源码共享 PVC(php、nginx 共用)

编写 ecshop-code-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ecshop-code-pvc
  namespace: ecshop
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: ecshop
  resources:
    requests:
      storage: 10Gi
kubectl apply -f ecshop-code-pvc.yaml
kubectl get pvc -n ecshop
# ecshop-code-pvc   Bound   10Gi   RWX   ecshop

7.2 php-fpm Deployment + Service

编写 php-fpm.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-fpm
  namespace: ecshop
spec:
  replicas: 1
  selector:
    matchLabels:
      app: php-fpm
  template:
    metadata:
      labels:
        app: php-fpm
    spec:
      containers:
      - name: php-fpm
        image: hub.laoma.cloud/library/php:7.2-fpm-ecshop   # 自己构建并推送到内网仓库
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9000
          name: php-fpm
        volumeMounts:
        - name: ecshop-code
          mountPath: /usr/share/nginx/html      # 挂载商城源码共享 pvc
        # 健康探针,tcp 探测 9000 端口
        livenessProbe:
          tcpSocket:
            port: 9000
          initialDelaySeconds: 20
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          tcpSocket:
            port: 9000
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
      volumes:
      - name: ecshop-code
        persistentVolumeClaim:
          claimName: ecshop-code-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: php-fpm-svc
  namespace: ecshop
spec:
  selector:
    app: php-fpm
  type: ClusterIP
  ports:
  - port: 9000
    targetPort: 9000

应用并验证:

kubectl apply -f php-fpm.yaml
# deployment.apps/php-fpm created
# service/php-fpm-svc created

kubectl describe pod php-fpm-<xxx>
# Image:      hub.laoma.cloud/library/php:7.2-fpm-ecshop
# Port:       9000/TCP
# Liveness:   tcp-socket :9000 delay=20s timeout=5s period=10s
# Readiness:  tcp-socket :9000 delay=5s timeout=3s period=5s
# Mounts:     /usr/share/nginx/html from ecshop-code (rw)
# Requests/Limits 由 LimitRanger 注入 200m/256Mi 与 500m/512Mi

注意:若先 apply php-fpm 再创建 PVC,Pod 会 Pending(persistentvolumeclaim "ecshop-code-pvc" not found),先创建 ecshop-code-pvc 再 apply 即可。


8. 部署 nginx 应用(Deployment + ConfigMap + PVC + Service)

8.1 nginx 配置文件 ConfigMap

编写 nginx-configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-cm
  namespace: ecshop
data:
  nginx.conf: |
    server {
        listen 80;
        root /usr/share/nginx/html;
        index index.html index.htm index.php;

        location / {
            try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
            fastcgi_pass php-fpm-svc:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

8.2 nginx Deployment + Service

编写 nginx-deploy.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: hub.laoma.cloud/library/nginx:1.24
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: http
        volumeMounts:
        # 挂载商城源码共享 pvc(NFS 动态卷)
        - name: ecshop-code
          mountPath: /usr/share/nginx/html
        # 挂载 configmap nginx 配置
        - name: nginx-conf
          mountPath: /etc/nginx/conf.d
        # 健康探针,tcp 探测 80 端口
        livenessProbe:
          tcpSocket:
            port: 80
          initialDelaySeconds: 15
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          tcpSocket:
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
      volumes:
      - name: ecshop-code
        persistentVolumeClaim:
          claimName: ecshop-code-pvc
      - name: nginx-conf
        configMap:
          name: nginx-cm
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: ecshop
spec:
  selector:
    app: nginx
  type: ClusterIP        # 对外统一走 ingress,因此使用 ClusterIP
  ports:
  - port: 80
    targetPort: 80

应用并验证:

kubectl apply -f nginx-configmap.yaml
# configmap/nginx-cm created
kubectl apply -f nginx-deploy.yaml
# deployment.apps/nginx created
# service/nginx-svc created

kubectl get pods -n ecshop
# mysql-0                                   1/1 Running
# nfs-subdir-provisioner-...               1/1 Running
# nginx-...                                1/1 Running
# php-fpm-...                              1/1 Running

LNMP 联通性测试(在 nginx Pod 内写测试页):

kubectl exec -it nginx-<pod> -- bash
cat > /usr/share/nginx/html/index.html <<EOF
<h1>ECSHOP TEST PAGE</h1>
<p>NFS PVC working ok!</p>
EOF
cat > /usr/share/nginx/html/test.php <<EOF
<?php
phpinfo();
?>
EOF

kubectl get svc
# nginx-svc     ClusterIP  10.101.87.156  <none>  80/TCP
# php-fpm-svc   ClusterIP  10.111.180.43  <none>  9000/TCP

curl 10.101.87.156              # <h1>ECSHOP TEST PAGE</h1> NFS PVC working ok!
curl 10.101.87.156/test.php     # phpinfo 输出(PHP Version 7.2.34,mysqli/pdo_mysql 已启用)

说明:nginx-svc 最初用 NodePort(80:32020)测试,确认 Metallb/Ingress 方案后删除重建为 ClusterIP,统一由 ingress-nginx 对外暴露。


9. 部署网络组件(metallb + ingress-nginx + 自签名证书)

9.1 部署 metallb(题目中写作 metallab)

wget http://192.168.46.200/class/course-materials/softwares/stage03/metallb-0.14.8.tar.gz
tar -xf metallb-0.14.8.tar.gz
grep image metallb-0.14.8/config/manifests/metallb-native.yaml
# quay.io/metallb/controller:v0.14.8
# quay.io/metallb/speaker:v0.14.8
sed -i 's/quay.io/hub.laoma.cloud/g' metallb-0.14.8/config/manifests/metallb-native.yaml
kubectl apply -f metallb-0.14.8/config/manifests/metallb-native.yaml
kubectl get all -n metallb-system
# pod/controller-... 1/1 Running
# pod/speaker-xxx     1/1 Running(每节点一个)

配置 IP 地址池与 L2 通告:

cat << 'EOF' > ippool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 10.1.8.40-10.1.8.80
EOF
kubectl apply -f ippool.yaml

cat << 'EOF' > L2.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
EOF
kubectl apply -f L2.yaml

9.2 部署 ingress-nginx 控制器

wget http://192.168.46.200/class/course-materials/softwares/stage03/ingress-nginx-controller-v1.11.2.tar.gz
tar -xf ingress-nginx-controller-v1.11.2.tar.gz
grep image: ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml | uniq
# 将镜像替换为内网仓库
sed -ir 's#@sha256.*##;s/registry.k8s.io/hub.laoma.cloud/' ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml
kubectl apply -f ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml

将控制器 Service 改为 LoadBalancer,由 metallb 分配 EXTERNAL-IP:

kubectl edit svc ingress-nginx-controller -n ingress-nginx    # type: LoadBalancer
kubectl get svc ingress-nginx-controller -n ingress-nginx
# NAME                       TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)
# ingress-nginx-controller   LoadBalancer   10.97.52.107   10.1.8.40     80:32455/TCP,443:30093/TCP

9.3 为 ingress-nginx 控制器提供自签名证书和私钥

生成商城域名自签名证书(最终以 shop.ningcode.cn 为准):

openssl genrsa -out shop.key 2048
openssl req -new -key shop.key -out shop.csr -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=shop.ningcode.cn/emailAddress=webadmin@ningcode.cn"
openssl x509 -req -days 3650 -in shop.csr -signkey shop.key -out shop.crt

kubectl create secret tls www-tls -n ecshop --cert=shop.crt --key=shop.key
kubectl get secret www-tls -n ecshop
# www-tls   kubernetes.io/tls   2

说明:日志中先生成了 www.ningcode.cn 的证书(www.key/www.csr/www.crt)并创建 www-tls,因域名与公网冲突,后重新生成 shop.ningcode.cn 证书,删除并用新证书重建同名 secret www-tls(ingress 中引用的 secretName 不变)。


10. 配置 ingress 规则,通过 https://shop.ningcode.cn 访问商城

编写 tls-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.ningcode.cn
    secretName: www-tls
  rules:
  - host: shop.ningcode.cn
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-svc
            port:
              number: 80
kubectl apply -f tls-ingress.yaml
# ingress.networking.k8s.io/ecshop-ingress created

kubectl get ingress -n ecshop
# NAME             CLASS   HOSTS             ADDRESS    PORTS
# ecshop-ingress   nginx   shop.ningcode.cn  10.1.8.40  80, 443

本机解析测试(/etc/hosts):

vim /etc/hosts
# 10.1.8.30 master30.ningcode.cn master30
# 10.1.8.31 worker31.ningcode.cn worker31
# 10.1.8.32 worker32.ningcode.cn worker32
# 192.168.46.201 hub.laoma.cloud
# 10.1.8.40 shop.ningcode.cn

curl -I http://shop.ningcode.cn
# HTTP/1.1 308 Permanent Redirect
# Location: https://shop.ningcode.cn
curl -k https://shop.ningcode.cn        # 返回商城测试页

11. 部署 Metric Server,为 php 和 nginx 配置 HPA(最小2、最大5)

11.1 部署 metrics-server

wget -O components.yaml http://192.168.46.200/class/course-materials/softwares/stage03/metrics-server-components-v0.7.1.yaml
# 添加 --kubelet-insecure-tls(自签 kubelet 证书环境)
sed -i '/metric-resolution/a\        - --kubelet-insecure-tls' components.yaml
grep image: components.yaml
# hub.laoma.cloud/metrics-server/metrics-server:v0.7.1
sed -i 's/registry.k8s.io/hub.laoma.cloud/g' components.yaml
kubectl apply -f components.yaml

kubectl get pods -n kube-system | grep metrics
# metrics-server-...   1/1 Running

kubectl top pods
# NAME                                    CPU(cores)   MEMORY(bytes)
# mysql-0                                 2m           180Mi
# nfs-subdir-provisioner-...              1m           6Mi
# nginx-...                               1m           2Mi
# php-fpm-...                             1m           12Mi

11.2 配置 HPA

kubectl autoscale deployment nginx --max=5 --min=2
# horizontalpodautoscaler.autoscaling/nginx autoscaled

kubectl get hpa
kubectl get pods -n ecshop        # HPA 生效后 nginx 保持 2 个副本
# nginx-...-96ttd  1/1 Running
# nginx-...-mz597  1/1 Running

补充:题目要求 php 也配置 HPA(本日志中未执行),可同样执行
kubectl autoscale deployment php-fpm --max=5 --min=2


12. 部署 Kubernetes Dashboard 并配置 https://dashboard.ningcode.cn

12.1 部署 Dashboard

wget -O recommended.yaml http://192.168.46.200/class/course-materials/softwares/stage03/kubernetes-dashboard-recommended-v2.6.1.yaml
grep image: recommended.yaml
# hub.laoma.cloud/kubernetesui/dashboard:v2.6.1
# hub.laoma.cloud/kubernetesui/metrics-scraper:v1.0.8
kubectl apply -f recommended.yaml
# namespace/kubernetes-dashboard created
# deployment.apps/kubernetes-dashboard created
# deployment.apps/dashboard-metrics-scraper created

确认/调整 dashboard Service(ClusterIP:443 供 ingress 后端 HTTPS 使用):

kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard
kubectl get svc -n kubernetes-dashboard
# kubernetes-dashboard   ClusterIP   <cluster-ip>  <none>  443/TCP

12.2 Dashboard 自签名证书

openssl genrsa -out dashboard.key 2048
openssl req -new -key dashboard.key -out dashboard.csr -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=dashboard.ningcode.cn/emailAddress=webadmin@ningcode.cn"
openssl x509 -req -days 3650 -in dashboard.csr -signkey dashboard.key -out dashboard.crt
kubectl create secret tls dashboard-tls -n kubernetes-dashboard --cert=dashboard.crt --key=dashboard.key

12.3 Dashboard Ingress

编写 dashboard-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: dashboard-ingress
  namespace: kubernetes-dashboard
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - dashboard.ningcode.cn
    secretName: dashboard-tls
  rules:
  - host: dashboard.ningcode.cn
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kubernetes-dashboard
            port:
              number: 443
kubectl apply -f dashboard-ingress.yaml
# ingress.networking.k8s.io/dashboard-ingress created

vim /etc/hosts
# 10.1.8.41 dashboard.ningcode.cn

curl -k -I https://dashboard.ningcode.cn
# HTTP/2 200

12.4 登录账号(RBAC + token)

kubectl create clusterrolebinding kubernetes-dashboard-cluster-admin \
  --clusterrole cluster-admin \
  --serviceaccount kubernetes-dashboard:kubernetes-dashboard
# clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard-cluster-admin created

kubectl -n kubernetes-dashboard create token kubernetes-dashboard
# 输出 eyJhbGciOiJSUzI1NiIs... 的 token,粘贴到 Dashboard 登录页

在这里插入图片描述


补充步骤(多出来的必要关键步骤)

补充1:ECShop 源码上传并部署到共享卷

# 1) 上传源码包(rz 上传 ECShop_V4.1.20_UTF8.zip 到 /root)
rz -E

# 2) 解压
cd /root
mkdir ecshop
apt install -y unzip
unzip ECShop_V4.1.20_UTF8.zip -d /root/ecshop/

# 3) 拷贝源码到 nginx Pod 的 html 目录(末尾 . 代表拷贝文件夹内部所有文件)
kubectl get pod | grep nginx
kubectl cp /root/ecshop/. nginx-<pod>:/usr/share/nginx/html/ -n ecshop

# 4) 进入 Pod 整理目录、清理测试文件、设置权限
kubectl exec -it nginx-<pod> -n ecshop -- bash
cd /usr/share/nginx/html
mv ECShop_V4.1.20_UTF8_release20250416/* ./      # 或 mv source/* ./
rm -rf ECShop_V4.1.20_UTF8_release20250416 source readme
rm -f index.html test.php *.txt *.jpg
mv ecshop/* ./ && rm -rf ecshop phalapi           # 源码在 source/ecshop 子目录时
chmod -R 755 /usr/share/nginx/html

补充2:浏览器安装 ECShop(网页安装向导)

# 浏览器访问
https://shop.ningcode.cn
# 按安装向导填写数据库信息:
#   数据库主机: mysql(或 mysql:3306)
#   数据库端口: 3306
#   数据库名:   ecshop
#   用户名:     ecshop
#   密码:       ningcode@123
# 安装完成后删除 install 目录:
kubectl exec -it nginx-<pod> -n ecshop -- bash
rm -rf /usr/share/nginx/html/install

补充3:自构建 php:7.2-fpm-ecshop 镜像(题目要求“自己构建”)

本环境使用内网仓库已有镜像 hub.laoma.cloud/library/php:7.2-fpm-ecshop;如需自构建:

# Dockerfile
FROM php:7.2-fpm
RUN apt-get update && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev libzip-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) mysqli pdo_mysql gd zip mbstring \
    && docker-php-ext-enable mysqli pdo_mysql gd zip mbstring
docker build -t hub.laoma.cloud/library/php:7.2-fpm-ecshop .
docker login hub.laoma.cloud
docker push hub.laoma.cloud/library/php:7.2-fpm-ecshop
# 各节点(containerd)拉取:
# nerdctl -n k8s.io pull hub.laoma.cloud/library/php:7.2-fpm-ecshop

补充4:php-fpm 的 HPA

kubectl autoscale deployment php-fpm --max=5 --min=2
kubectl get hpa -n ecshop

在这里插入图片描述

补充5:域名与解析说明

  • 考试/本机访问前在 master 及测试机 /etc/hosts 写入:
    • 10.1.8.40 shop.ningcode.cn
    • 10.1.8.41 dashboard.ningcode.cn
    • 10.1.8.30 master30.ningcode.cn master30 等节点记录
    • 192.168.46.201 hub.laoma.cloud
  • 证书 CN、ingress host、secret 需与域名一致;更换域名时重新执行 openssl 生成并重建 tls secret。

补充6:排错记录(本次日志中出现的问题)

  1. Quota 命令 request.cpu 报错 → 改为 requests.cpu
  2. mysql 镜像直接写 mysql:5.7 拉取/启动异常 → 改用内网仓库 hub.laoma.cloud/library/mysql:5.7(各节点“从内网下镜像”)。
  3. php-fpm Pending:persistentvolumeclaim "ecshop-code-pvc" not found → 先创建共享 PVC 再 apply。
  4. mysql 数据目录异常导致 CrashLoopBackOff → kubectl delete pod/pvc --force 后清理 NFS 上的 archived-ecshop-* 残留目录(rm -rf /ecshop/archived-*),再重新 apply sts/secret。
  5. 域名与公网冲突(www.ningcode.cn 被占用)→ 改为 shop.ningcode.cn 并重建证书与 ingress。
  6. dashboard 域名无法解析 → 在 /etc/hosts 增加 10.1.8.41 dashboard.ningcode.cn

更多推荐