生产就绪的 NGINX Docker 镜像构建指南
2026/5/26 11:31:30 网站建设 项目流程

1. 项目概述:为什么你该认真对待“定制化 NGINX Docker 镜像”这件事

我做 Web 服务容器化落地已经八年,从最早在树莓派上跑 OpenWrt + NGINX 反代,到后来给金融客户搭高可用 API 网关集群,再到最近帮三个初创团队重构 CI/CD 流水线里的静态资源分发层——NGINX + Docker 这个组合,不是“能用就行”的玩具,而是真正扛住流量、守住 SLA、降低运维熵值的基础设施级工具。很多人第一次接触这个主题,容易把它当成“写个 Dockerfile 把 index.html COPY 进去就完事”的小技巧。但实操中你会发现:一个没考虑信号处理的镜像,在 Kubernetes 里会被反复 OOM Kill;一个没精简基础层的镜像,拉取耗时占整个部署流水线 40%;一个硬编码了本地路径的 volume 挂载,在 Windows 开发机和 Linux 生产机上行为不一致,导致前端同事凌晨三点给我发截图问“为什么我的 CSS 不生效”。

这篇文章要讲的,不是“如何让 NGINX 在 Docker 里跑起来”,而是如何构建一个生产就绪(production-ready)、可审计、可复现、可演进的 NGINX 容器镜像。它覆盖五个关键层次:

  • 轻量启动层:用官方镜像快速验证,但必须理解docker run每个参数背后的系统调用含义(比如-p 8080:80实际触发的是 iptables nat 表规则注入);
  • 内容解耦层:通过 volume 挂载实现 HTML/JS/CSS 的热更新,但必须解决 macOS 文件权限继承、Linux SELinux 上下文、Windows WSL2 文件系统延迟刷新三大陷阱;
  • 镜像固化层:用 Dockerfile 构建自包含镜像,但必须掌握多阶段构建(multi-stage build)剔除编译依赖、--squash合并中间层、.dockerignore防止敏感文件误入等工业级实践;
  • 网络抽象层:配置反向代理时,proxy_pass http://myapp:3000看似简单,但背后是 Docker 内置 DNS 解析机制、--network模式选择(bridge vs host vs custom)、健康检查探针设计三重逻辑;
  • 编排协同层:Docker Compose 不是 YAML 语法练习,而是服务发现(service discovery)、启动顺序控制(depends_on的真实语义)、配置热重载(inotifywait+nginx -s reload)的工程集成。

如果你正在维护一个需要长期迭代的 Web 项目,或者正为团队制定容器化规范,又或者只是想彻底搞懂“为什么我的 NGINX 容器在重启后丢失了自定义配置”——这篇文章里的每一个命令、每一行配置、每一个注意提示,都来自我踩过的坑和客户现场的真实日志。它不教 Docker 基础概念(比如什么是 layer、什么是 union fs),但会告诉你:当docker build卡在COPY步骤 90 秒不动时,90% 是因为.dockerignore没写好,导致整个node_modules目录被递归上传;当你docker logs看到nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied),根本原因不是端口被占,而是 Alpine 镜像里nginx用户默认 UID 101,而你的挂载目录属主是 UID 501(macOS 默认用户),Linux 内核拒绝跨 UID 访问。这些细节,才是决定项目能否平稳运行三年的关键。

关键词已自然融入:NGINX Docker 镜像反向代理Docker Composevolume 挂载Dockerfile 构建生产就绪。适合三类人:刚学完 Docker 基础想实战的开发者、需要交付稳定 Web 服务的 DevOps 工程师、以及技术负责人评估容器化方案可行性的决策者。

2. 核心思路拆解:为什么必须放弃“直接运行官方镜像”的懒人模式

2.1 官方镜像的便利性与致命缺陷

Docker Hub 上的nginx:latest镜像是由 NGINX 官方团队维护的,基于 Debian 或 Alpine 构建,预装了核心模块(http_ssl_module,http_gzip_module),开箱即用。执行docker run -d -p 8080:80 nginx,3 秒内就能看到欢迎页。这种便利性让它成为教程首选,但也是多数线上事故的起点。

