Nginx+Docker容器化部署网站的完整实践指南

来源:PostgreSQL教程作者:刘卫东头衔:网络博主
导读:本期聚焦于刘卫东创作的《Nginx+Docker容器化部署网站的完整实践指南》,敬请观看详情。把网站跑在Docker容器里,再由Nginx统一接入流量,已经成为中小型项目上线的主流做法。这种方式的好处很直观:环境隔离、迁移方便、回滚快速,配合docker-compose还能一条命令拉起整套服务。本文将从镜像构建讲起,演示如何编写Dockerfile打包前端静态资源和后端应用,再配置Nginx反向代理实现域名到容器的转发,同时覆盖HTTPS证书挂载、负载均衡、日志持久化以及常见踩坑点的排查思路,帮你把一套可维护的容器化部署方案完整落地。

传统部署方式里,我们通常把Nginx直接装在服务器上,网站文件扔进/var/www,改完配置执行nginx -s reload就算上线。这种方式在单机、单个项目时没什么问题,但一旦服务器上要跑多个项目,或者需要频繁在测试环境和生产环境之间迁移,环境不一致带来的麻烦就会接踵而至。Docker的出现让部署这件事变得标准化:把网站和它依赖的一切打包成镜像,任何一台装了Docker的机器都能原样运行。这篇文章就来完整梳理Nginx与Docker结合部署网站的思路、配置和容易踩的坑。

Nginx+Docker容器化部署网站的完整实践指南

一、整体架构思路:Nginx放在容器内还是容器外

动手之前先要回答一个架构问题:Nginx自己要不要也容器化?两种方案各有适用场景。第一种是把Nginx跑在宿主机上,作为所有流量的统一入口,通过端口映射把请求转发到各个业务容器。这种方式的好处是Nginx配置修改起来直接,SSL证书管理集中,排查问题时不用进容器,适合服务器上项目较多、需要灵活调整路由规则的场景。

第二种方案是Nginx也打包成容器,和业务容器一起由docker-compose编排,整站可以做到一键启动、一键销毁。这种方式环境一致性最好,换一台服务器只需要docker compose up -d就能把整套服务拉起来,特别适合需要频繁迁移或者做CI/CD流水线的项目。下面我们以第二种方案为主线展开,因为它更能体现容器化部署的完整价值。

典型的架构链路是:外部请求进入宿主机80或443端口,先到达Nginx容器,Nginx根据域名或路径规则把请求转发给前端静态资源目录、或者后端应用容器对应的内部网络地址。容器之间通过自定义的Docker网络通信,用服务名代替IP,避免了硬编码地址带来的维护问题。

二、编写Dockerfile打包网站

假设我们要部署一个前后端分离的项目:前端是Vue编译出的静态文件,后端是一个Node.js的API服务。前端镜像的Dockerfile可以这样写:

# 前端静态站镜像
FROM nginx:1.25-alpine

# 删除默认配置,换上自己的
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/site.conf

# 把构建产物复制进镜像
COPY dist/ /usr/share/nginx/html/

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

这里有几个细节值得注意。基础镜像选择nginx:1.25-alpine而不是完整版,体积从一百多MB缩小到二十几MB,拉取和启动都更快。最后一行的daemon off;是关键,它让Nginx在前台运行,因为容器的生命周期取决于主进程,如果Nginx以守护进程方式运行,主进程立即退出,容器也会跟着停止。

后端服务的Dockerfile思路类似,核心是把运行环境和代码一起固化进镜像:

# 后端API镜像
FROM node:20-alpine

WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY . .

EXPOSE 3000
CMD ["node", "server.js"]

注意COPY package*.jsonnpm install要放在复制源码之前,这是利用Docker的层缓存机制:只要依赖清单没变,重新构建时就不会重新安装依赖,构建速度能快好几倍。这是新手最容易忽略的优化点。

三、用docker-compose编排整套服务

单靠Dockerfile只能启动一个容器,多个容器之间的网络、依赖关系、启动顺序要靠docker-compose来管理。下面是一份可直接使用的编排文件:

