docker:基于docker的前端部署方案

容器化部署:前端静态 + Nginx

核心原则:Nginx单独容器,前端dist作为静态资源挂载进Nginx容器;后端另起容器,完全解耦

整体架构(容器版)

复制代码
浏览器 → Nginx容器(静态资源+反向代理API) → 后端服务容器(Java/Go等)
  • Nginx容器职责:托管前端dist、history路由兜底、SSL、限流、请求转发、日志输出
  • 前端不单独起容器!前端只是一堆静态文件,没必要单独容器,这是很多人踩的坑。

方案一:Nginx官方基础镜像,dist用宿主机目录挂载(volume bind mount) ,更新前端只替换宿主机dist文件,nginx -s reload,不用重建镜像、不用重启容器 。

方案二:前端打包进独立容器(Nginx塞在前端镜像里),每次改前端就要重新构建镜像,版本回滚麻烦。

两种容器方案(二选一)

方案1:Bind挂载

宿主机存放前端dist、nginx配置,直接挂载到Nginx容器内。

优点:

  1. 更新前端:只需要堡垒机上传覆盖宿主机dist目录,不需要构建镜像
  2. 回滚简单:宿主机备份dist文件夹,需要回滚直接替换
  3. 配置文件直接在宿主机编辑,方便信息科查看审计
  4. 离线交付简单,镜像只需要提前导出nginx基础镜像

目录规划(宿主机,虚拟机/麒麟服务器)

复制代码
/opt/hospital-web/
├── dist/                # 前端打包产物dist
├── nginx/
│   ├── conf.d/
│   │   └── hospital.conf # 业务nginx配置
│   ├── nginx.conf       # nginx主配置(少量修改)
│   └── ssl/             # 证书目录(HTTPS)
└── logs/                # nginx日志,宿主机持久化(等保要求)
Docker run 启动命令示例
bash 复制代码
docker run -d \
  --name hospital-nginx \
  --restart=always \
  -p 80:80 \
  -p 443:443 \
  # 挂载静态资源
  -v /opt/hospital-web/dist:/usr/share/nginx/html \
  # 挂载自定义配置
  -v /opt/hospital-web/nginx/conf.d:/etc/nginx/conf.d \
  -v /opt/hospital-web/nginx/nginx.conf:/etc/nginx/nginx.conf \
  # 挂载证书
  -v /opt/hospital-web/nginx/ssl:/etc/nginx/ssl \
  # 日志持久化到宿主机
  -v /opt/hospital-web/logs:/var/log/nginx \
  # 安全:禁止特权,使用非root用户(等保重点)
  --user 101:101 \
  --read-only \
  --tmpfs /tmp \
  nginx:1.24-alpine

--user 101:101:nginx默认uid,容器内部不能root运行 ,等保必查项;--read-only只读文件系统,缩小攻击面。

平滑更新前端(非常重要,医院不能断业务)
  1. 宿主机备份旧dist:cp -r /opt/hospital-web/dist /opt/hospital-web/dist_bak_20261009
  2. 上传新dist覆盖
  3. 进入容器校验配置并reload:
bash 复制代码
# 校验nginx配置
docker exec hospital-nginx nginx -t
# 平滑重载,不中断连接
docker exec hospital-nginx nginx -s reload

⚠️ 不要 docker restart,会直接断开用户会话!

方案2:构建自定义Nginx镜像(适合交付包固化,版本严格管控场景)

把dist、nginx配置打包进镜像。

  • 适合:版本强审计,每次发布一个镜像tag,镜像作为交付物;缺点:改前端必须重新build镜像,回滚需要拉旧镜像
    Dockerfile示例
dockerfile 复制代码
# x86: nginx:1.24-alpine
# arm麒麟: nginx:1.24-alpine-arm64
FROM nginx:1.24-alpine
# 关闭版本号,安全头
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./nginx/conf.d /etc/nginx/conf.d
COPY ./dist /usr/share/nginx/html
COPY ./nginx/ssl /etc/nginx/ssl
# 安全:创建普通用户,不root运行
RUN addgroup -g 101 -S nginx && adduser -S -u 101 -G nginx nginx
USER nginx
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]

构建:docker build -t hospital-web:v1.0.0 .

交付方式:docker save hospital-web:v1.0.0 -o hospital-web-v1.0.0.tar,拷贝到医院内网机器 docker load 导入。

缺点:每次前端小改动都要打包镜像,运维不如bind mount方便,而且这样镜像包会比较大。

综合下来,我觉得选方案二的同时,只COPY一个文件index.html到容器中,其他静态资源放到一些静态资源服务器上去,然后当浏览器加载的时候,index.html就会去静态资源服务器去拉取对应的文件。

担心点:医院给我们分配一个新域名,如果域名还是解析到他们本身已有的服务器上了,那么他们那个服务器是否已经有nginx了,如果已经有了,那么我们的nginx就会失效,因为我们也要用80和443端口。想了下应该不存在,访问域名直接就走我们的nginx了,会设置server_name