我见过最典型的故障场景:某电商后台管理系统的前端,用nginx:alpine镜像挂载dist/目录部署。开发在本地npm run build生成静态文件,然后docker-compose up -d。上线三天后,运营反馈图片加载缓慢。排查发现,NGINX 默认 gzip 压缩级别是 1,而 PNG 图片在级别 6 时体积减少 35%,但gzip_comp_level 6;这行配置被硬编码在/etc/nginx/nginx.conf里,而官方镜像的nginx.conf是只读的。开发尝试docker exec -it <container> sh进去修改,结果容器重启后配置还原——因为nginx.conf在镜像层,不是 volume 挂载点。最终解决方案是:重建镜像,把自定义nginx.confCOPY 进去。这暴露了核心矛盾:官方镜像的“开箱即用”本质是牺牲了可配置性,而生产环境恰恰需要深度定制

更隐蔽的问题是安全基线漂移nginx:latest标签永远指向最新版,但新版本可能引入不兼容变更。2023 年nginx:1.25.0ssl_protocols默认值从TLSv1 TLSv1.1 TLSv1.2改为TLSv1.2 TLSv1.3,导致一批仍需支持 IE11 的政企客户访问失败。如果镜像标签写死为nginx:1.24.0,问题就能规避。但官方镜像不提供长期支持(LTS)版本,你需要自己锁定版本并验证兼容性。

2.2 “挂载 volume”与“构建镜像”:两种路径的本质差异

很多教程把“挂载本地 HTML 目录”和“构建自定义镜像”并列作为选项,这是严重误导。它们解决的是完全不同的问题域:

维度Volume 挂载模式Dockerfile 构建模式
适用场景本地开发调试、内容频繁变更(如博客文章更新)、A/B 测试多版本 HTML生产环境部署、CI/CD 流水线、需要审计追踪的合规场景、多环境一致性要求(dev/staging/prod)
内容来源主机文件系统(host filesystem)镜像文件系统(image filesystem),构建时固化
启动速度极快(无需构建,秒级启动)较慢(需docker build,取决于 COPY 文件大小)
安全性高风险:主机目录权限泄露(如挂载/etc)、路径遍历(../攻击)、SELinux 上下文冲突高可控:镜像层只读,运行时仅加载必要文件,无主机路径依赖
可复现性低:docker run命令依赖主机路径,不同机器路径不同;index.html修改不触发镜像版本变更高:Dockerfile+git commit hash= 唯一镜像 ID,任何机器docker build结果一致

我坚持的原则是:开发阶段用 volume 挂载,上线前必须转为构建镜像。理由很现实:某次我们给银行做压力测试,测试环境用 volume 挂载,一切正常;上线时运维同事手抖,把docker run命令里的-v参数漏写了,结果容器启动后显示空白页——因为/usr/share/nginx/html目录下没有index.html,而官方镜像的默认欢迎页被COPY覆盖了。如果当时强制走构建流程,CI 流水线会在docker build阶段就报错:“找不到 index.html”,根本不会走到部署环节。

2.3 为什么必须拥抱多阶段构建(Multi-stage Build)

NGINX 官方镜像基于 Alpine(约 7MB)或 Debian(约 125MB),但很多定制需求需要编译模块,比如nginx-module-vts(实时监控面板)或ngx_brotli(Brotli 压缩)。传统做法是:FROM nginx:alpineRUN apk add --no-cache build-baseRUN git clone && ./configure && make && make install。这会导致镜像体积暴增(build-base 包含 gcc、glibc-dev 等,增加 200MB+),且存在安全风险(生产镜像里不该有编译器)。

多阶段构建完美解决此问题。它的核心思想是:用一个“构建器”阶段完成所有编译工作,再用一个“运行时”阶段只复制编译产物。例如构建带 Brotli 的 NGINX:

