我的自动化部署实践

引言

说起部署这件事,估计很多开发同学都有一把辛酸泪。记得刚工作那会儿,每次上线都跟打仗似的——凌晨三四点盯着屏幕,手抖着敲命令,生怕一个不小心就搞挂生产环境。有时候改了个小 bug,光是部署就要折腾半天,测试环境、生产环境来回倒腾,累得半死不说,还经常出各种幺蛾子。

后来痛定思痛,我决定好好折腾一下自动化部署。这一搞就是好几年,中间踩了无数坑,也总结了不少经验。今天就聊聊我是怎么做自动化部署的,希望能给有同样困扰的同学一些参考。

那些年被部署支配的恐惧

刚工作的时候,我们团队的部署流程大概是这样的:开发本地测完没问题,然后手动打包成 war 包或者 jar 包,用 FTP 传到服务器,登录服务器停止服务、替换文件、启动服务。听起来很简单对吧?但实际操作中问题一堆:

- 经常漏传某个依赖包,导致启动失败

- 多台服务器要手动同步操作,有一次忘了在一台机器上更新,结果用户访问到的还是旧版本

- 回滚特别麻烦,需要手动找之前的备份

- 没有任何日志记录,出了问题都不知道该查哪里

最崩溃的是有一次上线,刚好遇到服务器磁盘满了,部署到一半卡住,上不去下不来,硬是折腾到早上八点才搞定。那一刻我就下定决心,一定要把这破流程改掉。

选型:踩过的那些坑

一开始我尝试用 Jenkins,不得不说这玩意儿功能确实强大,但配置起来也是真的复杂。各种插件版本兼容问题、权限配置、节点管理,搞得人头大。而且 Jenkins 那个界面,怎么说呢,比较有年代感……

后来试了 GitHub Actions,体验还不错,配置简单,跟 GitHub 集成也很紧密。但问题是我们的代码仓库在公司内部的 GitLab 上,用起来不太方便。

最后选来选去,我们决定用 GitLab CI。原因很简单:本身就是用 GitLab 托管代码,集成度高,而且 CI/CD 配置直接写在项目里的 .gitlab-ci.yml,版本控制也方便。

当然,如果你用的是 GitHub,GitHub Actions 也是很好的选择。关键是要选一个适合自己团队的工具,别盲目追求"最强大",适合自己的才是最好的。

我的 CI/CD 流程设计

说了这么多,来看看我现在的部署流程大概是什么样子。

首先在项目根目录创建一个 .gitlab-ci.yml 文件,内容大概是这样的:

stages:

- build

- test

- deploy

build:

stage: build

image: maven:3.8-openjdk-17

script:

- mvn clean package -DskipTests

artifacts:

paths:

- target/*.jar

expire_in: 1 hour

test:

stage: test

image: maven:3.8-openjdk-17

script:

- mvn test

coverage: '/Total.*?([0-9]{1,3})%/'

deploy_staging:

stage: deploy

script:

- ssh deploy@staging-server "cd /app && docker-compose pull && docker-compose up -d"

only:

- develop

environment:

name: staging

url: https://staging.example.com

deploy_production:

stage: deploy

script:

- ssh deploy@production-server "cd /app && docker-compose pull && docker-compose up -d"

only:

- main

environment:

name: production

url: https://example.com

when: manual

这个配置定义了三个阶段:构建、测试、部署。代码推送到 develop 分支会自动部署到测试环境,而推送到 main 分支则需要手动触发生产环境部署——这个 when: manual 真的很重要,至少给你一个后悔的机会。

Docker 与 Kubernetes 的应用

说到部署,不得不提 Docker。这玩意儿简直是部署界的救星,把应用和依赖打包成一个镜像,走到哪儿都能跑,再也不用担心"在我机器上能跑"的问题了。

我们项目用的是 Spring Boot + Vue,前后端分离。前端用 Nginx serve,后端打成 jar 包用 Docker 运行。docker-compose.yml 大概是这样的:

version: '3.8'

services:

backend:

build: ./backend

ports:

- "8080:8080"

environment:

- SPRING_PROFILES_ACTIVE=prod

- DB_HOST=postgres

depends_on:

- postgres

- redis

frontend:

build: ./frontend

ports:

- "80:80"

depends_on:

- backend

postgres:

image: postgres:14

volumes:

- pgdata:/var/lib/postgresql/data

environment:

- POSTGRES_PASSWORD=secret

redis:

image: redis:7-alpine

volumes:

pgdata:

这样一套下来,新同事入职只需要装好 Docker 和 Docker Compose,clone 代码,敲一行 docker-compose up -d,整个系统就跑起来了再也,不用花半天时间配环境。

至于 Kubernetes,之前也尝试过一段时间,但说实话,对于我们这种小团队来说,Kubernetes 的学习成本和运维成本都有点高。Docker Compose 已经能满足我们的需求,等团队规模大一些再考虑上 K8s。

一些小心得

最后分享几个我觉得比较有用的经验:

1. 做好监控和告警。部署成功不代表万事大吉,要监控应用健康状态、错误日志、接口响应时间等指标。我们用的是 Prometheus + Grafana + 钉钉告警,效果还不错。

2. 保留回滚能力。每次部署前自动打一个镜像标签,出了问题一键回滚到上一个版本,这个功能救过我的命。

3. 部署日志要完整。谁在什么时候部署的、部署的是哪个版本、部署过程中有没有错误,这些信息都要记录下来,方便出问题的时候排查。

4. 小步迭代。不要想着一口气把自动化做得十全十美,先把最痛的点解决掉,比如自动打包、自动部署测试环境,然后再逐步完善。

总结

自动化部署这件事,说难也不难,说简单也不简单。关键是要迈出第一步,然后不断迭代优化。现在我们团队每次部署基本不需要人工干预,推送代码后 CI 自动跑完测试、构建镜像、部署到对应环境,整个过程可能就几分钟的事。

虽然前期需要花时间搭建和踩坑,但长远来看省心太多了。最重要的是,终于不用熬夜部署了,头发也少掉了几根(误)。

如果你还在被手动部署折磨,不妨试试从今天开始搭建第一套自动化流程,哪怕只是自动打包、自动测试,也比纯手动强太多。祝各位部署顺利,永无 bug!