ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker Compose实战:MySQL+Java+Nginx前后端分离部署详解

Docker Compose实战:MySQL+Java+Nginx前后端分离部署详解 在前后端分离项目的部署里把 Docker、Compose、MySQL、Java、Nginx 这几样东西串起来看着是常规操作真正动手时才会发现问题往往不在“怎么启动容器”而在“容器之间怎么互通”“数据怎么不丢”“前端请求怎么精确打到后端”。很多刚上手 Compose 的人第一反应是我已经把每个服务都写进了编排文件一起docker compose up -d就结束了。实际跑起来才发现要么后端连不上数据库要么前端页面能打开但接口全部超时要么重启容器后数据消失。这些问题的根源大部分都出在对 Docker 网络模型的理解不够扎实。这篇内容我会从一次真实的前后端分离项目部署切入把 MySQL 8.0、Java 服务以 Spring Boot 为例、Nginx 静态资源服务这三层架构用 Docker Compose 完整编排起来重点讲清楚 Compose 的默认网络行为、服务间通信方式、数据卷持久化、健康检查、以及 Nginx 反向代理配置里的关键细节。适合正在学 Docker Compose 的开发者也适合那些已经能把容器跑起来但遇到网络不通、数据丢失、重启失联等问题的同学对照排查。1. 容器化部署前先把这几件事想清楚1.1 这次架构里每个容器扮演什么角色部署前后端分离项目最朴素的需求是MySQL 存数据Java 后端提供接口Nginx 托管前端静态文件并把 API 请求转发给 Java 服务。落在 Docker 世界里就对应三个镜像、三个容器、两个必须打通的网络通道。MySQL 容器对外暴露 3306 端口供开发调试但对内只对 Java 服务开放。Java 容器不对外暴露端口只让 Nginx 能访问它这样更安全。Nginx 容器对外暴露 80 端口浏览器能直接访问它既要托管前端静态资源也要把/api路径的请求反向代理到 Java 服务。这里的核心网络通道有两个Java 服务 → MySQL通过jdbc:mysql://mysql:3306/yourdb访问这里的mysql是 Compose 里的服务名也是 Docker 网络中的 DNS 主机名。Nginx → Java 服务通过http://backend:8080转发这里的backend同样是服务名。这个设计的核心思想是容器之间不依赖宿主机 IP而是通过 Compose 自动创建的 bridge 网络进行 DNS 解析。你不需要去查容器的 IP 地址不用配置宿主机 hosts只要服务名对上就行。1.2 为什么网络配置决定了项目能否跑通很多人在本机直接用localhost连 MySQL一切正常放进 Docker 后把 Java 配置里的localhost:3306改成mysql:3306还是连不上。这不是配置写错了而是根本没理解 Docker 的网络隔离特性。Docker 容器默认是独立的网络命名空间。容器里的localhost指向容器自己不是宿主机也不是别的容器。所以 Java 容器里写localhost:3306它试着连的是 Java 容器自己的 3306 端口那里什么都没有。要想跨容器通信必须让容器处于同一个 Docker 网络里。Compose 最大的价值之一就在这里它默认会为整个项目创建一个独立的 bridge 网络所有在同一个 Compose 文件里定义的服务都会自动加入这个网络并自动获得基于服务名的 DNS 解析。这意味着你只要用服务名去访问对方即可剩下的交给 Docker。这个“默认网络”的机制是整个部署架构里最关键的地基。把这一层想通了后面所有的连接问题基本都是配置文件路径问题想不通你就会陷入反复查 IP、改 hosts、手动docker network connect的泥潭。2. Compose 网络模型理解数据流走向2.1 默认 bridge 网络与自定义网络的取舍Docker Compose 在启动时会为每个项目创建一个默认的 bridge 网络网络名称通常是项目名_default。比如你的项目目录叫myapp那么网络名就是myapp_default。所有服务都挂在这个网络下彼此能用服务名互通。但真正要上线时我更倾向于使用networks字段显式声明网络。这样做的原因很直接默认网络虽然方便但它的生命周期完全跟着整个 Compose 项目走你很难单独对特定网络做精细控制。使用自定义网络时可以给网络指定driver_opts、设置internal等属性未来做跨主机 Swarm 或对接外置网络时也更灵活。显式声明网络后每一个服务挂在哪个网络上是只有前端入口还是同时连接多个网络这就明朗了。排查问题的时候你一眼就能看出这条链路是否合理化。在前后端分离场景下我这里建议你定义两个网络frontend承载 Nginx 的对外流量边界。backend承载 MySQL 与 Java 之间的内部数据链路。Nginx 需要同时访问前端静态文件和 Java 服务所以它同时接入两个网络。Java 服务只需要访问 MySQL所以它只接入backend网络。MySQL 只需要被 Java 访问所以也只接入backend网络。用表格来整理服务与网络的关系更直观服务接入网络网络中可访问的对象暴露端口Nginxfrontend backendJava 服务backend80JavabackendMySQLbackend不暴露MySQLbackend无仅接受 backend 网络内连接3306仅供调试可选择性暴露这样设计的好处是如果以后某个服务被攻破攻击者能横向移动的范围被限制在一个网络内而不是整个项目全暴露。虽然单机部署谈不上多高安全等级但顺手把网络边界画清楚总是值得的。2.2 服务名即主机名一次弄懂 DNS 解析机制在 Compose 网络的 bridge 模式下Docker 内置的 DNS 服务会自动把服务名解析为对应容器的 IP。这里有一个容易混淆的点这个解析是 Docker 内部的不会写入宿主机的/etc/hosts也不需要在容器里手动配置。Java 应用里连数据库JDBC URL 写成jdbc:mysql://mysql:3306/dbname这里的mysql指的就是 Compose 服务名而不是容器的 hostname虽然 Compose 默认也会把容器 hostname 设成服务名。同理Nginx 的proxy_pass http://backend:8080中的backend也是服务名。这里有一个常见误区Docker 网络的 DNS 解析不是“所有容器都能解析所有服务名”。只有在同一个网络里的服务才能互相解析。如果你把 Nginx 和 Java 放在不同网络Nginx 里写backend:8080是解析不了的报错就是host not found in upstream。这就是网络隔离在 DNS 层面的体现。在排查连接问题时我第一个执行的命令通常是docker exec -it 容器名 ping 服务名如果 ping 不通先检查两个容器是否在同一个网络docker network inspect 网络名这个命令会列出网络里所有挂载的容器一眼就能看出问题。80% 的连接不通在这一步就能找到原因——要么服务没挂到同一个网络要么服务名拼写不一致。2.3 端口暴露策略不要一股脑全映射很多初学者把宿主机端口和容器端口搞混。比如 Nginx 配置文件里监听 80然后 Compose 里写80:80这没问题但如果你再给 Java 也写个8080:8080这就有讲究了。如果是云端服务器把 Java 的 8080 端口暴露到宿主机相当于把你的后端接口直接露在公网任何人都可以绕过 Nginx 直接访问。反代的意义就少了而且安全隐患增加。我推荐的策略是MySQL 在云服务器上不暴露端口只在开发机或测试环境通过127.0.0.1:3306临时暴露出供查看数据。Java 服务完全不暴露端口只被 Nginx 在容器网络内部访问。Nginx 容器只暴露 80/443作为唯一外部入口。这样做还有一个好处你不需要在云服务器安全组里开多个端口。只需要开 80HTTP、443HTTPS、22SSH攻击面明显缩小。3. 编排文件逐段拆解从零搭一套前后端分离环境3.1 环境准备与镜像选择开始写docker-compose.yml前先确认 Docker 环境就绪。建议 Docker 版本不低于 20.10自带的 Compose V2 插件已经很好用。检查命令docker --version docker compose version如果你还在用老版本的docker-composeV1命令是带横杠的且配置文件语法和网络处理略有差异建议尽早迁移到 Compose V2。镜像版本方面我用的是MySQL 8.0具体标签mysql:8.0Java 服务镜像这里我用一个简化示意假设你自己已经通过 Dockerfile 构建好了假设镜像名为registry.example.com/myapp/backend:latestNginx 1.24镜像nginx:1.24-alpinealpine 版本体积小适合作为静态资源服务器Java 服务的 Dockerfile 我这里顺手给一版常规做法稍后编排里会直接引用它# 多阶段构建先用 Maven 打包 FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests # 运行阶段 FROM eclipse-temurin:17-jre WORKDIR /app COPY --frombuilder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]如果你是用 IDE 构建镜像也可以用docker build -t backend:1.0 .先打一个镜像再在 Compose 里引用。这一步和网络无关但容易出错建议按照自己的构建链路提前把镜像做好再进行编排。3.2 docker-compose.yml 的关键字段与逻辑顺序下面是完整的 Compose 文件。注意 YAML 的缩进必须严格一致一个空格错误就会导致整个文件解析失败。version: 3.8 services: mysql: image: mysql:8.0 container_name: project-mysql restart: always environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: shopdb MYSQL_USER: shopuser MYSQL_PASSWORD: shop123456 volumes: - mysql-data:/var/lib/mysql - ./mysql/init:/docker-entrypoint-initdb.d:ro networks: - backend healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -uroot, -proot123456] interval: 10s timeout: 5s retries: 5 start_period: 30s backend: image: backend:1.0 container_name: project-backend restart: always depends_on: mysql: condition: service_healthy environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/shopdb?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue SPRING_DATASOURCE_USERNAME: shopuser SPRING_DATASOURCE_PASSWORD: shop123456 networks: - backend nginx: image: nginx:1.24-alpine container_name: project-nginx restart: always ports: - 80:80 volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ../frontend/dist:/usr/share/nginx/html:ro depends_on: - backend networks: - frontend - backend volumes: mysql-data: networks: frontend: driver: bridge backend: driver: bridge逐个说关键字段。environment 部分MySQL 的MYSQL_DATABASE会在首次初始化时自动创建数据库MYSQL_USER和MYSQL_PASSWORD会创建一个普通用户。注意这个自动初始化只在数据目录/var/lib/mysql为空时生效如果已经有数据卷不会再执行。所以如果你改了密码但没效果多半就是数据卷已经存在了。volumes 部分mysql-data:/var/lib/mysql是命名卷数据持久化在这里容器删了数据还在。而./mysql/init这个挂载目录是让你放初始化 SQL 脚本的。MySQL 镜像启动时会发现/docker-entrypoint-initdb.d目录里有.sql文件在数据库初始化阶段自动执行。这个机制非常适合导入初始数据、建表语句、初始化配置项。depends_on 条件这里用的是condition: service_healthy意思是等 MySQL 健康检查通过后再启动 Java 服务否则会出现 Java 启动时数据库还没就绪、连接池初始化失败的情况。健康检查用的是mysqladmin ping这个命令在 MySQL 容器内是存在的不需要额外安装。networks 部分Java 服务只挂在backend网络Nginx 同时挂frontend和backend。这里的逻辑是Nginx 必须能访问 Java所以搭了backendNginx 是唯一对外服务所以放在单独的frontend网络未来如果还要加别的前端网关或 CDN 类的组件可以单独扩展。3.3 初始化脚本与数据持久化避免数据“消失”的坑第一次用 Compose 部署 MySQL最容易踩的坑就是数据卷未挂载容器一删库就没了。上面的文件里已经写了mysql-data:/var/lib/mysql这一步不能省。另外要理解/docker-entrypoint-initdb.d的触发时机它只在数据目录初始化时运行一次。如果你已经跑过 MySQL再去改初始化脚本重新docker compose up是不会生效的。想重跑初始化有两个办法删除数据卷彻底重新初始化docker compose down -v这条命令会连数据卷一起删掉谨慎使用。手动进入容器执行 SQL 文件适合补跑单个脚本不影响已有数据。实际项目里我通常会把初始化脚本分成两个目录init首次建表、基础数据和upgrade后续增量变更避免动不动就回炉重造。-- 示例mysql/init/01-schema.sql CREATE DATABASE IF NOT EXISTS shopdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE shopdb; CREATE TABLE IF NOT EXISTS products ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(128) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意utf8mb4这个编码MySQL 8.0 默认就是它它比utf8更完整能正确存储 emoji 和一些生僻字。如果你的项目还在用老编码升级到 8.0 之后建议把连接参数里加上characterEncodingutf8并且数据库侧统一为utf8mb4否则可能出现中文乱码或者索引长度超限的报错。4. 让数据链路真正活起来Nginx 配置里的门道4.1 为什么要把 Nginx 放进容器而不是装在宿主机以前没有容器的年代大家喜欢在宿主机直接装 Nginx做静态文件服务和反向代理。迁到 Docker 后很多人的第一直觉是宿主机装一个容器里跑业务。这能跑但有个致命问题Nginx 要反向代理到 Java 容器时如果用宿主机 IP 加映射端口一旦容器重建IP 就变了Nginx 配置要跟着改非常被动。把 Nginx 放进容器并接入同一个 Docker 网络后Nginx 直接通过服务名解析 Java 容器容器重建 IP 变了也无所谓DNS 会自动更新。这个体验比宿主机装 Nginx 干净太多。同时Nginx 的配置可以跟着项目一起版本化管理nginx/conf.d/default.conf放进代码仓库团队协作时大家拉下来就是同一套配置。在单节点部署场景里我建议默认用nginx:stable-alpine这种镜像。它不仅小约 20MB而且基础系统精简暴露面小稳定性足够。如果不需要额外的 Nginx 模块如 RTMP、Lua没必要装nginx:latest这种包含大量内置模块的版本。4.2 一份能用的 server 配置静态资源、API、SPA 路由三件事这是 Nginx 容器里default.conf的核心配置。它能同时做三件事托管前端文件、转发 API 到后端、处理前端路由的 history 模式刷新问题。server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; # 处理前端 SPA 路由如果请求的文件不存在尝试回退到 index.html location / { try_files $uri $uri/ /index.html; } # 前后端分离 API 转发 location /api/ { proxy_pass http://backend: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; proxy_connect_timeout 10s; proxy_read_timeout 60s; proxy_send_timeout 60s; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 30d; add_header Cache-Control public, no-transform; try_files $uri 404; } }逐段解释一下。location /api/块里的proxy_pass http://backend:8080;是很多人在这一步容易出问题的地方。http://backend是 Docker 网络里的服务名Nginx 容器启动后会自动把backend解析成 Java 容器的内网 IP。后端 Spring Boot 项目如果配置了全局context-path为/api那这里转发时要注意路径处理。比如后端接口路径是/api/products整个请求到 Nginx 时是/api/productsNginx 转发给 Java 服务Java 需要能接收到/api/products。当proxy_pass后面没有 URI 路径时只有域名和端口Nginx 会把原始 URI 完整传过去这就是我想要的效果。另一种情况是后端接口没有/api前缀那么 Nginx 需要在转发时把/api去掉。做法是location /api/ { proxy_pass http://backend:8080/; }注意proxy_pass后面的斜杠这个斜杠表示用替换模式请求/api/productsNginx 会去掉/api/转发成/products给后端。很多人在这个斜杠上栽过跟头转发之后 404 找不到路径先检查proxy_pass的 URI 部分。try_files $uri $uri/ /index.html;是 SPA 路由必需的。前端使用 Vue Router 或 React Router 的 history 模式时直接访问/dashboard这类路径后端是没有这个物理文件的Nginx 默认会返回 404。try_files会先去磁盘上找真实文件找不到就回退到/index.html让前端自己的路由接管。location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$这一段是给静态资源加缓存头。前端dist目录下的文件如果带了 hash 文件名比如app.8f3k2j.js可以放心加expires 30d因为文件名变化后 URL 会变不会出现缓存不更新的问题。这个优化对首屏加载速度提升很明显。4.3 前端资源放在哪镜像内置还是挂载目录关于前端dist目录的挂载方式有两条路线构建镜像时把 dist 打进 Nginx 镜像。通过数据卷挂载到宿主机目录。我更推荐把前端 dist 打进镜像。理由有三个镜像不可变部署到任何环境内容一致。天然支持回滚只需要换镜像 tag不用去服务器上复制文件。不用担心宿主机目录和容器内路径权限问题。具体做法是在 Nginx 镜像的 Dockerfile 里加上FROM nginx:1.24-alpine COPY dist/ /usr/share/nginx/html/ COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80但在 Compose 编排时为了开发方便也可以临时用挂载覆盖镜像内的目录volumes: - ../frontend/dist:/usr/share/nginx/html:ro这个 :roread-only是个好习惯防止容器运行时误改文件。生产环境如果用了镜像内置方案这个 volume 就不需要写了。5. 从搭建到验收一组实测命令与常见问题的现场排查5.1 启动服务后先看什么敲下启动命令之前我建议先做一个语法检查docker compose config这条命令会校验 Compose 文件的格式并输出最终展开后的配置内容。如果 YAML 缩进有问题、字段拼写有误在这里就能暴露出来。很多人在docker compose up时才发现错误然后又得慢慢排查其实提前做这个检查能省不少时间。确认没问题后启动docker compose up -d-d是后台运行。启动后立刻查看容器状态docker compose ps正常情况下三个服务的状态都应该是UpMySQL 还会显示(healthy)或(health: starting)。如果 MySQL 容器反复重启用docker compose logs mysql查看日志多半问题出在这几处数据卷挂载路径权限不对、初始化 SQL 有语法错误、root 密码配置与健康检查不一致。其中健康检查配置的密码和MYSQL_ROOT_PASSWORD如果对不上容器会被反复标记为 unhealthyJava 服务就会一直等待形成连锁反应。启动完所有容器后我不能说一切就此结束。你必须做一笔全面验证才能确认链路是通的。5.2 容器内连通性验证的完整思路这一条验证思路是我每次部署之后必定会走一遍的。第一步验证 Nginx 是否能访问 Java 服务docker exec -it project-nginx curl http://backend:8080/actuator/health如果你的 Java 项目没引入 actuator直接访问接口地址即可比如curl http://backend:8080/api/products。只要返回了 HTTP 响应码不管 200 还是 404就说明网络通了。第二步验证 Java 服务是否能连接 MySQLdocker exec -it project-backend java -cp /app/app.jar ...这个命令稍显复杂实际排查时我更喜欢直接看 Java 日志。后端能正常启动没有Communications link failure或Access denied之类的异常说明数据库连接成功。第三步验证 Nginx 是否能拿到前端页面curl -I http://localhost/返回 200说明 Nginx 静态资源服务正常。第四步走一个完整的前后端链路curl http://localhost/api/products如果返回 JSON 数据说明 Nginx → Java → MySQL 整条链路都是健康的。5.3 高频问题与根因定位问题一Java 容器报Communications link failure最典型的场景。先检查 MySQL 容器是否健康再确认 Java 服务里的 JDBC URL 用的是mysql服务名而不是localhost或127.0.0.1。如果确认没问题再确认两者是否在同一个 Docker 网络里。docker network inspect project_backend | grep -E project-mysql|project-backend如果发现只有其中一个容器的 IP另一个不在审查 Compose 文件看看是不是某个服务漏写了networks字段。问题二Nginx 日志报host not found in upstream backend:8080这个报错说明 Nginx 启动时解析不到backend这个主机名。原因是 Nginx 不在backend网络里或者 Java 服务确实没起来。先执行docker compose ps看服务状态再检查 Nginx 服务有没有加入backend网络。问题三MySQL 容器启动正常但外部工具连接不上如果你用的是 Navicat、DataGrip 之类的客户端要确认宿主机端口是否映射。上面的编排文件里我默认没暴露 MySQL 端口这是生产安全考虑。如果你想用客户端调试可以在mysql服务里加上ports: - 127.0.0.1:3306:3306只绑定在回环地址这样只有宿主机能访问不被外网扫到。这个127.0.0.1前缀很重要不加的话会绑定到所有网卡云服务器可能暴露到公网。问题四Nginx 转发后接口返回 404先区分 404 是 Nginx 返回的还是 Java 返回的。看响应头里的Server字段如果是nginx/1.24.0说明 Nginx 自己没找到资源如果是 Tomcat 或 Spring Boot 的默认格式说明请求已经到达后端是后端路径不匹配。此时检查proxy_pass末尾有没有多余的/以及后端接口路径是否带/api。问题五docker compose up -d后容器一直重启用docker compose logs --tail 200 service查看具体日志。常见原因还有数据卷权限SELinux/AppArmor 限制导致 MySQL 无法写数据目录。内存不足Java 容器默认的堆内存设置可能超出宿主机可用内存。启动命令异常镜像内默认启动命令与自定义配置冲突。内存问题比较隐蔽尤其在 2GB 的小机器上。如果 Java 容器反复被 OOM Kill可以在 Java 服务里加环境变量控制堆内存environment: JAVA_OPTS: -Xms256m -Xmx512mSpring Boot 项目如果是通过标准脚本启动JVM 会读取这个变量如果是直接用ENTRYPOINT [java, -jar]则需要在 Dockerfile 里配合使用。5.4 关于镜像拉取慢的一些实际处理思路很多人在部署时被镜像拉取速度卡住docker pull mysql:8.0等了半天。这里有几个实际可操作的处理方式为 Docker daemon 配置镜像加速地址修改/etc/docker/daemon.json配置后执行systemctl restart docker。提前在有良好网络条件的机器上把镜像docker pull下来再用docker save导出 tar 包传到服务器上用docker load导入。适合内网环境或服务器网络受限的情况。拉取特定版本时使用更具体的 tag比如mysql:8.0.36有时候比latest小一些而且可重复性更强。另外不要只盯着docker pull命令本身。如果你在云服务器上用 Compose 启动时它会自动拉取docker-compose.yml里引用的所有镜像这个过程是并行的。如果某个镜像拉取失败可以单独先把这个镜像 pull 下来再重新执行docker compose up -d。6. 一次完整的部署流程图解与操作顺序虽然不能用流程图工具但操作顺序按照下面这个顺序来基本上不会出大问题。1. 编写后端 Dockerfile构建后端镜像 2. 构建前端静态资源npm run build生成 dist 目录 3. 编写 Nginx 配置default.conf和 Dockerfile如需镜像内置前端 4. 编写 docker-compose.yml 与 MySQL 初始化脚本 5. 执行 docker compose config 校验语法 6. 执行 docker compose up -d 启动全部服务 7. 观察 docker compose ps 和 docker compose logs 8. 按编译顺序验证Nginx→Java、Java→MySQL、Nginx→前端页面、完整 API 链路 9. 确认无误后配置宿主机防火墙/安全组只放行 80/443 端口这个顺序的核心逻辑先确保每个镜像本身是独立可用的再去做容器间编排。很多新手一上来就写 Compose结果里面引用的镜像还不存在或者 Nginx 配置还是错的错误叠错误到最后无从排查。在步骤 6 执行后如果你用的是image: backend:1.0这种本地镜像注意 Compose 不会自动重新构建它只负责拉取和运行。所以每次改了代码你需要先重新构建镜像docker build -t backend:1.0 . docker compose up -d --force-recreate backend--force-recreate会强制重建 backend 容器让它使用最新构建的镜像。没有这个参数有时候 Compose 会认为配置没变就不重建你会看到代码改了但不生效这个坑也很容易踩。7. 我实测后的一些体会与建议这套架构我实际部署过不止一次每一次都能踩到不同的坑。下面是几个值得写下来的点关于数据库密码与配置管理不要在 Compose 文件里硬编码密码更不要提交到 Git 仓库。一个简单实用的替代方案是用环境变量文件env_file: - .env.mysql.env.mysql文件内容MYSQL_ROOT_PASSWORDroot123456 MYSQL_DATABASEshopdb MYSQL_USERshopuser MYSQL_PASSWORDshop123456.env.mysql加入.gitignore只有自己机器和部署服务器上保留。这样即使仓库被意外公开数据库密码也不会泄露。关于健康检查的细节MySQL 8.0 镜像里的mysqladmin ping即使在用户密码错误时也可能返回mysqld is alive所以健康检查并不能完全代表数据库可用性。如果你需要更严格的检查可以在初始化脚本里建一张healthcheck表然后健康检查命令改为mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e SELECT 1 FROM shopdb.healthcheck;但这样做会增加编排复杂度小型项目用默认的mysqladmin ping就足够了。关于日志管理容器默认的 json-file 日志驱动会不断增长时间久了可能占满磁盘。建议在 Compose 文件里加上日志限制logging: driver: json-file options: max-size: 10m max-file: 3这个配置对三个服务都加上尤其是 Nginx 和 Java它们的访问日志和业务日志量都不小。我见过不少服务器磁盘被容器日志打满的情况加了这个限制之后基本不需要定时去清理。关于升级与回滚保持镜像 tag 可追溯非常重要。不要一直用latest部署时用带版本号的 tag比如backend:1.3.2。当需要回滚时直接修改 Compose 文件指向旧版本然后docker compose up -d即可。镜像本身是不可变的这就是容器的基本特性。最后再分享一个排查思路方面的建议当整条链路不通时我从不用手去猜而是一层一层验证。先确认每个容器单独可用再确认网络互通再确认配置指向正确最后才考虑代码逻辑问题。耐心拆开每一层你很快就知道问题卡在哪个环节明明只花了十分钟就定位了但因为急于求成绕了很多弯。容器化部署的调试方法论本质上就是把这个“分层验证”的原则贯彻到底把它养成习惯部署任何架构都不会再心里没底。
返回列表