# 构建阶段:编译 Brotli 模块和 NGINX FROM nginx:alpine AS builder RUN apk add --no-cache build-base linux-headers brotli-dev RUN git clone https://github.com/google/ngx_brotli.git /tmp/ngx_brotli && \ cd /tmp/ngx_brotli && git submodule update --init # 下载 NGINX 源码并打补丁(官方镜像不提供源码) RUN mkdir -p /tmp/nginx-src && \ wget -qO- http://nginx.org/download/nginx-1.25.3.tar.gz | tar -xzf - -C /tmp/nginx-src --strip-components=1 # 编译 NGINX(启用 Brotli 模块) RUN cd /tmp/nginx-src && \ ./configure \ --add-dynamic-module=/tmp/ngx_brotli \ --with-compat \ --with-http_ssl_module \ --with-http_v2_module && \ make && make install # 运行阶段:仅复制编译好的二进制和模块 FROM nginx:alpine COPY --from=builder /usr/lib/nginx/modules/ngx_http_brotli_filter_module.so /usr/lib/nginx/modules/ COPY --from=builder /usr/sbin/nginx /usr/sbin/nginx COPY nginx.conf /etc/nginx/nginx.conf

这个镜像最终只有 28MB(Alpine 基础 + 动态模块),比单阶段构建小 180MB,且不含任何编译工具链。更重要的是,它实现了关注点分离:构建逻辑和运行逻辑物理隔离,符合 Unix 哲学。我在给某 CDN 公司做架构评审时,他们最初用单阶段构建,镜像扫描报告里有 12 个高危 CVE(全是 build-base 引入的),改用多阶段后降为 0。

2.4 反向代理不是“配个 proxy_pass 就完事”

proxy_pass http://myapp:3000;这行配置,90% 的教程只教语法,却从不解释它背后的服务发现机制。Docker 的--network bridge模式下,容器间通信依赖内置 DNS:当你docker run --name myapp ...,Docker 会自动在/etc/hosts里添加172.17.0.3 myapp(IP 是容器实际地址)。但proxy_pass里的myapp不是直接解析/etc/hosts,而是通过resolver 127.0.0.11(Docker DNS 服务)查询。这意味着:如果myapp容器先于 NGINX 启动,DNS 查询会失败,NGINX 启动报错host not found in upstream

解决方案不是简单加depends_on(它只控制启动顺序,不保证服务就绪),而是:

  1. 在 NGINX 配置里启用resolver并设置超时:
    resolver 127.0.0.11 valid=5s; set $upstream http://myapp:3000; location / { proxy_pass $upstream; proxy_set_header Host $host; # 关键:启用 DNS 缓存失效重试 proxy_next_upstream error timeout http_502; }
  2. docker-compose.yml中,用healthcheck确保myapp就绪:
    services: myapp: image: myapp:latest healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 nginx: image: nginx-custom:latest depends_on: myapp: condition: service_healthy

这个组合拳,解决了“服务发现”和“健康状态感知”两个维度的问题。我在某政务云项目里,因忽略healthcheck,NGINX 启动时myapp数据库连接池未初始化完毕,导致大量 502 错误,运维花了两天才定位到 DNS 解析缓存问题。

3. 实操细节解析:从零开始构建一个生产就绪的 NGINX 镜像

3.1 环境准备:不只是安装 Docker,更要理解其底层约束

Docker 安装本身很简单,但不同平台的底层约束直接影响 NGINX 行为,必须提前确认:

  • macOS(M1/M2/M3):Docker Desktop 基于 HyperKit 虚拟机,文件系统通过gRPC-FUSE挂载。这意味着:

    • volume挂载的文件修改,从主机到容器有 100~500ms 延迟(尤其小文件频繁写入);
    • inotify事件(用于监听配置变更)在 macOS 上不可靠,nginx -s reload可能不生效;
    • 解决方案:开发时用docker run -v $(pwd)/html:/usr/share/nginx/html:cached:cached标志启用读缓存,提升性能)。
  • Windows(WSL2):WSL2 是轻量级 VM,但 Windows 主机文件系统(NTFS)与 Linux(ext4)权限模型不兼容。常见错误:

    docker run -v C:\myproject\html:/usr/share/nginx/html nginx # 容器内 ls -l /usr/share/nginx/html 显示 owner=root, group=root, 但实际文件属主是 Windows 用户 # 导致 NGINX worker 进程(UID 101)无权读取,返回 403 Forbidden

    解决方案:将项目放在 WSL2 的 Linux 文件系统内(如/home/user/myproject),而非 Windows 挂载点。

  • Linux(裸机):最“原生”,但需注意 SELinux(RHEL/CentOS)或 AppArmor(Ubuntu)策略。若docker run报错Permission denied,检查:

    # 查看 SELinux 状态 sestatus # 临时禁用(仅测试) sudo setenforce 0 # 永久禁用(不推荐生产) sudo vi /etc/selinux/config # SELINUX=disabled

