用kubernetes(k8s)搭建,基于Spring Cloud 微服务架构的开源商城系统(mall-swarm)

✍️Auth:运维笔记       Date:2025/06/23       Cat:Linux服务器       👁️:50 次浏览

一,项目简介

开源项目地址:https://github.com/macrozheng/mall-swarm

mall-swarm是一套微服务商城系统,采用了 Spring Cloud Alibaba、Spring Boot 3.2、Sa-Token、MyBatis、Elasticsearch、Docker、Kubernetes等核心技术,同时提供了基于Vue的管理后台方便快速搭建系统。mall-swarm在电商业务的基础集成了注册中心、配置中心、监控中心、网关等系统功能。

基于Spring Cloud Java微服务架构,提供多种方式搭建,这里为了学习 k8s和微服务架构,使用自定义k8s搭建。

下载代码仓:

git clone https://github.com/macrozheng/mall-swarm.git

1,基本架构要求:

数据库Nacos 分别部署为独立的集群(不在 Kubernetes 中)

客户端访问 Kubernetes 集群中的 Gateway,由 Gateway 路由到各个微服务

所有微服务都部署在 Kubernetes 集群内

2,简单示意图:

          ┌────────────┐
          │   客户端    │
          └────┬───────┘
               │ HTTP 请求
               ▼
        ┌──────────────┐
        │  K8s Gateway │
        └──────┬───────┘
                  │
    ┌──────────┼──────────┐
    ▼          ▼          ▼
  微服务A   微服务B   微服务C   ...... (都部署在 K8s 中)
    ▲          ▲          ▲
    └────┬─────┘───────── │
         │                │
         ▼                ▼
   ┌────────┐      ┌─────────────┐
   │Nacos集群│      │数据库集群(如MySQL)│
   └────────┘      └─────────────┘
   (独立部署)       (独立部署)

二,基础环境准备

本地测试机器条件有限,这里只搭建基础的admin 后台。

虚拟机1: 198.19.249.12 (安装 mysql , redis, nacos 即可) 。

虚拟机2: 172.16.81.128 (k8s-master)

虚拟机3: 172.16.81.129(k8s-node01)

Mysql, redis, nacos自行安装,apt和docker 都可以,版本不限。

条件有限,这里就不使用集群了,生产环境可根据官方文档安装集群,我简单安装的版本mysql8.0, redis7.2, nacos2.5.1。

Mysql 创建数据库mall, 并将仓库document/sql/mall.sql 导入初始化数据库即可。

Nacos 也使用上面的数据库配置即可,redis 安装好后,配置好密码和端口访问。

k8s集群自行搭建好即可。

1,nacos配置:

nacos这边是开启了鉴权,配置了账号密码都是 nacos / nacos 。

新建命令空间dev,搭建的服务使用dev环境。

配置管理,在新建的dev中,将config中,相应的配置导入或复制到nacos,格式为YAML, 如:config/admin/mall-admin-dev.yaml

根据搭建的mysql和redis 环境修改,如:config/admin/mall-admin-dev.yaml ,只配置基础的mysql和redis,其他的暂时用不到就不用管,需要功能完整的也可以自行搭建。

aliyun:
  oss:
    endpoint: oss-cn-shenzhen.aliyuncs.com
    accessKeyId: test
    accessKeySecret: test
    bucketName: macro-oss
    policy:
      expire: 300
    maxSize: 10
    callback: http://39.98.190.128:8080/aliyun/oss/callback
    dir:
      prefix: mall/images/ # 上传文件夹路径前缀
spring:
  datasource:
    url: jdbc:mysql://198.19.249.12:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
    username: root
    password: qwe123
  data:
    redis:
      host: 198.19.249.12
      database: 0
      port: 6379
      password: qwe123
      timeout: 3000ms
minio:
  endpoint: http://localhost:9000
  bucketName: mall
  accessKey: minioadmin
  secretKey: minioadmin
logging:
  level:
    root: info
    com.macro.mall: debug
logstash:
  host: localhost

mall-gateway-dev.yaml

spring:
  data:
    redis:
      host: 198.19.249.12
      database: 0 
      port: 6379
      password: qwe123
      timeout: 3000ms
logging:
  level:
    root: info
    com.macro.mall: debug
logstash:
  host: localhost

以上两个,后台使用的基础服务就可以用了。

2,代码修改

也只是修改配置nacos地址,如果不配置,也可以在后面k8s 启动文件加上参数覆盖配置。(可选修改)

dev只需要改这一个mall-swarm/mall-gateway/src/main/resources/bootstrap-dev.yml

admin也一样修改`mall-swarm/mall-admin/src/main/resources/bootstrap-dev.yml

namespace就是dev的id。

spring:
  application:
    name: your-service
  cloud:
    nacos:
      discovery:
        server-addr: http://198.19.249.12:8848
        namespace: 67997cc1-b195-4200-8ce6-9fa02b499ea5
        username: nacos
        password: nacos
      config:
        server-addr: http://198.19.249.12:8848
        namespace: 67997cc1-b195-4200-8ce6-9fa02b499ea5
        file-extension: yaml
        username: nacos
        password: nacos

根目录的`mall-swarm/pom.xml代码修改。

配置里面有个docker 地址,用来构建docker镜像的,改成本地就行,只要本地安装了docker就行。不改的话,构建的时候会找不到地址报错。

原来的:

<docker.host>http://192.168.3.101:2375</docker.host>

更改后:

<docker.host>unix:///var/run/docker.sock</docker.host>

3,构建包和镜像

在项目机器上,安装好maven 和jdk17 后(自行安装),在项目根目录,构建包。

这个项目,pom.xml 已经配置了 构建jar的时候,同时构建了docker 镜像,所以构建一次即可。

构建命令:

mvn clean package -Dmaven.test.skip=true

