如何用Jenkins和Docker,提升发布效率
第一部分:我们曾经的发布方式
在我工作生涯中,我发现很多团队都经历过这样令人头疼的发布场景:
代码靠传来传去:开发在自己电脑上打包好程序,通过QQ或U盘发给我。
环境不一致的情况:经常出现“在我电脑上明明是好的,怎么一到服务器上就跑不起来了?”的问题。
手动操作,又慢又险:我需要登录一台台服务器,手动上传、解压、停止旧程序、启动新程序。整个过程耗时超过1小时,而且精神高度紧张,生怕哪个步骤出错。
深夜加班成常态:为了不影响用户,发布通常安排在深夜,费时费力。
我意识到,这种模式必须改变。我的目标很简单:让发布变成一件一键式的、自动化的、谁都能看懂的事情。
第二部分:我的解决方案:搭建一条自动化生产线
我设计的这套自动化流程,就像一条工厂里的流水生产线。代码是“原材料”,最终线上运行的服务就是“成品”。这条生产线主要由以下几个站点构成:
调度中心(CI/CD Server): Jenkins
负责指挥整个流程。当有新代码时,它会按顺序调用其他工具干活。
为什么选它? Jenkins是业界最老牌、最稳定的自动化工具,像个经验丰富的老师傅,插件特别多,想让它干啥几乎都能找到对应的工具。
代码仓库 (Code Repository): GitLab / GitHub
存放我们所有的程序代码。
关键作用:它能通知Jenkins——“有代码提交了,快开始工作”
集装箱技术(Containerization): Docker
它把我们的Java程序,连同运行它所需要的所有环境(比如JDK),一起打包成一个标准的Docker镜像。
核心优势: 彻底解决了“在我电脑上是好的”这个问题,保证了开发、测试、生产环境的绝对一致。
集装箱仓库 (Image Registry): Harbor / Docker Hub
Jenkins打包好后,就把镜像推到这里。
最终目的地 (Target Server): 生产服务器
最终运行我们程序的地方。Jenkins会告诉这台服务器:“去仓库里拉最新的‘集装箱,然后运行起来”
流程图如下

开发者 git push 代码到 GitLab。
GitLab 自动触发 Jenkins。
Jenkins 从 GitLab 拉取最新代码。
Jenkins 调用 Maven (构建工具) 把代码编译打包成一个JAR文件。
一个叫 Dockerfile 的说明书
Jenkins 调用 Docker,读取书”,把JAR文件和Java环境一起打包成一个 Docker镜像。
Jenkins 把打包好的 Docker镜像 推送到 Harbor 镜像仓库。
Jenkins 通过SSH远程登录到 生产服务器。
生产服务器 从 Harbor 拉取最新的镜像,并启动新的Docker容器,替换掉旧的。
第三部分:一步步带你走完流程
下面,我以一个叫 user-service 的Java项目为例,展示这条流水线是如何工作的。
首先,我们需要在项目代码里创建一个名为 Dockerfile 的文件,告诉Docker如何打包我们的程序。
# Dockerfile
# 1. 基础环境:选择一个包含Java 8的环境作为基础
FROM openjdk:8-jre-slim
# 2. 作者信息(可选)
LABEL maintainer="Your Name <your.email@example.com>"
# 3. 工作目录:在容器里创建一个叫 /app 的文件夹
WORKDIR /app
# 4. 复制文件:把我们用Maven打包好的JAR文件(比如 target/user-service.jar),复制到容器的 /app 目录下
# 并重命名为 app.jar
COPY target/user-service.jar app.jar
# 5. 暴露端口:告诉Docker,我们的程序会使用8080端口
EXPOSE 8080
# 6. 启动命令:当容器启动时,执行 `java -jar app.jar` 这个命令来运行我们的程序
ENTRYPOINT ["java", "-jar", "app.jar"]这份文件就像一份菜谱,简单明了,定义了我们应用的标准运行方式。
我们在Jenkins的界面上,创建一个“流水线 (Pipeline)”任务,然后配置几个关键步骤:
源码管理 (Source Code Management):
选择 Git。
填入我们项目的GitLab地址:http://gitlab.mycompany.com/app/user-service.git
设置触发器:选择“当有代码推送到GitLab时自动构建”。
构建步骤 (Build Steps):
这是核心部分。我们不需要写复杂的代码,只需要在Jenkins的脚本框里,填入类似下面这样清晰的命令:
// Jenkins Pipeline Script (简化版)
pipeline {
agent any
stages {
stage('1: 拉取代码') {
steps {
echo "开始拉取代码..."
git branch: 'main', url: 'http://gitlab.mycompany.com/app/user-service.git'
}
}
stage('2: Java打包') {
steps {
echo "开始用Maven打包..."
// `sh`表示执行一个shell命令
sh 'mvn clean package'
}
}
stage('3: 构建并推送Docker镜像') {
steps {
script {
// 定义镜像的名字和标签。用构建号作标签,简单又实用。
def imageName = "harbor.mycompany.com/app/user-service:${env.BUILD_NUMBER}"
echo "开始构建Docker镜像: ${imageName}"
// build an image
sh "docker build -t ${imageName} ."
echo "登录Harbor仓库..."
// 登录Harbor(密码已在Jenkins凭证中安全存储)
sh 'docker login harbor.mycompany.com -u admin -p $HARBOR_PASSWORD'
echo "推送镜像..."
// push the image
sh "docker push ${imageName}"
}
}
}
stage('4: 部署到生产服务器') {
steps {
script {
def imageName = "harbor.mycompany.com/app/user-service:${env.BUILD_NUMBER}"
echo "开始远程部署..."
// 通过SSH远程执行命令(密码/密钥已在Jenkins凭证中安全存储)
// ssh 'user@production-server-ip' "..."
sh '''
ssh user@192.168.1.100 "
echo '正在拉取最新镜像...'
docker pull ''' + imageName + '''
echo '停止并删除旧容器...'
docker stop user-service-container || true
docker rm user-service-container || true
echo '启动新容器...'
docker run -d --name user-service-container -p 8080:8080 ''' + imageName + '''
echo '部署完成!'
"
'''
}
}
}
}
}第四部分:成果与改变
这套自动化CI/CD流水线成功上线后,为我们的团队带来了脱胎换骨的改变,这些改变是实实在在、可以量化的:
效率飞跃: 发布时间从超过1小时锐减至5分钟,实现了真正的“一键发布”。
可靠性飙升: 自动化流程消除了99%的人为失误,发布引发的线上故障几乎绝迹。
协作模式升级: 开发者可以自主、安全地进行发布,运维不再是研发流程中的“瓶颈”,整个团队的协作更加流畅。
对我个人而言,最大的收获并不仅仅是搭建了一套系统,而是角色的转变。
我终于可以从无尽的、重复的手动部署工作中抽身,将宝贵的精力投入到更具价值和挑战性的领域,比如:
优化系统监控,让问题在发生前就被预警。
研究成本控制,为公司节省云资源开销。
探索更先进的架构,让系统更加稳定和高可用。