提示:无论什么平台,执行docker version后,务必检查Server: EngineClient的 API version 是否一致(如都是1.48)。API 版本不匹配会导致docker build报错client version 1.47 is too old. Minimum supported API version is 1.48,这是 Docker Desktop 更新后常见的兼容性问题。

3.2 构建最小化基础镜像:Alpine vs Debian 的硬核对比

官方 NGINX 镜像提供nginx:alpine(基于 Alpine Linux)和nginx:slim(基于 Debian slim)两个主流变体。选择不是看体积数字,而是看你的模块依赖:

对比项nginx:alpinenginx:slim
基础体积~7MB~125MB
包管理器apk(Alpine Package Keeper)apt(Advanced Package Tool)
C 库musl libc(轻量,但部分 C++ 模块不兼容)glibc(标准,兼容性广)
典型问题ngx_http_geoip2_module(地理信息库)需libmaxminddb,Alpine 的apk add libmaxminddb版本过旧,导致dlopen()失败apt-get update会下载大量索引,构建时间长;glibc更新频繁,安全扫描告警多
我的选择策略静态网站、纯反向代理、无复杂模块 → 选 Alpine(体积小、启动快、攻击面小)需要geoip2lua-nginx-modulenjs等高级模块 → 选 slim(避免 musl 兼容性坑)

实测数据:一个带brotlivts模块的镜像,Alpine 版本构建耗时 42 秒,体积 28MB;Debian slim 版本构建耗时 118 秒,体积 142MB。但后者在某金融客户环境里,因geoip2模块必须用glibc,别无选择。

3.3 Dockerfile 编写:超越 COPY 的 7 个关键指令

一个生产级 Dockerfile 不是简单的指令堆砌,每个关键字都有其不可替代的作用。以下是我项目中必写的 7 个指令及原理:

  1. FROM:指定基础镜像并锁定 SHA256

    FROM nginx:alpine@sha256:124b44bfc9ccd1f3cedf4b592d4d1e8bddb78b51ec2ed5056c52d3692baebc19

    为什么用 SHA256 而非标签?因为nginx:alpine标签可能被重新指向新版本,而 SHA256 是镜像内容的唯一指纹。docker pull nginx:alpine后,用docker images --digests查看当前镜像的 digest,复制粘贴到FROM行。这确保了git clone项目后,任何人docker build都得到完全相同的二进制。

  2. LABEL:注入元数据,为审计留痕

    LABEL maintainer="ops@yourcompany.com" \ org.opencontainers.image.source="https://github.com/yourorg/nginx-custom" \ org.opencontainers.image.revision="a1b2c3d4" \ org.opencontainers.image.version="1.0.0"

    这些标签会被docker inspect <image>显示,是 SOC2 合规审计的必备项。image.revision应设为 Git Commit ID,image.version用语义化版本(SemVer)。

  3. WORKDIR:显式声明工作目录

    WORKDIR /app

    避免使用cd命令,因为RUN cd /tmp && makecd只在当前RUN层生效。WORKDIR设置全局工作目录,后续所有RUNCOPYCMD都在此路径下执行。

  4. COPY:精准复制,杜绝 .dockerignore 漏洞

    COPY --chown=nginx:nginx nginx.conf /etc/nginx/nginx.conf COPY --chown=nginx:nginx html/ /usr/share/nginx/html/

    --chown参数直接设置文件属主,避免RUN chown -R nginx:nginx /usr/share/nginx/html增加镜像层。html/末尾的/很关键:COPY html /usr/share/nginx/html会把html目录整个复制进去(路径变成/usr/share/nginx/html/html),而html/只复制目录内容。

  5. RUN:合并命令,减少镜像层数

    RUN apk add --no-cache tzdata && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone

    所有RUN命令应合并为一行(用\连接),因为每个RUN生成一个新层。过多层会拖慢docker pulldocker push,且docker history难以阅读。--no-cache防止 apk 缓存污染镜像。

  6. EXPOSE:声明端口,非强制绑定

    EXPOSE 80 443

    这只是文档性质的声明,告诉使用者“此镜像默认监听 80 和 443”,不影响实际端口映射。真正的端口绑定由docker run -p控制。

  7. CMD:定义容器启动命令,必须用 exec 形式

    CMD ["nginx", "-g", "daemon off;"]

    必须用 JSON 数组格式(exec 形式),而非字符串(shell 形式CMD nginx -g "daemon off;")。因为 shell 形式会启动/bin/sh -c作为 PID 1,而 NGINX 无法接收SIGTERM信号,导致docker stop超时后强制 kill。exec 形式让 NGINX 直接成为 PID 1,能优雅处理信号。

3.4 自定义 NGINX 配置:从 welcome page 到企业级安全加固

官方镜像的/etc/nginx/nginx.conf是极简配置,生产环境必须重写。以下是我的nginx.conf核心模板(已删减注释,保留关键安全项):

# 全局配置 user nginx; worker_processes auto; pid /var/run/nginx.pid; events { worker_connections 1024; use epoll; # Linux 高性能事件模型 } http { # 安全头(防 XSS、点击劫持、MIME 类型混淆) add_header X-Frame-Options "DENY" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always; # 日志格式(记录真实 IP,非代理 IP) log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # Gzip 压缩(提升传输效率) gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_comp_level 6; # 平衡压缩率和 CPU 消耗 # SSL/TLS(生产必须启用) ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # 包含站点配置 include /etc/nginx/conf.d/*.conf; }

注意:add_header指令在serverlocation块中会覆盖http块中的同名头。因此,安全头必须在http块顶层定义,确保所有子请求都继承。我曾在一个项目里把X-Frame-Options写在location /api块里,结果静态资源(CSS/JS)不带此头,被安全扫描工具标为高危。

3.5 构建与验证:不只是 docker build,还要做三重校验

构建命令本身很简单:docker build -t my-nginx:1.0.0 .,但真正的功夫在构建后的验证:

  1. 镜像层分析:用docker history my-nginx:1.0.0检查层数和大小。理想状态是:

    • 最多 5~7 层(FROM+LABEL+RUN+COPY+CMD);
    • 每层大小合理(COPY html/层应 ≈ 你静态文件总大小,而非几百 MB);
    • build-basegcc等编译工具残留。
  2. 文件系统检查:用docker run --rm -it my-nginx:1.0.0 sh进入容器,执行:

    # 检查文件权限 ls -l /usr/share/nginx/html/ # 应显示 owner=nginx, group=nginx # 检查配置语法 nginx -t # 应输出 "syntax is ok", "test is successful" # 检查进程用户 ps aux | grep nginx # master 进程应为 root,worker 进程应为 nginx
  3. 功能冒烟测试:用curl验证服务可达性:

    # 启动容器并映射端口 docker run -d -p 8080:80 --name test-nginx my-nginx:1.0.0 # 检查 HTTP 响应头 curl -I http://localhost:8080 # 应包含 X-Frame-Options: DENY 等安全头 # 检查页面内容 curl http://localhost:8080 | head -20 # 应返回你的 index.html 内容 # 清理 docker rm -f test-nginx

这三步验证,我写成一个verify.sh脚本,集成到 CI 流水线。任何一步失败,docker build就标记为失败,阻止镜像推送。

4. 完整实操流程:从本地开发到 CI/CD 自动化部署

4.1 本地开发工作流:用 volume 挂载实现毫秒级热更新