构建完成,相应的目录下已经有jar包,不需要k8s服务的话,可以直接拿这个jar包运行服务了。

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for mall-swarm 1.0-SNAPSHOT:
[INFO] 
[INFO] mall-swarm ......................................... SUCCESS [  0.130 s]
[INFO] mall-common ........................................ SUCCESS [  4.550 s]
[INFO] mall-mbg ........................................... SUCCESS [  7.518 s]
[INFO] mall-demo .......................................... SUCCESS [  2.892 s]
[INFO] mall-admin ......................................... SUCCESS [ 13.817 s]
[INFO] mall-search ........................................ SUCCESS [  5.206 s]
[INFO] mall-portal ........................................ SUCCESS [ 10.181 s]
[INFO] mall-monitor ....................................... SUCCESS [  7.148 s]
[INFO] mall-gateway ....................................... SUCCESS [  7.411 s]
[INFO] mall-auth .......................................... SUCCESS [  4.291 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:03 min
[INFO] Finished at: 2025-06-21T14:11:47+07:00
[INFO] ------------------------------------------------------------------------
root@ubuntu:/opt/mall-swarm# ls mall-gateway/target/
classes  docker  generated-sources  mall-gateway-1.0-SNAPSHOT.jar  mall-gateway-1.0-SNAPSHOT.jar.original  maven-archiver  maven-status

这里采用k8s,主要需要的是docker镜像,docker镜像也已经构建好了。

root@ubuntu:/opt/mall-swarm# docker images 
REPOSITORY                 TAG            IMAGE ID       CREATED         SIZE
mall/mall-auth             1.0-SNAPSHOT   7a36051160a5   3 minutes ago   567MB
mall/mall-gateway          1.0-SNAPSHOT   8adeabef82e7   3 minutes ago   575MB
mall/mall-monitor          1.0-SNAPSHOT   f2a7d9eb6abe   3 minutes ago   558MB
mall/mall-portal           1.0-SNAPSHOT   c30a4d4def96   3 minutes ago   628MB
mall/mall-search           1.0-SNAPSHOT   777a012b9172   4 minutes ago   591MB
mall/mall-admin            1.0-SNAPSHOT   468fb68aa46b   4 minutes ago   606MB

4,镜像上传至镜像仓库

可以上传到官方docker仓库,也可以上传到自己搭建的私仓(harbor)。

这里我用虚拟机搭建了一个harbor仓,按要求修改tag后,上传即可。

docker tag mall/mall-gateway:1.0-SNAPSHOT 198.19.249.70/mall-swarm/mall-gateway:1.0-SNAPSHOT

由于私仓,还没有配置 https,需要将私仓harbor的IP加入信任列表才能上传。

新建/etc/docker/daemon.json文件加入即可,如下:

root@master:~# cat /etc/docker/daemon.json 
{
  "insecure-registries": ["198.19.249.70"]
}

然后重启docker服务就可以push到harbor仓库了。

登录harbor:

docker login 198.19.249.70

push上传镜像:

docker push 198.19.249.70/mall-swarm/mall-gateway:1.0-SNAPSHOT 

镜像这时都准备好了,最后就是k8s启动了。

三,k8s配置后端服务

在k8s主节点配置,大概目录结构

root@master:/opt/mall-swarm/services# 
.
├── mall-admin
│   ├── admin-pvc.yaml
│   ├── admin-pv.yaml
│   └── mall-admin-deployment.yaml
├── mall-auth
├── mall-gateway
│   ├── gateway-pvc.yaml
│   ├── gateway-pv.yaml
│   ├── mall-gateway-deployment.yaml
│   └── mall-gateway-ingress.yaml
├── mall-monitor
├── mall-portal
├── mall-search
└── mall-namespace.yml

只需要gateway通过ingress暴露一个端口域名,用来外部访问外,其他的服务都不需要暴露端口。

为了隔离,先创建个专属命名空间mall-namespace.yml

apiVersion: v1
kind: Namespace
metadata:
  name: mall-cluster

启动服务:

kubectl apply -f mall-namespace.yml

1,pvc nfs 持久化配置

通过nfs服务器,创建PVC 持久话配置,用来存储日志。nfs服务器已安装在172.16.81.129

在 NFS 服务器上(172.16.81.129):

#这里先创建两个服务测试,如果测试全部服务都可以加上去创建。
mkdir -p /opt/k8s-volumes/{mall-admin-logs,mall-gateway-logs}
chmod 777 /opt/k8s-volumes

确保这个路径已经在 /etc/exports 中:

/opt/k8s-volumes *(rw,sync,no_subtree_check,no_root_squash)

然后重新加载 NFS 配置:

exportfs -ra

这里通过静态创建PV,不使用nfs.csi.k8s.io 驱动插件动态创建,因为动态创建的文件夹名称都是随机不友好,如pvc-171ce929-0520-4f04-a87f-cc0081aa3df0

关于动态和静态创建对比:

场景是否需要 CSI是否写 PV是否控制目录名
想要自动分配子目录(默认)✅ 是❌ 否❌ 否
想要固定 NFS 目录挂载❌ 否✅ 是✅ 是

这里使用静态创建,方便后面日志收集等软件操作,每台节点都需要安装nfs软件

apt install nfs-common -y
#创建PV
vim gateway-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mall-gateway-logs-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs-storage
  nfs:
    server: 172.16.81.129
    path: /opt/k8s-volumes/mall-gateway-logs
#创建PVC
vim gateway-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mall-gateway-logs-pvc
  namespace: mall-cluster
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: nfs-storage
  volumeName: mall-gateway-logs-pv   # <-- 关键,绑定上面的PV
  resources:
    requests:
      storage: 10Gi

启动pv和pvc服务:

kubectl apply -f gateway-pv.yaml
kubectl apply -f gateway-pvc.yaml

2,Deployment 和 Service

生产情况建议分开部署,这里测试,就写在同一个文件。

vim mall-gateway-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mall-gateway
  namespace: mall-cluster
  labels:
    app: mall-gateway
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mall-gateway
  template:
    metadata:
      labels:
        app: mall-gateway
    spec:
      #nodeSelector:
      #  kubernetes.io/hostname: master
      containers:
        - name: mall-gateway
          image: 198.19.249.70/mall-swarm/mall-gateway@sha256:08bdf39064dc1935710bbb2b2b99dcb4ab6b467e0170f22dea1154f3da06ea37
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8201
          # 使用 shell 来执行带时间戳的命令
          command: ["/bin/sh"]
          args:
            - "-c"
            - |
              TIMESTAMP=$(date +%Y%m%d%H%M%S)
              # 确保日志目录存在
              mkdir -p /var/logs/gc /var/logs/heapdump /var/logs/app
              exec java \
                -Xmx512m \
                -Xms512m \
                -Xmn256m \
                -XX:MetaspaceSize=64M \
                -XX:MaxMetaspaceSize=128M \
                -XX:MaxDirectMemorySize=256m \
                -Dfile.encoding=UTF-8 \
                -Duser.timezone=Asia/Shanghai \
                -Xlog:gc*:file=/var/logs/gc/gc-${TIMESTAMP}.log:tags,uptime,time,level \
                -XX:+HeapDumpOnOutOfMemoryError \
                -XX:HeapDumpPath=/var/logs/heapdump/errorDump-${TIMESTAMP}.hprof \
                -jar mall-gateway-1.0-SNAPSHOT.jar \
                --spring.profiles.active=dev \
​
          env:
            - name: TZ
              value: Asia/Shanghai
            - name: SPRING_CLOUD_NACOS_DISCOVERY_SERVER-ADDR
              value: "198.19.249.12:8848"
            - name: SPRING_CLOUD_NACOS_CONFIG_SERVER-ADDR
              value: "198.19.249.12:8848"
            - name: SPRING_CLOUD_NACOS_USERNAME
              value: "nacos"
            - name: SPRING_CLOUD_NACOS_PASSWORD
              value: "nacos"
            - name: LOG_FILE
              value: /var/logs/app
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "1024Mi"
              cpu: "500m"
          volumeMounts:
            - name: logs
              mountPath: /var/logs
​
          livenessProbe:
            httpGet:
            #检查路径和端口
              path: /actuator/health/liveness
              port: 8201
            #延迟90秒检查
            initialDelaySeconds: 90
            #30秒检查一次
            periodSeconds: 30
            #超时时间(防止误判)
            timeoutSeconds: 5
            #失败阈值次数(防止误判)
            failureThreshold: 3
​
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8201
            initialDelaySeconds: 60
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 5
​
      volumes:
        - name: logs
          persistentVolumeClaim:
            claimName: mall-gateway-logs-pvc
​
---
apiVersion: v1
kind: Service
metadata:
  name: mall-gateway-service
  namespace: mall-cluster
  labels:
    app: mall-gateway
spec:
  selector:
    app: mall-gateway
  ports:
    - port: 8201
      targetPort: 8201
  type: ClusterIP

启动服务:

kubectl apply -f mall-gateway-deployment.yaml

Deployment主要功能配置有:

1:配置harbor镜像:

同样,因为没有https,harbor服务器配置加入信任,然后重启docker服务。

cat /etc/docker/daemon.json 
{
  "insecure-registries": ["172.16.81.128","172.16.81.129"]
}

k8s目前使用的是containerd镜像,所以配置containerd加入harhor仓库,加入信任,然后重启containerd服务。

在k8s所有节点都需要加入,在配置文件合适位置加入:/etc/containerd/config.toml

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."198.19.249.70"]
    endpoint = ["http://198.19.249.70"]

