1. 引言:为什么使用 Docker Compose?
2. 项目介绍与服务架构图
3. 服务配置讲解:从混乱到规范
4. 前端服务配置与调试
4.1. 问题1:npm install 非常慢或失败
4.2. 问题2:容器启动后立即退出
4.3. 问题3:本地代码修改不生效
5. 后端服务配置与问题解析
5.1. 问题1:Maven 下载依赖慢或失败
5.2. 问题2:后端连接数据库失败
6. 数据库服务配置
6.1. 问题1:数据库初始化脚本未执行
6.2. 问题2:数据无法持久化
6.3. 问题3:连接被拒绝
7. Nginx 服务配置
7.1. 配置说明
8. 常见问题排查与经验总结
8.1. 8.1 容器启动失败或退出
8.2. 8.2 依赖安装卡顿
8.3. 8.3 后端数据库连接失败
8.4. 8.4 容器间访问失败
8.5. 8.5 环境变量未生效
8.6. 8.6 数据未持久化
8.7. 8.7 初始化 SQL 未执行
9. 推荐实践与配置规范
10. 附录
10.1. 示例 `.env`
10.2. Makefile 示例
在日常开发过程中,经常遇到开发环境与线上环境不一致,导致各种异常问题出现。为了让线下环境与线上保持一致,减少“本地跑正常,线上崩”的问题,我们采用 Docker 虚拟环境。
由于项目包含前后端多个服务,手动用 `docker run` 启动繁琐,且配置容易错乱,因此选择使用 Docker Compose 来统一管理启动配置,提升开发效率。
本文通过一个微信小程序项目案例,介绍 Docker Compose 的配置方法及遇到的问题和解决方案。
项目为一个微信小程序,界面通过 UniApp 编译而来。前端通过 Nginx 访问后端服务,同时 Nginx 负责图片处理。
项目包含四个容器:
front :前端(uniapp/vue)
devtool :微信开发者工具,查看编译结果
api :后端(Spring Boot)
db :数据库(PostgreSQL)
nginx :Nginx 代理和图片处理
在配置容器时,关键步骤包括:
选择合适镜像(image、build)
让镜像访问到本地文件(本地挂载和 volume)
系统初始化(Postgres 初始化、npm install、mvn install)
运行命令(npm run、mvn spring-boot:run)
容器启动后,多容器之间协作(网络、端口、依赖、环境变量)
遇到的主要问题是配置重复、启动失败、服务通信异常等,下面我们依次讲解。
原因 :默认镜像内 npm 使用国外源,网络慢,且没有缓存
解决方案 :
挂载 `.npmrc` 文件,配置国内镜像源
挂载 npm 缓存目录,避免重复下载依赖
volumes: - ../../front:/data - ./.npmrc:/root/.npmrc - npm_cache:/root/.npm
原因 :默认命令执行完容器即停止
解决方案 :
设置 `tty: true`,`stdin_open: true`
用 `command: tail -f /dev/null` 保持容器常驻方便调试
正确设置启动命令如 `npm run dev:mp-weixin`
原因 :未正确挂载本地源码
解决方案 :
挂载源码目录到容器内
设置工作目录 `working_dir: /data`
—
原因 :镜像默认使用国外仓库
解决方案 :
挂载本地 Maven 缓存目录 `.m2`
使用私服或配置阿里云 Maven 镜像仓库
volumes: - ../../api:/app - ./m2:/root/.m2
原因 :数据库容器未启动或连接配置错误
解决方案 :
在 Compose 配置中用 `depends_on` 确保数据库先启动
在后端配置文件和 `.env` 中正确配置数据库地址、端口和账户
—
原因 :初始化 SQL 文件挂载路径或格式错误;已有数据卷导致初始化跳过
解决方案 :
将初始化脚本挂载到 `/docker-entrypoint-initdb.d/`
确保脚本文件后缀为 `.sql` 或 `.sh`
清理数据卷重新初始化(`docker compose down -v`)
原因 :未挂载数据目录 Volume
解决方案 :
配置 named volume 持久化数据库数据目录
volumes: tm-pgdata:
volumes: - tm-pgdata:/var/lib/postgresql/data
原因 :数据库未启动或端口配置错误
解决方案 :
使用服务名访问数据库(`db:5432`)
正确映射端口供本地访问
—
下面是 Nginx 的完整配置文件,附带详细注释,涵盖代理 API、图片处理、HTTP 到 HTTPS 重定向等功能。正式环境请根据注释调整启用 HTTPS 和域名配置。
####################################### 功能说明:# 1. 代理 API 地址 /api/# 2. 处理图片请求 /images/${图片名}@${宽度}w_${高度}h_${图片质量}Q_[r(调整大小)|c(裁剪)]# 3. HTTP 重定向 HTTPS(注释部分)## 注意:# 1. 正式环境需启用 HTTPS,具体配置参考注释部分# 2. 正式环境需修改 server_name# 3. 需正确配置代理的主机 IP 和端口(API_HOST、API_PORT)# 4. 需安装 Nginx image_filter 模块支持图片处理# 5. 需修改上传图片路径(FILE_PATH)######################################upstream tm { server ${API_HOST}:${API_PORT}; }# HTTP 重定向到 HTTPS(正式环境启用)# server {# listen 80;# server_name time.loujiaye.cn;# rewrite ^(.*)$ https://time.loujiaye.cn$1 permanent;# }proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=cache_one:50m inactive=24h use_temp_path=off;server { listen ${NGINX_PORT}; # listen 443 ssl http2; # server_name time.loujiaye.cn 127.0.0.1; # SSL 配置示例(正式环境启用) # ssl_certificate /root/cert/time.pem; # ssl_certificate_key /root/cert/time.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # ssl_prefer_server_ciphers on; # 缓存相关配置 proxy_buffer_size 16k; proxy_buffers 4 32k; proxy_busy_buffers_size 96k; proxy_temp_file_write_size 96k; index index.html; # API 代理配置 location ^~ /api/ { proxy_pass http://tm/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect / /api/; } # 处理图片请求,支持动态调整大小或裁剪 location ~ /images/(.+)\.(png|jpg|gif|JPG|PNG|GIF)@(\d+)w_(\d+)h_(\d+)Q_([rc])$ { set $w $3; set $h $4; set $q $5; set $type $6; set $image_path $1.$2; set $cache_path $1_$3w_$4h_$5Q_$6.$2; if ( $type = 'r') { set $type 'image-resize'; } if ( $type = 'c') { set $type 'image-crop'; } set $image_uri /$type/$image_path?w=$w&h=$h&q=$q; if ( -f $cache_path) { rewrite (.+)\.(png|jpg|gif|JPG|PNG|GIF)@(\d+)w_(\d+)h_(\d+)Q_([rc])$ $cache_path; break; } if ( !-f $cache_path) { proxy_pass https://127.0.0.1$image_uri; } proxy_store $cache_path; proxy_store_access user:rw group:rw all:rw; proxy_temp_path /tmp/; proxy_set_header Host $Host; expires 30d; } # 图片调整大小处理 location ~ /image-resize(.+)\.(png|jpg|gif|PNG|JPG|GIF) { rewrite /image-resize(.+)\.(png|jpg|gif|PNG|JPG|GIF)$ $1.$2 break; image_filter resize $arg_w $arg_h; image_filter_jpeg_quality $arg_q; image_filter_buffer 50M; root ${FILE_PATH}; } # 图片裁剪处理 location ~ /image-crop(.+)\.(png|jpg|gif|PNG|JPG|GIF) { rewrite /image-crop(.+)\.(png|jpg|gif|PNG|JPG|GIF)$ $1.$2 break; image_filter crop $arg_w $arg_h; image_filter_jpeg_quality $arg_q; image_filter_buffer 50M; root ${FILE_PATH}; } # 静态图片访问及缓存配置 location /images/ { add_header Cache-Control "public"; proxy_redirect off; proxy_set_header Host $host; proxy_cache cache_one; proxy_cache_valid 200 302 24h; proxy_cache_valid 301 30d; proxy_cache_valid any 5m; expires 90d; access_log off; alias ${FILE_PATH}; autoindex on; } }
—
表现 :容器启动后立即退出
排查 :
用 `tail -f /dev/null` 暂时保持容器运行
查看日志 `docker compose logs <service>`
进入容器 `docker exec -it <container> sh`
表现 :npm、Maven 依赖安装慢或失败
解决 :
使用国内镜像源
挂载缓存目录
表现 :Spring Boot 报错无法连接数据库
解决 :
保证数据库容器启动优先
使用正确服务名和端口访问
检查环境变量配置是否正确注入
表现 :API 服务无法被 Nginx 或前端访问
解决 :
容器间通过服务名通信,避免 localhost
端口映射要正确
表现 :配置文件未正确替换变量
解决 :
使用 `env_file` 明确引入 `.env`
Nginx 使用 `envsubst` 替换模板变量
表现 :重启后数据丢失
解决 :
使用 volume 持久化数据库数据目录
表现 :初始化脚本无效
解决 :
挂载 SQL 脚本目录正确
清理旧数据卷
—
统一使用 `.env` 管理配置变量
开发时挂载源码目录,部署时构建镜像
缓存依赖目录提升构建效率
Nginx 配置用模板 + `envsubst` 动态注入变量
编写 Makefile 简化日常命令
日志限制防止占用过多磁盘
版本控制时忽略缓存、数据目录
—
POSTGRES_DB=tm_db POSTGRES_USER=tm_user POSTGRES_PASSWORD=123456 POSTGRES_PORT=5432 API_PORT=8080 NGINX_PORT=80 FILE_PATH=/var/www/images API_HOST=api
up: docker compose up -d --builddown: docker compose downlogs: docker compose logs -f
—