复制代码
server {
    listen  80;
    server_name cdn.cmvalue.com;

    add_header Access-Control-Allow-Origin *;
    location /repo/web/homepage {
        alias /Users/zhangyu/web/yongliu/homepage/;

        index index.html index.htm index.php;
        autoindex on;

        try_files $uri $uri/ /index.html;
    }
}

推荐:Docker Compose 编排(单机容器,医院最常用)

把Nginx + 后端服务写在compose.yml,一键启停,交付简单。

docker-compose.yml

yaml 复制代码
version: '3.8'
services:
  hospital-nginx:
    image: nginx:1.24-alpine
    container_name: hospital-nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /opt/hospital-web/dist:/usr/share/nginx/html
      - /opt/hospital-web/nginx/conf.d:/etc/nginx/conf.d
      - /opt/hospital-web/nginx/ssl:/etc/nginx/ssl
      - /opt/hospital-web/logs:/var/log/nginx
    user: "101:101"
    read_only: true
    tmpfs: /tmp
    depends_on:
      - hospital-api

  hospital-api:
    image: hospital-api:v1.0.0
    container_name: hospital-api
    restart: always
    # 后端端口,不对外暴露,只给同compose网络内nginx访问
    expose:
      - "8080"
    volumes:
      # 后端日志、数据持久化
      - /opt/hospital-api/logs:/app/logs

启动:docker-compose up -d

重载nginx:docker-compose exec hospital-nginx nginx -s reload

Compose 内部是默认bridge网络,nginx可以直接用服务名 hospital-api:8080 代理后端,不需要宿主机IP,非常方便。

Nginx代理API地址就写 proxy_pass http://hospital-api:8080/

Nginx配置(容器内,history SPA,可直接放进conf.d)

nginx 复制代码
server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;
    server_tokens off;

    # 静态缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2|ttf)$ {
        expires 30d;
        add_header Cache-Control "public,max-age=2592000";
        add_header X-Content-Type-Options nosniff;
    }

    # history路由兜底
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API转发到compose内后端服务
    location /api/ {
        proxy_pass http://hospital-api: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_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

医院容器化重点约束(等保+内网离线,重中之重)

  1. 离线镜像交付

    医院内网隔离,不能pull镜像。提前在外网机器下载对应架构镜像,docker save 导出tar包,拷贝到医院服务器,docker load 导入。

    • x86:nginx:1.24-alpine
    • ARM鲲鹏/飞腾麒麟:nginx:1.24-alpine 拉arm64版本,不要混用x86镜像,会报错。
  2. 安全(等保必查)

    • 容器禁止--privileged特权模式
    • 容器内进程不能root,必须指定普通user运行
    • 日志挂载宿主机,日志留存≥6个月,不能只存在容器内部(容器删了日志丢失,直接等保扣分)
    • 不要开放Docker远程API(2375端口),高危
  3. 网络

    • 后端容器尽量expose,不ports映射到宿主机,仅Nginx暴露80/443;后端服务只能被Nginx访问,缩小攻击面
    • 提前和信息科确认服务器防火墙、堡垒机上传包大小限制
  4. 版本更新与回滚流程(容器bind挂载方案)

    复制代码
    备份dist → 上传新dist → docker exec nginx nginx -t → nginx -s reload
    异常:直接把备份dist覆盖回去,再次reload,回滚几十秒完成

方案对比

方案 适用场景 优点 缺点
Bind挂载Nginx容器(推荐) 绝大多数医院私有化容器交付 前端更新只替换静态文件,无需重制镜像,回滚简单,运维友好 宿主机要管理dist目录
自定义Nginx镜像打包dist 版本强审计,需要镜像作为交付物 交付包完整,环境一致性强 前端改动就要重新build镜像,发布笨重

如果你需要,我可以给你:

  1. 完整可直接交付的离线交付文档(包含镜像导出导入脚本、启停、回滚脚本)
  2. 配套的健康检查脚本(给医院运维用,检测页面/nginx/接口状态)
  3. HTTPS版本完整nginx配置

要不要我顺便写一份离线镜像导出、导入脚本 + 发布/回滚shell脚本?

相关推荐
程序人生8883 小时前
从“把隐私文件上传到别人云端“到本地可控:DocConverter Web 立项初心与架构选型
前端·图像处理·人工智能·opencv·架构·ocr·文心一言
承渊政道3 小时前
自建服务越来越多怎么管理?Docker部署Flare,搭一个自己的数字入口
运维·docker·容器·内网穿透·cpolar·nas·flare
开开心心就好3 小时前
视频里的图片怎么提取?双击一下就导出
java·前端·人工智能·spring·智能手机·intellij-idea·excel
爱喝水的小周3 小时前
实验十 Vue‑Router 王者荣耀战绩查询 APP 开发实践
前端·javascript·vue.js
yexianglunbai3 小时前
Vue 3 全面详解:从核心特性到工程化实践
前端·javascript·vue.js
传人once3 小时前
选择文件按钮样式如何美化
前端·html·css3·jquery·html5
parade岁月4 小时前
告别 Deno 时代?运行时进入一年倒计时,Deno 团队并入 Cloudflare
前端
Lucky_Turtle4 小时前
【HTML】前端页面禁止缩放
前端·html
恋猫de小郭4 小时前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter