目录
一,项目简介
开源项目地址: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就已经搭建完成,其他服务差不多一样,这里虚拟机配置有限,就不演示了。
打赏作者