version: "3.8"

services:
  web:
    build: ./frontend
    container_name: mysite-web
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./certs:/etc/nginx/certs:ro
      - weblogs:/var/log/nginx
    depends_on:
      - api
    networks:
      - appnet

  api:
    build: ./backend
    container_name: mysite-api
    expose:
      - "3000"
    volumes:
      - apilogs:/app/logs
    restart: unless-stopped
    networks:
      - appnet

networks:
  appnet:
    driver: bridge

volumes:
  weblogs:
  apilogs:

这份配置里有几个设计决策需要解释。API容器用的是expose而不是ports,意味着3000端口只在容器网络内部可见,不映射到宿主机,外部流量必须经过Nginx这一层,安全性更好。Nginx容器挂载了证书目录用来支持HTTPS,日志则通过命名卷持久化,即使容器被删除,日志数据依然保留,方便事后排查。

两个容器处于同一个自定义网络appnet,Docker内置的DNS会把这个网络里的服务名解析成对应容器IP,所以Nginx配置里可以直接写proxy_pass http://api:3000,不需要关心容器的实际IP是什么,容器重建后IP变了也不影响。

四、Nginx反向代理与HTTPS配置

容器内Nginx的核心工作是两件事:对外提供静态文件服务,对内把API请求代理给后端容器。下面这份配置覆盖了常见需求:

server {
    listen 80;
    server_name www.ipipp.com;
    # 全部跳转HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name www.ipipp.com;

    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    # 静态资源
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    # API请求转发给后端容器
    location /api/ {
        proxy_pass http://api:3000/;
        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;
    }

    # 开启gzip压缩
    gzip on;
    gzip_types text/css application/javascript application/json;
    gzip_min_length 1024;
}

try_files $uri $uri/ /index.html这一行对前端单页应用至关重要。Vue、React这类框架使用前端路由,用户直接访问/user/profile这样的深层路径时,服务器上并不存在对应文件,没有这条回退规则就会返回404。把它回退到index.html后,由前端路由接管页面渲染。

代理API时别忘了那几行proxy_set_header。不设置X-Real-IPX-Forwarded-For,后端拿到的客户端IP永远是Nginx容器的内网地址,做访问统计、限流或者风控时数据就全错了。如果后端使用WebSocket,还需要额外加上UpgradeConnection两个头,并把超时时间调大,否则连接会频繁断开。

五、常见踩坑点与排查思路

容器化部署Nginx最常见的报错是502 Bad Gateway,多半是代理的目标地址写错了。排查时先在Nginx容器里执行ping api确认DNS能否解析,再用wget -qO- http://api:3000验证后端服务是否存活。如果服务名解析失败,检查两个容器是否在同一个网络里;如果解析正常但连不上,看后端容器日志确认应用是否真的监听了0.0.0.0而不是127.0.0.1,后者会导致容器内服务只接受本机连接。

第二个高频问题是挂载配置不生效。很多人把宿主机的nginx.conf挂载进容器后忘记执行docker exec mysite-web nginx -s reload,或者挂载的是单个文件而主配置里没有include对应目录,导致修改被静默忽略。建议挂载整个配置目录而不是单个文件,并养成改完配置用nginx -t先做语法检查的习惯。

第三个坑是端口冲突。宿主机的80端口如果已经被其他程序占用,容器启动时会直接报port is already allocated。用ss -lntp | grep 80查一下占用者,要么停掉它,要么把容器映射到其他端口。另外时区问题也容易被忽视:alpine镜像默认是UTC时区,日志时间戳会差八小时,在Dockerfile里加一句RUN apk add --no-cache tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime即可解决。

最后补充一点运维层面的建议:镜像构建完成后记得打上版本标签,比如mysite-web:20240601,不要长期依赖latest标签。有了明确的版本号,发布出问题时一条docker compose down加回退旧镜像就能快速回滚,这也是容器化部署相比传统方式最实在的收益之一。

NginxDocker容器化部署修改时间:2026-09-03 10:49:23

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49512.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。