Docker Compose 如何一键部署前后端 + PostgreSQL + Redis?从 ShiyuAdmin 拆解完整部署方案

对于一个完整的后台管理系统来说,真正麻烦的往往不是“代码写完”,而是:
怎么把它跑起来?
比如一个典型的前后端分离项目,至少可能包含:
React 前端
Go 后端
PostgreSQL
Redis
如果全部手动部署,你可能要分别处理:
Node.js
npm
Go
Nginx
PostgreSQL
Redis
环境变量
数据库连接
端口
进程守护
服务启动顺序
日志
数据持久化
对于开发者来说还能接受。
但是如果这是一个开源项目,用户下载以后还需要看几十分钟文档才能启动,使用门槛就会非常高。
所以我在 ShiyuAdmin 里专门做了一套 Docker Compose 部署方案。
项目地址:
https://github.com/Rodert/ShiyuAdmin
ShiyuAdmin 是一个使用:
Go
Gin
GORM
React
Umi Max
Ant Design Pro
PostgreSQL
Redis
Docker Compose
搭建的通用后台管理系统。
现在本地启动完整系统,核心就是一条命令:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up -d --build
执行完成之后:
PostgreSQL
Redis
Go Backend
React Frontend
Nginx
全部运行起来。
这篇文章就从 ShiyuAdmin 的实际部署方式出发,完整讲一下:
一个前后端分离项目,如何使用 Docker Compose 做成真正的一键部署。
一、先看 ShiyuAdmin 的整体部署架构
如果完全按照“一个程序一个容器”的思路,我们可能会设计成:
Browser
│
▼
┌─────────────┐
│ React │
│ Container │
└──────┬──────┘
│
▼
┌─────────────┐
│ Go Backend │
│ Container │
└──────┬──────┘
│
├──────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ PostgreSQL │ │ Redis │
└─────────────┘ └─────────────┘
这样当然没问题。
但是 ShiyuAdmin 当前采用了另一种思路:
Browser
│
│ 18000
▼
┌──────────────────────────────┐
│ shiyu-app │
│ │
│ ┌────────────────────────┐ │
│ │ Nginx │ │
│ │ React 静态资源 │ │
│ │ API Reverse Proxy │ │
│ └──────────┬─────────────┘ │
│ │ /api │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Go Backend :8080 │ │
│ └────────────────────────┘ │
│ │
│ Supervisor 管理进程 │
└─────────────┬────────────────┘
│
Docker Network
┌────┴─────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ PostgreSQL │ │ Redis │
│ :5432 │ │ :6379 │
└─────────────┘ └─────────────┘
也就是说:
前端 + 后端
↓
一个应用镜像
PostgreSQL
↓
一个容器
Redis
↓
一个容器
最终只有三个核心 Service:
shiyu-app
shiyu-postgres
shiyu-redis
这种结构特别适合:
后台管理系统
中小型 SaaS
个人开源项目
内部管理系统
单机 Docker 部署
因为部署非常简单。
二、ShiyuAdmin 的项目结构
整个仓库核心结构可以简化为:
ShiyuAdmin
├── backend/
│ └── shiyu-admin-backend/
│ ├── cmd/
│ │ └── server/
│ ├── configs/
│ │ ├── config.yaml
│ │ └── config.docker.yaml
│ ├── internal/
│ ├── go.mod
│ └── go.sum
│
├── frontend/
│ └── shiyu-admin-web/
│ ├── src/
│ ├── package.json
│ └── ...
│
├── deploy/
│ ├── nginx.conf
│ └── supervisord.conf
│
├── Dockerfile
├── docker-compose.yml
├── docker-compose.local.yml
└── docker-compose.README.md
这里面几个文件分别负责:
| 文件 | 作用 |
|---|---|
Dockerfile |
构建前端、后端和最终运行镜像 |
docker-compose.yml |
定义 PostgreSQL、Redis、应用服务 |
docker-compose.local.yml |
本地从源码构建 ShiyuAdmin |
config.docker.yaml |
Docker 环境下的后端配置 |
nginx.conf |
托管前端并代理 /api |
supervisord.conf |
同时管理 Nginx 和 Go 后端 |
看似文件不少,但真正执行的时候用户只需要:
docker compose up
这才是一键部署真正应该解决的问题。
三、Docker Compose 到底解决了什么?
如果不用 Docker Compose,我们首先启动 PostgreSQL:
docker run -d \
--name shiyu-postgres \
-e POSTGRES_DB=shiyu_admin_scaffold \
-e POSTGRES_USER=shiyu \
-e POSTGRES_PASSWORD=shiyu123 \
-p 15432:5432 \
postgres:15
然后 Redis:
docker run -d \
--name shiyu-redis \
-p 16379:6379 \
redis:7
然后构建 ShiyuAdmin:
docker build \
-t shiyu-admin:latest \
.
然后启动:
docker run -d \
--name shiyu-app \
-p 18000:80 \
shiyu-admin:latest
接下来还有一个问题:
这些容器怎么互相找到?
还得创建 Network:
docker network create shiyu-network
再指定:
--network shiyu-network
数据库数据还不能随着容器删除消失,所以又需要 Volume:
docker volume create shiyu-pg-data
docker volume create shiyu-redis-data
你会发现,最终命令越来越多。
Docker Compose 实际上就是把这些东西声明到:
docker-compose.yml
里面。
然后:
docker compose up -d
一次启动。
所以 Compose 的核心思想其实非常简单:
把一堆 Docker 命令,变成一份可版本管理、可重复执行的基础设施配置。
四、先从 PostgreSQL 开始
ShiyuAdmin 默认的 Compose 部署使用 PostgreSQL。
我们可以把 PostgreSQL Service 简化理解为:
services:
shiyu-postgres:
image: postgres:15
container_name: shiyu-postgres
restart: unless-stopped
environment:
POSTGRES_DB: shiyu_admin_scaffold
POSTGRES_USER: shiyu
POSTGRES_PASSWORD: shiyu123
ports:
- "15432:5432"
volumes:
- shiyu-pg-data:/var/lib/postgresql/data
networks:
- shiyu-network
逐个看。
五、image 是什么意思?
image: postgres:15
表示使用:
PostgreSQL 15
官方 Docker 镜像。
如果本地没有:
postgres:15
Docker 会自动拉取。
相当于:
docker pull postgres:15
这就是 Docker 最大的优势之一。
用户完全不需要自己安装 PostgreSQL。
不需要:
brew install postgresql
也不需要:
apt install postgresql
更不用考虑:
你的 PostgreSQL 是 14
我的 PostgreSQL 是 15
他的 PostgreSQL 是 16
统一:
postgres:15
六、environment 初始化数据库
这里:
environment:
POSTGRES_DB: shiyu_admin_scaffold
POSTGRES_USER: shiyu
POSTGRES_PASSWORD: shiyu123
PostgreSQL 容器第一次启动的时候会创建:
数据库:
shiyu_admin_scaffold
用户名:
shiyu
密码:
shiyu123
因此 Go 后端连接数据库时也需要使用相同的信息。
例如:
database:
driver: postgres
host: shiyu-postgres
port: 5432
username: shiyu
password: shiyu123
database: shiyu_admin_scaffold
注意这里最重要的是:
host: shiyu-postgres
而不是:
host: localhost
为什么?
因为 Go 后端本身也运行在 Docker 容器中。
七、Docker 里面的 localhost 是谁?
这是 Docker 新手最容易犯的错误之一。
假设:
shiyu-app
容器里面配置:
database:
host: localhost
那么:
localhost
代表的是:
shiyu-app 自己
不是 PostgreSQL。
因为每个容器都有自己的网络环境。
可以理解为:
Container A localhost
≠
Container B localhost
那么 ShiyuAdmin 后端怎么找到 PostgreSQL?
直接使用 Compose Service Name:
shiyu-postgres
所以:
database:
host: shiyu-postgres
port: 5432
Docker DNS 会自动完成:
shiyu-postgres
↓
PostgreSQL Container IP
Redis 同理:
redis:
host: shiyu-redis
port: 6379
这其实是 Docker Compose 非常重要的一项能力:
Service Name 本身就是内部 DNS 名称。
八、端口映射为什么是 15432:5432?
ShiyuAdmin 的 PostgreSQL 使用类似:
ports:
- "15432:5432"
Docker 的端口规则是:
宿主机端口 : 容器端口
所以:
15432
↓
Host
5432
↓
PostgreSQL Container
如果你在 Mac 上使用数据库客户端:
Host:
localhost
Port:
15432
但是 ShiyuAdmin 容器访问数据库的时候:
Host:
shiyu-postgres
Port:
5432
这两个一定不要混。
可以记成:
宿主机访问容器
↓
localhost:15432
容器访问容器
↓
shiyu-postgres:5432
九、为什么一定需要 Volume?
如果只写:
shiyu-postgres:
image: postgres:15
数据库数据默认会保存在容器内部。
假设删除容器:
docker compose down
再重新创建。
就可能涉及数据库数据生命周期的问题。
因此 ShiyuAdmin 使用:
volumes:
- shiyu-pg-data:/var/lib/postgresql/data
然后在 Compose 最下面定义:
volumes:
shiyu-pg-data:
driver: local
现在数据路径变成:
PostgreSQL
│
▼
/var/lib/postgresql/data
│
▼
Docker Volume
│
▼
shiyu-pg-data
于是:
docker compose down
删除容器以后,数据卷还在。
重新:
docker compose up -d
数据库数据还能回来。
十、什么时候数据库会真的被删?
如果执行:
docker compose down
默认不会删除 Volume。
但是如果执行:
docker compose down -v
这里的:
-v
代表:
同时删除 Volume
这意味着 ShiyuAdmin 的 PostgreSQL 数据也会被清理。
所以:
docker compose down -v
一定要谨慎。
特别是在生产环境。
十一、PostgreSQL Health Check
光启动 PostgreSQL 容器还不够。
例如:
10:00:00
PostgreSQL Container 启动
10:00:01
Go Backend 启动
10:00:02
Go Backend 连接 PostgreSQL
10:00:03
PostgreSQL 还没初始化完成
10:00:03
连接失败
所以 ShiyuAdmin 给 PostgreSQL 配置了健康检查。
大体类似:
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U shiyu -d shiyu_admin_scaffold"
]
interval: 10s
timeout: 5s
retries: 5
其中:
pg_isready
是 PostgreSQL 自带的检查工具。
它会判断:
数据库是否真的已经可以接受连接。
而不是简单判断:
Container 是否启动。
这两个概念完全不同。
十二、Container Running 不等于 Service Ready
Docker 显示:
Up
只能说明:
进程已经启动。
但是服务可能还在初始化。
例如:
PostgreSQL:
正在初始化数据库
Redis:
正在加载 AOF
Go:
正在连接数据库并初始化
Nginx:
配置文件还没加载完成
所以一个比较完整的 Compose 系统最好都加:
healthcheck
ShiyuAdmin 目前 PostgreSQL、Redis、应用容器都有自己的健康检查。
这其实就是一个成熟 Docker Compose 项目和普通 Demo 的区别之一。
十三、再来看 Redis
Redis 的配置可以简化为:
services:
shiyu-redis:
image: redis:7
container_name: shiyu-redis
restart: unless-stopped
ports:
- "16379:6379"
volumes:
- shiyu-redis-data:/data
command:
redis-server --appendonly yes
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 10s
timeout: 5s
retries: 5
networks:
- shiyu-network
Redis 默认端口:
6379
宿主机映射:
16379
所以本机可以:
redis-cli \
-h 127.0.0.1 \
-p 16379
然后:
PING
正常应该:
PONG
十四、Redis 为什么开启 AOF?
ShiyuAdmin 使用:
redis-server --appendonly yes
也就是开启:
AOF
Append Only File
简单理解:
Redis 每执行一个写操作,就可以把操作记录下来。
例如:
SET token abc
HSET user:1 name javapub
LPUSH queue task1
这样 Redis 重启以后,可以根据 AOF 文件恢复数据。
同时 Compose 还挂载:
volumes:
- shiyu-redis-data:/data
所以:
Redis
↓
/data
↓
shiyu-redis-data
Redis 数据也不会随着容器重新创建直接消失。
十五、Redis Health Check
Redis 健康检查更简单:
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 10s
timeout: 5s
retries: 5
实际上执行的就是:
redis-cli ping
正常返回:
PONG
然后 Docker 就会把它标记为:
healthy
十六、最重要的 shiyu-app
接下来才是整个 ShiyuAdmin 部署最核心的部分:
shiyu-app
可以简化成:
services:
shiyu-app:
image:
${SHIYU_IMAGE:-ghcr.io/rodert/shiyuadmin:latest}
container_name:
shiyu-app
restart:
unless-stopped
ports:
- "18000:80"
environment:
- TZ=Asia/Shanghai
- CONFIG_FILE=configs/config.docker.yaml
depends_on:
shiyu-postgres:
condition: service_healthy
shiyu-redis:
condition: service_healthy
networks:
- shiyu-network
注意:
ShiyuAdmin 当前并没有:
shiyu-frontend
shiyu-backend
两个正式运行容器。
而是:
shiyu-app
一个容器同时包含:
React Build
Go Server
Nginx
Supervisor
这部分是整个方案最值得拆解的地方。
十七、为什么把前后端打进一个镜像?
传统前后端分离:
frontend container
backend container
没有错。
但是对于 ShiyuAdmin 这种项目:
后台管理系统
单体后端
SPA 前端
单机部署
其实可以进一步简化。
React 最终构建以后,并不是一个需要 Node.js 长期运行的服务。
React:
npm run build
最终得到:
dist/
里面主要就是:
index.html
*.js
*.css
images
fonts
这些都是静态文件。
因此完全可以:
React
↓
Build
↓
dist
↓
Nginx
后端:
Go
↓
Build
↓
server 二进制文件
最终:
Nginx
+
server
一起放进一个镜像。
这样最终部署只需要:
一个应用容器。
十八、ShiyuAdmin Dockerfile 的核心:多阶段构建
ShiyuAdmin 当前使用的是典型的:
Multi-stage Build
可以抽象成三个阶段:
# ===============================
# Stage 1:构建 React
# ===============================
FROM node:20-alpine AS frontend-builder
WORKDIR /app
COPY frontend/shiyu-admin-web/package.json ./
RUN npm install \
--legacy-peer-deps \
--no-audit
COPY frontend/shiyu-admin-web/ ./
RUN npm run build
这一阶段只负责:
React Source
↓
npm install
↓
npm run build
↓
dist
运行完以后,我们真正需要的其实只是:
/app/dist
Node.js 本身在生产镜像里并不需要。
十九、第二阶段:构建 Go 后端
接下来:
FROM golang:1.23-alpine AS backend-builder
WORKDIR /app
ENV GOPROXY= https://proxy.golang.org ,direct
RUN apk add \
--no-cache \
gcc \
musl-dev \
sqlite-dev
COPY \
backend/shiyu-admin-backend/go.mod \
backend/shiyu-admin-backend/go.sum \
./
RUN go mod download
COPY \
backend/shiyu-admin-backend/ \
./
RUN CGO_ENABLED=1 \
GOOS=linux \
go build \
-trimpath \
-ldflags="-s -w" \
-o /server \
./cmd/server
这一层做:
Go Source
↓
go mod download
↓
go build
↓
/server
最后真正需要的是:
/server
而不是:
Golang 编译器
Go Module Cache
Source Code
gcc
二十、第三阶段才是真正的生产镜像
最后:
FROM nginx:alpine
生产镜像以:
nginx:alpine
为基础。
再安装:
RUN apk add \
--no-cache \
ca-certificates \
tzdata \
supervisor \
sqlite-libs
然后复制 React:
COPY \
--from=frontend-builder \
/app/dist \
/usr/share/nginx/html
复制 Go:
COPY \
--from=backend-builder \
/server \
/app/server
复制配置:
COPY \
--from=backend-builder \
/app/configs \
/app/configs
再复制 Nginx:
COPY \
deploy/nginx.conf \
/etc/nginx/conf.d/default.conf
以及 Supervisor:
COPY \
deploy/supervisord.conf \
/etc/supervisord.conf
最后:
WORKDIR /app
EXPOSE 80
CMD [
"/usr/bin/supervisord",
"-c",
"/etc/supervisord.conf"
]
整个镜像最终变成:
nginx:alpine
│
├── React dist
│
├── Go server
│
├── Nginx config
│
├── Supervisor
└── backend configs
二十一、为什么不把 Node 和 Go 都留在最终镜像?
假设不用多阶段构建。
可能需要:
Node.js
npm
Go SDK
gcc
源代码
node_modules
Go Module Cache
Nginx
全部塞进最终镜像。
结果就是:
镜像更大
攻击面更大
依赖更多
部署更慢
安全性更差
多阶段构建之后:
Node
只负责 Build
Go
只负责 Build
Nginx
负责 Runtime
最终镜像不需要:
Node.js
npm
Go Compiler
这才是多阶段 Docker Build 真正解决的问题。
二十二、Nginx 同时解决前端和 API
ShiyuAdmin 生产容器里 Nginx 监听:
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
}
所以访问:
http://localhost:18000
实际上:
18000
↓
Docker Port Mapping
↓
Container :80
↓
Nginx
↓
React
二十三、React Router 刷新 404 怎么解决?
SPA 前端有一个经典问题。
例如访问:
/system/user
前端路由可以正常进入。
但是刷新页面以后 Nginx 会去磁盘找:
/system/user
文件。
显然不存在。
于是:
404
因此 ShiyuAdmin 的 Nginx 使用类似:
location / {
try_files \
$uri \
/index.html;
}
意思就是:
文件存在
↓
直接返回
不存在
↓
回到 index.html
再由 React Router 接管
这是部署 React/Vue SPA 时非常常见的配置。
二十四、API 为什么不需要跨域?
这是这个架构另一个很实用的地方。
ShiyuAdmin 的 Nginx 会把:
/api
代理给:
127.0.0.1:8080
大体类似:
location /api {
proxy_pass \
http://127.0.0.1:8080;
proxy_set_header \
Host \
$host;
proxy_set_header \
X-Real-IP \
$remote_addr;
proxy_set_header \
X-Forwarded-For \
$proxy_add_x_forwarded_for;
proxy_set_header \
X-Forwarded-Proto \
$scheme;
}
因此浏览器请求:
http://localhost:18000/api/v1/system/users
实际链路:
Browser
↓
Nginx :80
↓
/api
↓
127.0.0.1:8080
↓
Go Gin
前端页面:
http://localhost:18000
API:
http://localhost:18000/api
所以浏览器看到的是:
同域
同协议
同端口
这就大大减少了:
CORS
跨域
前端 API Host 配置
生产环境域名切换
这些问题。
二十五、为什么 Nginx 能访问 127.0.0.1:8080?
前面我说:
容器 A 的 localhost
≠
容器 B 的 localhost
但是这里:
proxy_pass http://127.0.0.1:8080;
却是正确的。
因为:
Nginx
和
Go Backend
就在:
同一个 shiyu-app 容器。
所以:
Nginx localhost
=
Go Backend localhost
架构就是:
shiyu-app
│
├── nginx :80
│
└── go :8080
所以:
127.0.0.1:8080
完全合理。
二十六、一个容器两个进程怎么管理?
通常 Docker 推荐:
一个容器一个主要进程
但是实际工程里并不是绝对不能跑多个进程。
ShiyuAdmin 这里为了让:
Nginx
Go Backend
统一放入一个应用镜像,引入了:
Supervisor
配置可以理解成:
[supervisord]
nodaemon=true
[program:backend]
command=/app/server
directory=/app
autostart=true
autorestart=true
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
Supervisor 负责:
启动 Nginx
启动 Go
监控 Nginx
监控 Go
异常退出
↓
自动重启
最终 Docker 只需要运行:
supervisord
Supervisor 再负责两个子进程。
二十七、Docker 专用后端配置
ShiyuAdmin 还单独提供:
config.docker.yaml
因为:
本地开发配置
和:
Docker 配置
不能完全一样。
例如 Docker 环境:
server:
port: "8080"
mode: "debug"
database:
driver: "postgres"
host: "shiyu-postgres"
port: 5432
username: "shiyu"
password: "shiyu123"
database: "shiyu_admin_scaffold"
ssl_mode: "disable"
redis:
host: "shiyu-redis"
port: 6379
password: ""
db: 0
核心就是:
database.host
=
shiyu-postgres
redis.host
=
shiyu-redis
而本地开发则可能是:
database:
host: localhost
redis:
host: localhost
环境不同,连接地址就不同。
二十八、CONFIG_FILE 让程序主动选择配置
Compose 中:
environment:
- CONFIG_FILE=configs/config.docker.yaml
后端启动后可以读取:
configFile :=
os.Getenv("CONFIG_FILE")
如果没有:
if configFile == "" {
configFile =
"configs/config.yaml"
}
Docker 环境:
CONFIG_FILE
↓
configs/config.docker.yaml
本地开发:
默认
↓
configs/config.yaml
这样同一个 Go 程序可以运行在不同环境。
二十九、depends_on 不是简单的启动顺序
ShiyuAdmin 应用服务依赖:
PostgreSQL
Redis
因此 Compose 使用:
depends_on:
shiyu-postgres:
condition: service_healthy
shiyu-redis:
condition: service_healthy
意思不是:
先创建 PostgreSQL Container
然后创建 Redis Container
然后马上启动 Go
而是:
启动 PostgreSQL
↓
等待 PostgreSQL healthy
启动 Redis
↓
等待 Redis healthy
两者都正常
↓
启动 shiyu-app
这就比简单写:
depends_on:
- shiyu-postgres
- shiyu-redis
更加可靠。
三十、ShiyuAdmin 自己也有 Health Check
应用启动以后还需要判断:
Nginx 正常吗?
Go Backend 正常吗?
API 能请求吗?
因此可以通过:
/api/v1/system/health
检查。
Compose 中类似:
healthcheck:
test:
[
"CMD-SHELL",
"wget --quiet \
--tries=1 \
--spider \
http://127.0.0.1/api/v1/system/health \
|| exit 1"
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
请求链路:
wget
↓
Nginx :80
↓
/api/v1/system/health
↓
Go :8080
↓
Health API
所以这一个 Health Check 同时验证了:
Nginx
+
Nginx Reverse Proxy
+
Go Backend
整条链路。
三十一、Docker Network 把三个容器连起来
Compose 最后定义:
networks:
shiyu-network:
driver: bridge
三个服务都加入:
networks:
- shiyu-network
最终:
shiyu-network
│
┌───────┼────────┐
│ │ │
▼ ▼ ▼
app postgres redis
于是:
shiyu-app
可以访问:
shiyu-postgres:5432
以及:
shiyu-redis:6379
但是 PostgreSQL 和 Redis 没必要直接暴露给公网。
三十二、把核心 Compose 串起来看
如果按照 ShiyuAdmin 的整体设计思想重新整理,一个完整版本大体可以理解成:
services:
# ===========================
# PostgreSQL
# ===========================
shiyu-postgres:
image:
postgres:15
container_name:
shiyu-postgres
restart:
unless-stopped
environment:
POSTGRES_DB:
shiyu_admin_scaffold
POSTGRES_USER:
shiyu
POSTGRES_PASSWORD:
shiyu123
ports:
- "15432:5432"
volumes:
- shiyu-pg-data:/var/lib/postgresql/data
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U shiyu -d shiyu_admin_scaffold"
]
interval:
10s
timeout:
5s
retries:
5
networks:
- shiyu-network
# ===========================
# Redis
# ===========================
shiyu-redis:
image:
redis:7
container_name:
shiyu-redis
restart:
unless-stopped
ports:
- "16379:6379"
volumes:
- shiyu-redis-data:/data
command:
redis-server
--appendonly yes
healthcheck:
test:
[
"CMD",
"redis-cli",
"ping"
]
interval:
10s
timeout:
5s
retries:
5
networks:
- shiyu-network
# ===========================
# ShiyuAdmin
# ===========================
shiyu-app:
image:
${SHIYU_IMAGE:-ghcr.io/rodert/shiyuadmin:latest}
container_name:
shiyu-app
restart:
unless-stopped
ports:
- "18000:80"
environment:
TZ:
Asia/Shanghai
CONFIG_FILE:
configs/config.docker.yaml
depends_on:
shiyu-postgres:
condition:
service_healthy
shiyu-redis:
condition:
service_healthy
healthcheck:
test:
[
"CMD-SHELL",
"wget --quiet --tries=1 --spider http://127.0.0.1/api/v1/system/health || exit 1"
]
interval:
30s
timeout:
10s
retries:
3
start_period:
40s
networks:
- shiyu-network
# ===========================
# Network
# ===========================
networks:
shiyu-network:
driver:
bridge
# ===========================
# Volume
# ===========================
volumes:
shiyu-pg-data:
driver:
local
shiyu-redis-data:
driver:
local
把这份配置理解以后,Docker Compose 基本就已经入门了。
三十三、为什么还有 docker-compose.local.yml?
ShiyuAdmin 当前又提供了:
docker-compose.local.yml
它并不是重新定义整套系统。
而是:
覆盖 docker-compose.yml 中的一部分配置。
例如主配置:
shiyu-app:
image:
ghcr.io/rodert/shiyuadmin:latest
表示正常可以直接拉取已经构建好的镜像。
但是开发者修改了源码以后,希望:
从当前源码重新 build。
于是:
services:
shiyu-app:
build:
context:
.
dockerfile:
Dockerfile
image:
shiyu-admin:local
volumes:
- ./backend/shiyu-admin-backend/configs:/app/configs:ro
启动时同时指定:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up -d --build
Compose 会把两份配置合并。
三十四、为什么这种覆盖设计很好用?
如果只有:
docker-compose.yml
你可能需要在:
生产模式
和:
本地源码构建模式
之间来回修改:
image:
和:
build:
非常容易误提交。
现在拆成:
docker-compose.yml
↓
基础/正式配置
docker-compose.local.yml
↓
本地开发覆盖
部署就更加清晰。
生产:
docker compose up -d
本地构建:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up -d --build
这是我比较推荐的一种 Compose 组织方式。
三十五、真正启动 ShiyuAdmin
首先:
git clone \
https://github.com/Rodert/ShiyuAdmin.git
进入目录:
cd ShiyuAdmin
然后:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up -d --build
这里:
-d
表示:
后台运行
而:
--build
表示:
启动之前重新 Build 镜像。
三十六、这条命令背后发生了什么?
虽然用户只执行:
docker compose up -d --build
但后面实际上发生:
读取 docker-compose.yml
↓
读取 docker-compose.local.yml
↓
合并配置
↓
创建 Docker Network
↓
创建 PostgreSQL Volume
↓
创建 Redis Volume
↓
拉取 PostgreSQL
↓
拉取 Redis
↓
构建 React
↓
构建 Go
↓
生成 shiyu-app 镜像
↓
启动 PostgreSQL
↓
等待 PostgreSQL Healthy
↓
启动 Redis
↓
等待 Redis Healthy
↓
启动 shiyu-app
↓
Supervisor 启动 Go
↓
Supervisor 启动 Nginx
↓
Nginx 提供 React
↓
Nginx 代理 Go API
↓
Health Check
↓
ShiyuAdmin Ready
这就是:
一条命令部署整个系统。
三十七、查看运行状态
执行:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
ps
理想状态类似:
NAME STATUS
shiyu-app Up (healthy)
shiyu-postgres Up (healthy)
shiyu-redis Up (healthy)
重点看:
healthy
而不只是:
Up
三十八、访问 ShiyuAdmin
启动以后访问:
http://localhost:18000
进入后台。
后端 API:
http://localhost:18000/api/v1
Health Check:
http://localhost:18000/api/v1/system/health
默认管理员账号:
用户名:
admin
密码:
Admin@123
第一次体验 ShiyuAdmin 的话,基本只需要:
clone
↓
docker compose
↓
浏览器打开
三十九、怎么查看日志?
查看全部:
docker compose logs -f
只看应用:
docker compose \
logs \
-f \
shiyu-app
PostgreSQL:
docker compose \
logs \
-f \
shiyu-postgres
Redis:
docker compose \
logs \
-f \
shiyu-redis
-f 类似 Linux:
tail -f
会持续跟踪日志。
四十、进入 ShiyuAdmin 容器
如果需要排查:
docker exec \
-it \
shiyu-app \
sh
然后可以:
ls -lah
你应该能够看到:
/app/server
/app/configs
检查进程:
ps
会看到类似:
supervisord
nginx
server
这也是排查部署问题非常有效的方法。
四十一、进入 PostgreSQL
执行:
docker exec \
-it \
shiyu-postgres \
psql \
-U shiyu \
-d shiyu_admin_scaffold
进入以后:
\dt
查看表。
例如:
SELECT *
FROM sys_users
LIMIT 10;
退出:
\q
四十二、进入 Redis
执行:
docker exec \
-it \
shiyu-redis \
redis-cli
检查:
PING
返回:
PONG
查看数据库:
INFO keyspace
切换:
SELECT 0
查看 Key:
SCAN 0
ShiyuAdmin 自己后台也有 Redis 缓存管理功能,因此 Docker Redis 正常启动以后,可以直接在后台查看 Redis 数据。
四十三、只想启动 PostgreSQL + Redis 怎么办?
开发后端的时候可能不希望:
Go
React
也运行在 Docker。
比如希望:
Go
↓
IDE 本地启动
React
↓
npm run start:dev
PostgreSQL
↓
Docker
Redis
↓
Docker
那就只启动:
docker compose \
up \
-d \
shiyu-postgres \
shiyu-redis
然后本地后端使用:
database:
host:
localhost
port:
15432
redis:
host:
localhost
port:
16379
注意:
Docker 容器访问数据库:
shiyu-postgres:5432
本机程序访问:
localhost:15432
这是两种不同的网络路径。
四十四、修改代码以后怎么重新构建?
如果修改:
frontend
backend
Dockerfile
可以:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
build \
shiyu-app
再:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up \
-d
或者直接:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up \
-d \
--build
四十五、只重启 Go 应用有没有用?
可以:
docker compose restart shiyu-app
但是如果修改的是源代码:
restart
并不会重新编译。
必须:
docker compose up -d --build
区别:
restart
=
重新启动旧镜像
--build
=
重新构建新镜像
这个也很容易搞错。
四十六、停止 ShiyuAdmin
执行:
docker compose down
会:
停止 Container
删除 Container
删除 Network
但:
Volume 还在。
所以数据库数据还在。
四十七、彻底恢复到全新状态
如果你正在测试初始化逻辑,希望数据库完全清空:
docker compose down -v
再:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up \
-d \
--build
相当于:
删除 PostgreSQL 数据
删除 Redis 数据
重新初始化系统
再次提醒:
生产环境不要随便执行
down -v。
四十八、生产环境不要直接使用默认密码
ShiyuAdmin 为了:
开箱即用
仓库里的 Docker 示例配置会提供开发环境默认值。
但是如果真正部署生产环境:
PostgreSQL Password
JWT Secret
管理员密码
必须修改。
比如不要长期使用:
shiyu123
不要长期使用示例:
JWT Secret
更合理的是通过:
.env
管理。
例如:
POSTGRES_DB=shiyu_admin_scaffold
POSTGRES_USER=shiyu
POSTGRES_PASSWORD=change-me
JWT_SECRET=replace-with-a-long-random-secret
然后 Compose:
environment:
POSTGRES_DB:
${POSTGRES_DB}
POSTGRES_USER:
${POSTGRES_USER}
POSTGRES_PASSWORD:
${POSTGRES_PASSWORD}
四十九、生产环境甚至不应该暴露 PostgreSQL
本地开发为了调试方便可以:
ports:
- "15432:5432"
生产服务器如果:
只有 shiyu-app 需要连接 PostgreSQL
完全可以删除:
ports:
变成:
shiyu-postgres:
image:
postgres:15
networks:
- shiyu-network
因为:
shiyu-app
通过 Docker Network 依然可以:
shiyu-postgres:5432
连接。
但是公网访问不了 PostgreSQL。
Redis 同理。
生产环境通常应该:
公网
↓
只暴露 80 / 443
PostgreSQL
↓
内部网络
Redis
↓
内部网络
五十、生产环境可以再加 HTTPS
例如服务器前面增加:
Cloudflare
↓
Nginx / Caddy
↓
ShiyuAdmin :18000
或者:
Internet
↓
443
↓
Reverse Proxy
↓
127.0.0.1:18000
↓
ShiyuAdmin
例如:
server {
listen 443 ssl;
server_name admin.example.com;
ssl_certificate
/etc/ssl/fullchain.pem;
ssl_certificate_key
/etc/ssl/privkey.pem;
location / {
proxy_pass
http://127.0.0.1:18000;
proxy_set_header
Host
$host;
proxy_set_header
X-Real-IP
$remote_addr;
proxy_set_header
X-Forwarded-For
$proxy_add_x_forwarded_for;
}
}
最终:
https://admin.example.com
就可以访问 ShiyuAdmin。
五十一、为什么我认为 ShiyuAdmin 这套 Docker 方案比较适合开源项目?
一个开源后台系统,如果 README 写:
第一步安装 PostgreSQL
第二步安装 Redis
第三步创建数据库
第四步修改数据库用户
第五步安装 Go
第六步安装 Node.js
第七步 npm install
第八步 build
第九步安装 Nginx
第十步配置代理
……
很多人看到这里就不会继续用了。
而 ShiyuAdmin 希望做到:
git clone ...
cd ShiyuAdmin
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up -d --build
然后:
http://localhost:18000
直接访问。
这才符合一个:
开箱即用的后台脚手架
应该有的体验。
五十二、这套设计真正值得借鉴的几个技术点
如果把 ShiyuAdmin 的 Docker 方案拆开,我认为真正值得其他项目借鉴的是下面这些东西。
1. Multi-stage Build
Node Builder
+
Go Builder
+
Runtime Image
构建环境和运行环境分离。
2. 前后端合并为一个部署单元
React dist
+
Go Binary
+
Nginx
降低中小项目部署复杂度。
3. Nginx 统一入口
/
↓
React
/api
↓
Go
让前后端在生产环境保持同源。
4. Supervisor 管理多个进程
Supervisor
├── Go
└── Nginx
让单应用镜像仍然可以稳定管理两个进程。
5. Docker DNS
shiyu-postgres
shiyu-redis
直接作为内部 Host。
不写 IP。
6. Health Check
不是:
容器启动就算成功。
而是:
数据库真的 Ready
Redis 真的 Ready
应用真的 Ready
7. depends_on + service_healthy
PostgreSQL Ready
+
Redis Ready
↓
ShiyuAdmin Start
降低初始化时的连接失败。
8. Volume 数据持久化
Container
可以删
Data
不能跟着删
9. Local Override
docker-compose.yml
+
docker-compose.local.yml
生产配置与本地源码构建解耦。
五十三、最终部署链路
最后再把整个 ShiyuAdmin Docker 部署链路完整串一次:
Git Clone
│
▼
docker compose up
│
├────────────────────────────┐
│ │
▼ ▼
PostgreSQL Redis
│ │
▼ ▼
Volume Volume
│ │
▼ ▼
Health Check Health Check
│ │
└─────────────┬──────────────┘
│
Ready
│
▼
shiyu-app
│
┌───────────┴────────────┐
│ │
▼ ▼
React Build Go Build
│ │
└──────────┬─────────────┘
▼
Final Image
│
▼
Supervisor
┌───┴────┐
│ │
▼ ▼
Nginx Go
│ │
┌──────┘ │
│ │
▼ ▼
React /api
│ │
└───────┬───────┘
│
▼
localhost:18000
用户最终看到的其实只有:
http://localhost:18000
但是背后:
前端构建
后端构建
Nginx
Supervisor
PostgreSQL
Redis
Docker Network
Volume
Health Check
Reverse Proxy
全部已经被 Docker Compose 管起来了。
总结
Docker Compose 真正的价值,并不是:
少敲几条 Docker 命令。
而是把整个应用运行环境:
代码
数据库
缓存
网络
端口
数据卷
依赖关系
健康检查
启动顺序
全部变成:
代码化配置。
这样无论谁拿到 ShiyuAdmin:
开发者 A 的 Mac
开发者 B 的 Windows
Linux 服务器
CI 环境
理论上都可以按照同一套方式启动。
而 ShiyuAdmin 当前采用的:
React
+
Go
+
Nginx
+
Supervisor
↓
shiyu-app
PostgreSQL
↓
shiyu-postgres
Redis
↓
shiyu-redis
我认为是一个非常适合中小型后台项目和开源脚手架的部署结构。
复杂度没有 Kubernetes 那么高,却已经解决了绝大多数单机部署问题。
最后真正留给用户的,只剩一条命令:
docker compose \
-f docker-compose.yml \
-f docker-compose.local.yml \
up -d --build
这也是我认为一个开源后台项目应该尽量做到的事情:
不只是把代码开源,而是让别人能够真正把它跑起来。
ShiyuAdmin:
https://github.com/Rodert/ShiyuAdmin

浙公网安备 33010602011771号