尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

企业管理系统部署实战:Docker容器化与前后端分离上线全流程

企业管理系统部署实战:Docker容器化与前后端分离上线全流程 项目做到 22 讲这个阶段其实最磨人的已经不是写代码了——而是怎么把写好的东西安稳地跑起来让业务方真正用上。《看潮企业管理软件》从立项到开发一路走到部署上线这个环节我踩过的坑比写业务逻辑多得多。这篇文章就把整个部署过程掰开揉碎从方案选型到实操命令把开发完怎么上线这件事说透。先说这项目是干啥的。看潮是一家典型的贸易型企业业务形态就是采购入库、销售出库、库存盘点、客户对账外加员工权限管理。整个系统说白了就是一套进销存加轻量级CRM的组合。开发阶段我们用到的技术栈主要是一套经典的搭配Spring Boot 作为后端接口层Vue 做前端单页应用MySQL 存业务数据Redis 做缓存和会话管理。整个项目前后端完全分离这也就意味着部署的时候不再是扔一个 WAR 包到 Tomcat 就完事而是要同时处理前端静态资源、后端服务、数据库、缓存这四块内容每一块都要安排得明明白白。1. 项目整体设计与思路拆解1.1 企业管理软件的核心需求到底是什么做企业管理软件和做 To C 产品完全是两个思路。个人 App 可以没那么多条条框框但企业管理软件面对的是真实业务流程一丁点数据错误都可能造成对账不平、库存负数这类严重问题。所以项目的第一个设计原则就是数据结构要严谨。体现在数据库层面我们为所有资金相关字段选择了 DECIMAL 类型而不是 FLOAT因为浮点数在二进制存储下天然存在精度损失搞财务的人看到 0.30000000000000004 这种数字会直接炸毛。第二个设计原则是权限必须清晰。不同岗位看到的界面和能执行的操作完全不一样比如仓库管理员只能做入库出库操作看不到采购成本和毛利财务人员能看到所有资金流水但不能修改商品资料。这个权限模型采用了 RBAC基于角色的访问控制设计用户、角色、菜单权限三张主表加上两张关联表组成一套完整的授权体系。第三个设计原则是操作留痕。每一笔出入库单、每一次价格调整、每一次删除操作系统都要求写入操作日志表。这不是为了刁难程序员而是企业管理的刚需——一旦出现业务纠纷系统要能还原谁在什么时间做了什么操作的完整证据链。1.2 数学逻辑在业务模块里的落脚点编程与数学这个系列标题不是硬贴上去的这个项目里确实有几个模块离了数学就转不起来。最典型的是库存成本核算。企业进货的价格会随采购批次变化比如第一次进某商品单价 50 元第二次进同款商品单价变成了 55 元那出库的时候成本到底按哪个算我们用的是移动加权平均法。逻辑是这样的每次入库后计算一次新的平均单价计算公式是当前库存总成本 新入库成本/当前库存数量 新入库数量。比如第一次入库 100 件单价 50 元库存在成本就是 5000 元第二次入库 100 件单价 55 元那新的加权单价就是 (5000 5500) / (100 100) 52.5 元。后续出库成本都按 52.5 元计算直到下一次入库再重新计算单价。销售统计模块也用到了不少统计学的知识。比如计算月度销售趋势我们用到了基础的聚合计算SUM 求总销售额、COUNT 求订单量、AVG 求客单价。更复杂一点的是同比环比分析同比是今年某月与去年同月比环比是本月与上月比计算公式为本期值 - 上期值/ 上期值 × 100%这些都是纯数学运算落到 SQL 语句里。优惠折扣模块更是条件逻辑和数值计算的组合。我们的促销规则是满 1000 元打 95 折、满 5000 元打 9 折、满 10000 元打 85 折再加上会员等级对折扣的额外加成。这个模块的核心逻辑其实是一个分段函数用代码实现就是多重条件判断加上金额累加运算每一步都在做数学推导。1.3 为什么部署方案最终选择了 Docker这可能是整个项目部署环节最重要的一个决策。刚开始团队内部有分歧有人主张传统方式比如直接在服务器上装 JDK、MySQL、Nginx然后用 systemd 管理进程有人提出用宝塔面板图形界面操作简单直观。但最终我们统一选择了 Docker 加 docker-compose 的方案。这个决策背后的逻辑并不复杂。第一团队有多个开发环境每个人的电脑系统不一样有人是 Windows有人是 macOS有人主力机装的是 Linux 发行版。如果不用容器化就会出现经典的在我电脑上是好的这类问题而 Dcker 镜像构建一次所有环境跑出来的行为完全一致。第二项目已经拆成了多个服务后端、前端、MySQL、Redis 四个组件各跑各的用 docker-compose 一条命令就能全部拉起不再需要一个个手写 systemd 单元文件。第三将来无论是迁移服务器还是做集群扩展容器化的优势会非常明显。2. 开发阶段的核心细节与部署前的准备工作2.1 配置文件中那些必须做对的参数部署的第一步不是打包而是把项目配置文件里的每一项参数都核查清楚。后端项目用到了application-prod.yml作为生产环境专用配置这里面的细节值得逐条说。首先是数据库连接配置不能照搬开发环境的 localhost 地址。在 Docker 网络里服务名就是主机名所以 MySQL 的地址应该写成容器名而不是 IP。比如spring: datasource: url: jdbc:mysql://mysql-container:3306/kanchao_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: kanchao password: ${DB_PASSWORD}这里的serverTimezoneAsia/Shanghai是个极其关键的参数。如果漏掉这个Java 连接 MySQL 时会默认使用服务器的时区如果服务器设置的是 UTC那所有写入数据库的时间都会比北京时间少 8 小时。数据进去之后业务方看到的每一笔订单时间都是错的这种问题排查起来非常隐蔽。Redis 配置同理redis-container对应的是 Docker 网络中的 Redis 服务spring: redis: host: redis-container port: 6379 password: ${REDIS_PASSWORD}数据库密码和 Redis 密码我们没有写死在配置文件里而是用了环境变量DB_PASSWORD和REDIS_PASSWORD占位。这样做的考虑是配置仓库不应该存明文密码一旦代码仓库泄露生产环境的数据库就等于裸奔。环境变量在 docker-compose 文件中通过${DB_PASSWORD}引用宿主机上的.env文件来注入。前端项目这一侧需要修改的是 API 请求地址。开发环境下 Vue 项目通常通过 devServer 的 proxy 代理解决跨域问题但生产环境是静态文件部署没有 Node 代理了。我们在.env.production里配置VUE_APP_BASE_API https://erp.kanchao.example.com/api打包时所有 axios 请求都会走这个完整地址由 Nginx 统一做反向代理分发到后端服务。2.2 前端构建阶段要用生产模式打包前端部署的第一个大坑就是直接用开发模式跑生产环境。Vue 项目在开发模式下有很多额外的资源消耗完整的错误提示、未压缩的代码、热更新的状态管理钩子——这些都严重影响性能。生产部署必须执行npm install npm run build这条命令会触发 vue-cli-service 的构建流程最终在项目根目录生成一个dist文件夹里面是所有静态资源。这个文件夹里通常有两个关键文件index.html作为入口页面static或assets目录存放 JS、CSS、图片等资源。构建过程中有个细节值得注意那就是路由模式。如果前端项目用的是 history 模式路由URL 中不带#号那么 Nginx 必须配置 try_files 回退到 index.html否则用户刷新子页面时会出现 404。我们项目用的就是 history 模式所以部署时 Nginx 的配置文件里有一条location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }这一条配置的意思是当 Nginx 收到一个请求路径先尝试找对应的物理文件找不到的情况下回退到 index.html由前端路由接管处理。少了这一行你点进商品管理页刷新一下页面直接白屏报 404 Not Found。2.3 数据库初始化与数据迁移的两种方式项目部署到一台全新服务器上数据库不会自己冒出来需要初始化。我们总结了两种可行方案。第一种是最直接的从开发环境把整个数据库导出为 SQL 文件然后在服务器上导入。mysqldump -u root -p kanchao_db kanchao_db_backup.sql然后把这份 SQL 文件上传到服务器执行mysql -u root -p -e CREATE DATABASE kanchao_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -u root -p kanchao_db kanchao_db_backup.sql注意数据库字符集要显式指定utf8mb4否则默认字符集可能是 latin1中文数据存储会乱码。utf8mb4相比utf8还要多支持生僻字和 emoji 表情企业管理软件的用户名里偶尔会出现特殊字符用utf8mb4才是最稳妥的。第二种方式是使用 Flyway 这类数据库迁移工具好处是可以自动管理表结构升级。但我们这次没有采用原因是项目已经开发完成表结构基本稳定没有持续迭代的需求直接导入 SQL 文件就能跑简单粗暴最省事。如果项目还要二期三期持续开发建议尽早引入 Flyway把每次表结构变更固化为一个版本脚本多人协作时不会出现漏改导致的不一致问题。3. 部署环境准备与完整安装步骤3.1 服务器选型和基础环境配置这个项目需要一台什么配置的服务器我直接给结论以看潮这种规模的企业管理系统为例初期用户量几十到一两百人并发规模不会很高一台 2核4G 的云服务器绰绰有余。如果考虑到未来三年的数据增长和业务扩展4核8G 会更从容一点尤其是报表模块需要做大量聚合查询CPU 多几个核在导出月度报表时能够明显缩短等待时间。操作系统推荐 Ubuntu 22.04 LTS 或 Debian 12这两者都自带较新的内核和软件源而且 Docker 官方对它们的支持是最完善的。拿到服务器之后建议先做一次系统更新apt update apt upgrade -y然后安装 Docker 和 docker-compose 插件。这里我不推荐用apt install docker.io因为源里的版本可能比较旧。官方推荐的安装方式是这样的curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh装完之后验证一下版本docker --version docker compose version看到输出有版本号说明安装成功。这一步算是整套部署流程的地基地基稳不稳直接决定后面所有操作顺不顺畅。3.2 项目文件的上传与目录规划服务器准备好之后需要把项目的相关产物传到服务器上。这里说的产物包括三块内容后端打好的 JAR 包、前端构建出的 dist 目录、以及我们准备的 docker-compose.yml 和 Dockerfile。目录规划很重要建议在服务器上建立一套清晰的项目目录避免将来文件散落各处找不到mkdir -p /opt/kanchao/{backend,frontend,nginx,mysql-data,logs}目录结构解释一下/opt/kanchao/backend存放后端 Dockerfile 和 JAR 包/opt/kanchao/frontend存放前端 Dockerfile 和 dist 目录/opt/kanchao/nginx存放 Nginx 自定义配置/opt/kanchao/mysql-data挂载 MySQL 数据目录确保容器销毁后数据不丢/opt/kanchao/logs挂载 Nginx 日志目录方便排查问题文件上传可以用 scp 命令行工具scp target/kanchao-server.jar root服务器IP:/opt/kanchao/backend/ scp -r dist/* root服务器IP:/opt/kanchao/frontend/没有特殊要求的话用 scp 最简单直接。如果文件数量特别多或者有断点续传的需求也可以安装 lrzsz 传单个文件或者用 rsync 同步目录。3.3 四个 Docker 镜像的构建与配置后端服务的 Dockerfile 是这次部署的重点。我们基于 OpenJDK 官方镜像来构建但有个细节值得注意JAR 包的体积通常不小如果把构建和运行放在同一个镜像阶段最终镜像会非常大。项目里我们采用了多阶段构建的方式FROM maven:3.8-openjdk-11 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:11-jre-slim WORKDIR /app COPY --frombuilder /app/target/kanchao-server.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar, --spring.profiles.activeprod]这个 Dockerfile 写了两个 FROM 指令第一个阶段用 Maven 镜像完成编译打包第二个阶段只复制编译产物。好处是最终镜像里没有完整的 Maven 环境和源码体积从 800MB 降到大约 200MB部署时传输更快启动也更快。前端镜像更简单直接用 Nginx 官方镜像把构建好的 dist 目录复制进去FROM nginx:1.24-alpine COPY dist /usr/share/nginx/html COPY default.conf /etc/nginx/conf.d/default.conf WORKDIR /usr/share/nginx/html EXPOSE 80MySQL 和 Redis 不用自己构建镜像直接用官方镜像即可但需要注意版本固定。MySQL 建议用 8.0 系列镜像Redis 用 7.0 以上版本。镜像版本不锁定会导致什么后果举个例子如果使用了mysql:latest过一段时间官方更新镜像你重启容器拉取到了新版本结果可能因为默认认证插件变化导致应用连不上。3.4 docker-compose 编排所有服务所有服务都准备好之后用 docker-compose 一次性编排这些服务。在/opt/kanchao/目录下创建docker-compose.yml文件version: 3.8 services: mysql: image: mysql:8.0 container_name: kanchao-mysql restart: always environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: kanchao_db MYSQL_USER: kanchao MYSQL_PASSWORD: ${DB_PASSWORD} volumes: - /opt/kanchao/mysql-data:/var/lib/mysql - /opt/kanchao/backend/mysql-init:/docker-entrypoint-initdb.d ports: - 3306:3306 command: --character-set-serverutf8mb4 --collation-serverutf8mb4_general_ci networks: - kanchao-net redis: image: redis:7.0 container_name: kanchao-redis restart: always command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - /opt/kanchao/redis-data:/data ports: - 6379:6379 networks: - kanchao-net backend: build: ./backend container_name: kanchao-backend restart: always environment: DB_HOST: mysql DB_PASSWORD: ${DB_PASSWORD} REDIS_HOST: redis REDIS_PASSWORD: ${REDIS_PASSWORD} ports: - 8080:8080 depends_on: - mysql - redis networks: - kanchao-net frontend: build: ./frontend container_name: kanchao-frontend restart: always ports: - 80:80 depends_on: - backend volumes: - /opt/kanchao/nginx/logs:/var/log/nginx networks: - kanchao-net networks: kanchao-net: driver: bridge这里有一个分批启动顺序的考量depends_on只保证 mysql 和 redis 容器先启动但不保证数据库已经完全初始化完成接受连接。Spring Boot 应用启动时如果发现连不上数据库启动会失败。我们的应对方案是 Spring Boot 的依赖重试机制。在application-prod.yml中配置了连接失败重试参数实际处理中将spring.datasource.hikari.initialization-fail-timeout调整为 60000 毫秒这样即使 MySQL 初始化慢了点应用也能等一等再重试连接而不是瞬间就放弃启动。3.5 Nginx 作为前端入口的反向代理配置Nginx 在这个架构里承担了两个角色静态文件服务器和反向代理服务器。请求进来后先到 Nginx如果是/api开头的路径就转发给后端 8080 端口其余请求直接由 Nginx 返回前端静态文件。default.conf的完整配置如下server { listen 80; server_name erp.kanchao.example.com; client_max_body_size 50m; 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; } location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } }client_max_body_size 50m这行不是随便设置的。企业管理软件里有导入 Excel、上传产品图片的功能如果用的是 Nginx 默认的 1MB 上传限制导入超过 1MB 的 Excel 就直接报 413 错误业务方会以为系统坏了。我们就把这个值调到了 50MB完全能覆盖日常使用场景。特别要注意的是proxy_pass http://backend:8080;这里的backend是 docker-compose 中定义的服务名。在 Docker 网络内部容器之间通过服务名互相访问所以这里不能写 localhost。如果你写成了http://localhost:8080Nginx 会尝试连接自己容器内的 8080 端口而那个端口上根本没有服务结果必然是 502 Bad Gateway。4. 一键部署与项目启动的全流程实操4.1 环境变量配置和上线前的最后一次检查在正式启动之前需要在/opt/kanchao/目录下创建.env文件写入所有环境变量MYSQL_ROOT_PASSWORDKanChaoRoot2024 MYSQL_DATABASEkanchao_db DB_USERNAMEkanchao DB_PASSWORDKanchaoDb2024 REDIS_PASSWORDKanchaoRedis2024这些密码信息写入.env文件之后要注意文件权限。建议修改为仅 root 用户可读写chmod 600 /opt/kanchao/.env启动前的最后检查清单基本是这四项服务器端口是否被占用ss -tlnp | grep -E 80|3306|6379|8080防火墙是否开放相关端口ufw status或云控制台安全组配置JAR 包和 dist 目录是否齐全ls -lh /opt/kanchao/backend/*.jar /opt/kanchao/frontend/dist/.env文件里的密码是否与业务方约定的一致建议在启动前把所有镜像先拉取到本地避免启动时遭遇网络问题docker pull mysql:8.0 docker pull redis:7.0 docker pull nginx:1.24-alpine4.2 启动命令与初始化日志观察所有前置工作完成后在/opt/kanchao/目录下执行docker compose up -d --build-d参数表示后台运行--build会强制重新构建本地镜像。首次执行时docker-compose 会按依赖顺序启动服务。执行完可以用docker compose ps查看所有容器的状态正常的输出应该是四个容器全部处于 Up 状态。然后观察后端启动日志重点看 Spring Boot 的启动过程有没有异常docker logs -f kanchao-backend日志中出现类似下面这样的一行说明后端启动成功了Started KanchaoApplication in 25.321 seconds (JVM running for 26.012)接着验证 Nginx 是否正常工作docker logs -f kanchao-frontend如果看到 Nginx 成功启动的日志没有报配置错误就可以打开浏览器访问服务器 IP 地址了。首次打开会看到系统登录页输入管理员账号密码能正常跳转到首页那部署这一步就算完成了。4.3 HTTPS 证书配置让系统访问更安全刚部署好的系统直接用 HTTP 访问这个状态在生产环境是不能接受的。企业管理软件里有大量敏感业务数据账号密码如果以明文在网络上传输安全隐患非常大。我们给域名配置了免费 HTTPS 证书用的是 Lets Encrypt 方案。在宿主机上安装 Certbotapt install certbot python3-certbot-nginx -y然后执行证书签发命令certbot --nginx -d erp.kanchao.example.comCertbot 会自动检查并修改 Nginx 配置来启用 HTTPS。修改完之后的 server 块会多出 443 端口的监听和证书路径。签发成功后需要在原来的 Nginx 配置里加一条 HTTP 自动跳转 HTTPS 的规则server { listen 80; server_name erp.kanchao.example.com; return 301 https://$host$request_uri; }修改完 Nginx 配置后重启容器docker compose restart frontend这里有个细节要提醒每次重启容器原来 Nginx 镜像里的配置会被容器内的配置覆盖。因为我们把配置放在了镜像构建阶段所以在修改配置之后需要重新构建镜像再启动docker compose up -d --build frontend这样才能确保新配置生效。5. 部署过程中踩过的坑与排查实录5.1 数据库时区引起的八小时时间偏差第一次部署完成后测试人员反馈系统里所有订单创建时间都比实际时间早了 8 小时。当时第一反应是服务器时区不对检查了宿主机的时间发现没问题又是北京时间。后来排查到问题出在两个层面。第一层是 MySQL 容器的时区Debian 基础镜像默认使用 UTC 时区数据写入时就偏移了 8 小时。解决方法是 MySQL 启动命令加上时区参数command: --default-time-zone8:00 --character-set-serverutf8mb4 --collation-serverutf8mb4_general_ci第二层是 Java 应用连接数据库时也要显式指定时区。JDBC URL 中必须带上serverTimezoneAsia/Shanghai。这两个配置配合起来时间才能完全对齐。提醒一下排查这类时间问题时建议先分别查询应用服务器系统时间和数据库中的NOW()返回值快速定位是哪一层出现了偏移。5.2 容器进程 OOM 导致整个服务频繁重启项目运行了大概一周后运维反馈说系统偶尔会卡顿几分钟查看容器状态发现后端容器被反复重启。查了日志发现关键报错是Native memory allocation (mmap) failed to map 1048576 bytes for committing reserved memory这是典型的容器内存溢出。问题的根源是我们的 JVM 默认堆内存设置。服务器是 4G 内存MySQL 配置了 1.5G 的缓冲池Redis 再占几百 MBJVM 如果按照默认策略动态分配可能在高峰时把系统内存全部占满。解决方案是在 Dockerfile 里显式指定 JVM 内存参数ENTRYPOINT [java, -Xms512m, -Xmx512m, -jar, app.jar, --spring.profiles.activeprod]-Xmx512m限制 JVM 最大堆内存为 512MB-Xms512m让堆内存一开始就分配到位避免运行时频繁扩容申请内存导致的性能抖动。同时调整 MySQL 的innodb_buffer_pool_size从默认值调低到 512MB给系统留出足够的冗余。调整完之后容器运行稳定再也没有出现过 OOM 问题。5.3 前端页面刷新就白屏问题出在路由模式前段对话提到过 history 路由模式的回退问题这里详细说说我们实际踩坑的过程。前端部署完成后用户从登录页进到系统首页点击菜单跳转各项功能都正常。但用户按 F5 刷新当前页面的时候浏览器直接白屏报 404 Not Found。原因分析Nginx 收到/product/list这个路径请求后先在站内查找对应的物理文件product/list发现不存在就默认返回 404。但实际上这个路径是前端路由不是真实的文件路径应该把请求交给前端的入口页面index.html去处理。解决方式就是前面写的try_files语句location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }这个语法结构解释起来很清楚先尝试按原路径找文件找不到再尝试按目录方式找默认文件还找不到就回退到 index.html。实测配置完刷新页面一切恢复正常。如果前端用了 hash 模式路由URL 中带#号这个问题根本不会出现因为#后面的内容不会发送到服务器。但 history 模式看起来更专业所以我们的项目还是选择保留这个模式把 Nginx 配置配好。5.4 图片上传成功但访问 404系统上线后用户反馈商品图片上传功能时好时坏有时传完能看到图片有时传完点击图片却是 404。排查后发现问题出在我们当时把上传的文件保存在了应用容器内部的临时目录里。容器日常状态正常时文件还在但一旦容器因为发布更新或意外崩溃被重建容器内部的非持久化目录就会全部消失上传的图片跟着灰飞烟灭。解决方案是在 docker-compose 中把宿主机的一个目录挂载到后端容器内部backend: volumes: - /opt/kanchao/upload:/app/uploadSpring Boot 的配置文件中把上传路径改为/app/uploadfile: upload-dir: /app/upload同时 Nginx 增加一条静态资源映射让图片能直接通过 URL 访问location /upload/ { alias /opt/kanchao/upload/; }这样一来图片文件保存在宿主机上即使容器重建数据依然完好。这次排查经历给我们团队提了个醒任何需要持久化的数据都不能只放在容器内部。数据库用到了 volume 挂载Redis 用了 volume 挂载现在文件上传也改成了 volume 挂载三个容易丢数据的地方都堵住了。6. 运维日常与备份机制的完善6.1 日志查看和容器健康状态监控系统上线之后不是就结束了日常运维要跟上。日志查看用的最多的三个命令再拿出来说说。看后端实时日志docker logs -f --tail 200 kanchao-backend只看最近一段时间的错误日志docker logs --since 1h kanchao-backend | grep ERROR看一下所有容器的资源占用情况docker stats曾经遇到过 Nginx 容器假死的情况表现是服务器负载很低但页面打开超时。后来通过 docker stats 看到 Nginx 容器的 CPU 和内存都很正常但连接数堆积。这类问题排查到最后发现是后端服务响应变慢导致连接被阻塞Nginx 等待后端返回数据等不到。所以日常监测不能只看容器本身状态要结合业务请求成功率来综合判断。6.2 数据库备份脚本和恢复验证数据是企业管理软件的生命线数据库备份必须做到自动化。我们写了一个简单的备份脚本放在宿主机上通过 crontab 每天凌晨自动执行#!/bin/bash BACKUP_DIR/opt/kanchao/backup DATE$(date %Y%m%d_%H%M%S) docker exec kanchao-mysql mysqldump -u root -p$MYSQL_ROOT_PASSWORD kanchao_db | gzip $BACKUP_DIR/kanchao_db_$DATE.sql.gz find $BACKUP_DIR -name *.sql.gz -mtime 30 -delete脚本的逻辑很简单用 mysqldump 导出数据库并压缩然后删除 30 天前的备份文件。在 crontab 里加入定时任务0 2 * * * /opt/kanchao/backup.sh每天凌晨两点执行备份。备份脚本可以但恢复演练也同样重要。备份文件如果从来没有恢复测试过真出问题的时候等于没有备份。建议每季度做一次恢复演练解压备份文件在临时环境里导入检查关键表的数据量确认数据完整。这项工作在纸上看着繁琐真到数据库崩溃那天会庆幸自己演练过。6.3 日常平滑发布更新的操作流程项目上线后肯定还会有小的功能迭代或 bug 修复。平滑发布更新的流程我们总结成了固定的一套操作步骤分享出来供参考。第一步本地或 CI 环境打包新版本的 JAR 包和前端 dist 目录。第二步上传产物到服务器替换旧文件scp target/kanchao-server.jar root服务器IP:/opt/kanchao/backend/ scp -r dist/* root服务器IP:/opt/kanchao/frontend/第三步优先重启后端docker compose up -d --build backend docker logs -f kanchao-backend观察启动日志确认没有报错后再重建前端容器docker compose up -d --build frontend最后用浏览器访问系统走一遍核心流程——登录、查询商品、创建订单验证功能正常后再让业务方正式使用。这个顺序是为了避免前后端不同步后端先更新兼容旧前端前端再更新指向新接口。反过来操作的话新前端页面带着新接口调用但后端还是旧版本没有这些接口用户直接报错。部署这件事看起来就是把几个容器拉起来但真正做下来才会发现每一层都有值得打磨的细节。从配置文件的参数到目录规划再到备份机制每一步都需要沉下心来。这个项目的部署过程我收获最大的不是记住多少条命令而是建立了一套排查思维出问题先分层定位网络层、服务层、数据层逐层排除不靠猜来解决问题。希望这次的分享能帮正在做企业软件部署的同行们少走一些弯路把精力花在真正有价值的事情上。
返回列表