2:启动参数配置:

自定义启动参数,内存日志啥的自定义即可(虚拟机条件有限,所以配置比较低,如果太低的话服务也容易起不来)。

--spring.profiles.active=dev必须配置,表明用来读取dev环境配置。

env环境变量,如果代码没修改nacos或者修改后不生效,在env重定义nacos相关参数即可,还有日志保存位置,挂载目录。

加入了 健康检查策略,根据返回值确定服务是否健康,2xx/3xx 都会健康。

类型检查目标失败后行为延迟建议应用场景
livenessProbe是否需要重启重启容器建议 ≥ 启动时间进程卡死检测
readinessProbe是否可以接收请求移除服务流量建议 < liveness服务冷启动/依赖未就绪

3: volumes

指定挂载前面到前面创建的PVC服务

Service 服务:

生产环境,建议和deployment文件分开配置。

type: ClusterIP主要用来暴露集群内部端口服务,用于集群内部服务互相访问即可。

3,ingress配置

Ingress 用来向外暴露gateway的域名和端口,只能域名访问,作为所以内部服务的唯一入口。

vim mall-gateway-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gateway-ingress
  namespace: mall-cluster
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
  ingressClassName: nginx
  rules:
    - host: gateway.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: mall-gateway-service
                port:
                  number: 8201

启动服务

kubectl apply -f mall-gateway-ingress.yaml 

这里配置了本地域名gateway.local测试作为访问入口,查看ingress服务和ingress端口。

root@master:/opt/mall-swarm/services/mall-gateway# kubectl get ingress -n mall-cluster 
NAME              CLASS   HOSTS           ADDRESS         PORTS   AGE
gateway-ingress   nginx   gateway.local   172.16.81.128   80      47h
root@master:/opt/mall-swarm/services/mall-gateway# kubectl get svc -n ingress-nginx 
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.100.30.232   <none>        80:32427/TCP,443:31870/TCP   64d
ingress-nginx-controller-admission   ClusterIP   10.98.230.199   <none>        443/TCP                      64d

从上面可以通过域名gateway.local,配置反向代理至集群任意node端口32427, 即可访问。

4,服务检查

其他服务也一样,admin根据上面的gateway一样配置,更改名称,镜像,启动参数即可。

服务启动完成后,可以查看所有服务 pods,pv,pvc,svc,ingress是否正常。

root@master:/opt/mall-swarm/services# kubectl get pods,pv,pvc,svc,ingress -n mall-cluster 
NAME                                READY   STATUS    RESTARTS   AGE
pod/mall-admin-547ff64f79-dcx6v     1/1     Running   0          27h
pod/mall-admin-547ff64f79-xrl7j     1/1     Running   0          27h
pod/mall-gateway-5f8d7f5cdb-j759g   1/1     Running   0          66m
pod/mall-gateway-5f8d7f5cdb-mgsms   1/1     Running   0          67m
​
NAME                                    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/mall-admin-logs-pv     10Gi       RWX            Retain           Bound    mall-cluster/mall-admin-logs-pvc     nfs-storage    <unset>                          40h
persistentvolume/mall-gateway-logs-pv   10Gi       RWX            Retain           Bound    mall-cluster/mall-gateway-logs-pvc   nfs-storage    <unset>                          40h
​
NAME                                          STATUS   VOLUME                 CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/mall-admin-logs-pvc     Bound    mall-admin-logs-pv     10Gi       RWX            nfs-storage    <unset>                 2d1h
persistentvolumeclaim/mall-gateway-logs-pvc   Bound    mall-gateway-logs-pv   10Gi       RWX            nfs-storage    <unset>                 40h
​
NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/mall-admin-service     ClusterIP   10.98.9.107     <none>        8080/TCP   47h
service/mall-gateway-service   ClusterIP   10.109.122.16   <none>        8201/TCP   39h
​
NAME                                        CLASS   HOSTS           ADDRESS         PORTS   AGE
ingress.networking.k8s.io/gateway-ingress   nginx   gateway.local   172.16.81.128   80      47h

登录nacos,在服务列表,可以看到启动的服务是否注册成功。

NFS PVC日志保存持久化,在 nfs服务,可以看到挂载的日志也正常收集运行,可以直接查看日志或采集日志。

ubuntu@node01:~$ ls /opt/k8s-volumes/mall-gateway-logs/app/debug/
mall-gateway-2025-06-22-0.log  mall-gateway-2025-06-22-1.log  mall-gateway-2025-06-22-2.log

四,前端测试

1,nginx配置

mall-admin-web地址:https://github.com/macrozheng/mall-admin-web

这里测试不用下载仓库,直接下载打包构建好的静态文件即可:https://github.com/macrozheng/mall-admin-web/releases/tag/v1.0.0

下载后解压到一台服务器,位置/app/admin,安装配置nginx:

admin.local 访问前端,这项目前端通过 mall-admin 访问网关地址的,所以配置proxy_pass到网关。

gateway.local 作为后端入口,根据上面k8s ingress 到配置,反向到任意节点的32427端口即可。

server {
    listen 80;
    server_name admin.local;
​
    location / {
        #try_files $uri $uri/ /index.html;
        root  /app/admin;
        index  index.html;
    }
​
    location ^~/mall-admin/ {
        proxy_pass http://gateway.local/;
​
        proxy_set_header Host $host:$server_port;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
​
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root   html;
    }
    error_page 404 /404.html;
    location = /404.html{
        root html;
    }
}
​
​
server {
    listen 80;
    server_name gateway.local
            ;
​
    location / {
        #try_files $uri $uri/ /index.html;
        #index  index.html;
​
        proxy_pass http://172.16.81.128:32427/;
​
        #proxy_set_header Host $host:$server_port;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

配置好前端nginx后,在测试访问的本地机器,配置hosts文件,指向上面这台nginx服务器,模拟域名访问。

198.19.249.107   gateway.local
198.19.249.107   admin.local

2,前端代码更改

由于目前构建好的包,网关地址配置的是 localhost:8201/mall-admin,所以修改为自己配置的网关gateway.local

不想从源代码重新构建的话,直接在静态文件查找修改静态文件即可。

root@ubuntu:/app/admin# grep -rn 'localhost:8201' ./
./static/js/app.8f2a0576109e7db3e15f.js:1:webpackJsonp([49]...

查找的文件位置在./static/js/app.8f2a0576109e7db3e15f.js编辑修改保存

#修改前
d=s.a.create({baseURL:"http://localhost:8201/mall-admin"
#修改后
d=s.a.create({baseURL:"http://gateway.local/mall-admin"

3,访问测试

浏览器访问:http://admin.local/

输入默认密码: admin / macro123 即可登录成功。

这里admin就已经搭建完成,其他服务差不多一样,这里虚拟机配置有限,就不演示了。

打赏作者

发表评论