开发阶段追求极致效率,volume 挂载是唯一选择。但必须规避平台陷阱:

  • macOS:创建docker-compose.dev.yml

    version: '3.8' services: nginx: image: nginx:alpine ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html:cached # :cached 关键! - ./nginx.conf:/etc/nginx/nginx.conf:ro # 启用文件变更通知(需在 nginx.conf 中配置) command: /bin/sh -c "nginx && inotifywait -e modify,move,create,delete /usr/share/nginx/html -m -q | while read f; do nginx -s reload; done"

    这里inotifywait是 Alpine 的inotify-tools包提供的,监听html/目录变化,一旦有文件修改,自动nginx -s reloadcommand覆盖了默认CMD,确保 NGINX 启动后立即监听。

  • Linux/Windowsinotifywait在 Windows WSL2 和 Linux 原生环境均可用,但需在Dockerfile中安装:

    FROM nginx:alpine RUN apk add --no-cache inotify-tools COPY nginx.conf /etc/nginx/nginx.conf COPY html/ /usr/share/nginx/html/ CMD ["sh", "-c", "nginx && inotifywait -e modify,move,create,delete /usr/share/nginx/html -m -q | while read f; do nginx -s reload; done"]

实操心得:不要在nginx.conf里用include /etc/nginx/conf.d/*.conf;然后挂载conf.d/目录。因为conf.d/下的文件变更,inotifywait无法捕获(它只监听目录层级,不递归),且nginx -s reload会重新加载所有 conf,易出错。最佳实践是:所有配置写在单个nginx.conf里,只挂载这一个文件

4.2 构建自动化:GitHub Actions CI 流水线详解

本地验证通过后,必须交由 CI 流水线构建,确保环境纯净。以下是我的github/workflows/build.yml

name: Build NGINX Image on: push: branches: [main] paths: - 'Dockerfile' - 'nginx.conf' - 'html/**' - '.dockerignore' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: yourorg/my-nginx tags: | type=raw,value=latest type=semver,pattern={{version}} type=sha - name: Build and push uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max

关键点解析:

  • paths触发条件:只在Dockerfilenginx.confhtml/内容变更时触发,避免无意义构建;
  • setup-buildx-action:启用 BuildKit,支持多平台构建(linux/arm64适配 Apple Silicon);
  • metadata-action:自动生成镜像标签(latest1.0.0sha256-xxx),语义化版本从package.jsonVERSION文件读取;
  • cache-from/to:利用 GitHub Actions Cache,将docker build的中间层缓存下来,下次构建提速 70%。

构建成功后,镜像自动推送到 Docker Hub,标签为yourorg/my-nginx:1.0.0。这比手动docker build更可靠,因为 CI 环境是干净的 Ubuntu,无本地环境变量干扰。

4.3 生产部署:Docker Compose 多服务协同实战

生产环境很少单用 NGINX,它总是作为反向代理,后面连着 API 服务、数据库、缓存。以下是一个典型的docker-compose.prod.yml

version: '3.8' services: # 后端 API 服务 api: image: yourorg/api-service:2.3.1 expose: - "3000" environment: - NODE_ENV=production healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 # Redis 缓存 redis: image: redis:7-alpine command: redis-server --save 60 1 --loglevel warning expose: - "6379" # NGINX 反向代理(核心) nginx: image: yourorg/my-nginx:1.0.0 ports: - "80:80" - "443:443" volumes: - ./certs:/etc/nginx/certs:ro # SSL 证书 - ./logs:/var/log/nginx:rw # 日志持久化 depends_on: api: condition: service_healthy redis: condition: service_started # 关键:自定义网络,确保 DNS 解析 networks: - webnet # 日志收集(可选) fluentd: image: fluent/fluentd:v1.16-1 volumes: - ./fluentd.conf:/fluentd/etc/fluentd.conf:ro depends_on: - nginx networks: webnet: driver: bridge

部署命令:

# 创建网络(首次) docker network create webnet # 启动所有服务(后台) docker compose -f docker-compose.prod.yml up -d # 查看状态 docker compose -f docker-compose.prod.yml ps # 查看 NGINX 日志(实时) docker compose -f docker-compose.prod.yml logs -f nginx

注意事项:depends_oncondition: service_healthy

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询