20 分钟搞定:Jenkins + Docker 一键流水线,自动构建镜像并部署到远程服务器
Jenkins 后端服务流水线部署脚本
节点设置
此处配置的节点是用来执行脚本中的每一个步骤(stage)的,包括但不限于代码构建、镜像构建/推送等
Groovy
agent any
agent { label 'master' }
- 可通过 agent 设置流水线使用到节点
- any 表示任意节点
- { label 'master' } 表示标签为 master 到节点
- 此处可使用的节点可在节点和云管理查看。查看路径:右上角齿轮图标"系统设置" --> 节点和云管理 --> 节点列表
- master 节点是内置节点,默认没有标签,可在 master 节点详情内 --> 配置从节点设置标签
- 备注:设置节点标签时注意大小写、空格。
环境变量设置
此处配置的环境变量可在整个脚本内使用,将动态的变量设置在此处可形成通用脚本,需要复制流水线时修改此处的变量即可
Groovy
environment {
// Git 仓库配置
GIT_URL = 'git@git.xxx.com:xxx.git' // 替换为你的代码仓库
GIT_KEY_ACR_CRED = 'git-ssh-key'
// Docker 镜像相关配置
DOCKER_IMAGE_REGISTRY = 'registry.xxx.aliyuncs.com' // 替换为你的镜像仓库
DOCKER_IMAGE_ACR_CRED = 'docker-cred'
IMAGE_PRE_NAME = 'xxxx' // 镜像名称前缀
IMAGE_NAME = 'xxx-service' // 替换为你的应用名称
// 远程服务器
REMOTE_HOST = 'xxx.xxx.xxx.xx' // 服务器 IP
REMOTE_HOST_ACR_CRED = 'host-cred'
}
- GIT_URL 代码仓库:存储代码的仓库,用来获取代码至 jenkins
- GIT_KEY_ACR_CRED:访问代码仓库的凭据
- DOCKER_IMAGE_REGISTRY 镜像仓库:存储 docker 镜像的仓库,可拉取/推送镜像
- DOCKER_IMAGE_ACR_CRED:用来访问镜像仓库的凭据
- IMAGE_PRE_NAME:镜像名称前缀
- IMAGE_NAME:应用名称,用来拼接镜像名称
- REMOTE_HOST 远程服务器 IP:我们需要运行服务的服务器 IP
- REMOTE_HOST_ACR_CRED:用来访问远程服务器的凭据
- DOCKER_IMAGE_ACR_CRED、DOCKER_IMAGE_ACR_CRED、REMOTE_HOST_ACR_CRED 需要去凭据管理 添加
- 添加路径:右上角齿轮图标"系统设置" --> 凭据管理 --> 在界面上找到域选一个点进去
- 点击右上角Add Credentials添加
- 类型可选择username with password 、SSH username with private key
- 其中的 ID 就是我们需要的值,可根据喜好设置,不填写会自动生成
构建工具设置
这里设置构建代码的 Maven 工具
Groovy
tools {
// Install the Maven version configured as "M3" and add it to the path.
maven "Maven3"
}
- 这里设置的工具需要在全局工具配置添加
- 添加路径:右上角齿轮图标"系统设置" --> 全局工具配置
- 找到Maven 配置,这里可用设置 maven 本地仓库、settings.xml 文件
- 找到Maven 安装 ,安装 Maven 时注意 Name,这里填写的信息就是脚本中使用的
- 安装 Maven 时可选择自动安装,可选版本。建议版本与开发保持一致
代码检出
拉取仓库中的代码 此操作属于运行在节点的步骤,需要添加在 stages 对象中
Groovy
stage('Checkout') {
steps {
// Get some code from a GitHub repository
git branch: 'master',
url: GIT_URL,
credentialsId: GIT_KEY_ACR_CRED
}
}
- git branch: 'master':用于拉取的分支
- url: GIT_URL
- 设置代码仓库地址,可以使用环境变量中的值,也可直接填写代码仓库字符串**'git@git.xxx.com:xxx.git'**
- credentialsId:用于设置仓库访问凭据
工程构建
使用 Maven 构建 Maven 工程中的 Java 代码 此操作属于运行在节点的步骤,需要添加在 stages 对象中
Groovy
stage('Build') {
steps {
// Run Maven on a Unix agent.
sh "mvn -Dmaven.test.failure.ignore=true clean package"
// To run Maven on a Windows agent, use
// bat "mvn -Dmaven.test.failure.ignore=true clean package"
}
post {
// If Maven was able to run the tests, record the test results and archive the jar file.
success {
archiveArtifacts 'target/*.jar'
}
}
}
- sh "mvn -Dmaven.test.failure.ignore=true clean package"
- 先清理上次构建的产物,然后重新打包
- post 是后置处理器
- success 当这个步骤运行成功后所做的操作
- 此处所做的操作是归档,可在流水线每次的运行记录内Build Artifacts位置找到
构建、推送 Dokcer 镜像
此操作属于运行在节点的步骤,需要添加在 stages 对象中
Groovy
stage('Build and Push Docker Image') {
steps {
script {
// 生成时间戳
def timestamp = new Date().format('yyyyMMddHHmm', TimeZone.getTimeZone('Asia/Shanghai'))
def imageTag = "${IMAGE_NAME}:${timestamp}"
def fullImageNameWithTimestamp = "${DOCKER_IMAGE_REGISTRY}/${IMAGE_PRE_NAME}/${imageTag}"
// 使用宿主机 Docker 构建镜像
echo "Building Docker image: ${fullImageNameWithTimestamp}"
sh "docker build -t ${fullImageNameWithTimestamp} ."
withCredentials([usernamePassword(credentialsId: DOCKER_IMAGE_ACR_CRED, usernameVariable: 'REG_USER', passwordVariable: 'REG_PASS')]) {
// 登录到镜像仓库
echo "Login Docker..."
sh "docker login --username \$REG_USER -p \$REG_PASS \$DOCKER_IMAGE_REGISTRY"
// 推送镜像
echo "Push Docker Image ${fullImageNameWithTimestamp}"
sh "docker push ${fullImageNameWithTimestamp}"
}
// 输出构建信息
echo "Successfully built and pushed image: ${fullImageNameWithTimestamp}"
// 将镜像名称保存到环境变量,供后续步骤使用
env.BUILT_IMAGE = fullImageNameWithTimestamp
}
}
}
- 此处使用当前时间为镜像设置 tag
- 镜像名称格式:<镜像仓库地址>/<镜像名称前缀>/<应用名称>:<当前时间>
- sh "docker build -t ${fullImageNameWithTimestamp} ."
- 使用 docker 构建镜像
- withCredentials 使用凭据登陆、推送镜像
- 登陆仓库: sh "docker login --username REG_USER -p REG_PASS $DOCKER_IMAGE_REGISTRY"
- 推送镜像:sh "docker push ${fullImageNameWithTimestamp}"
- 将镜像名称保存到环境变量,供后续步骤使用:env.BUILT_IMAGE = fullImageNameWithTimestamp
部署到远程服务器
此操作属于运行在节点的步骤,需要添加在 stages 对象中
Groovy
stage('Deploy to Remote Host') {
steps {
withCredentials([usernamePassword(credentialsId: REMOTE_HOST_ACR_CRED, usernameVariable: 'REMOTE_USER', passwordVariable: 'REMOTE_PWD')]) {
sh """
sshpass -p \$REMOTE_PWD ssh -T -o StrictHostKeyChecking=no \
\$REMOTE_USER@${REMOTE_HOST} << 'EOF'
# 去往工作目录
cd /app/${IMAGE_NAME}
# 修改 .env 文件
sed -i '/${IMAGE_NAME}/c\\SERVICE_V=${BUILT_IMAGE}' .env
# 停止目标服务
docker-compose stop ${IMAGE_NAME}
# 删除目标容器
docker-compose rm -f ${IMAGE_NAME}
# 启动目标容器
docker-compose up -d ${IMAGE_NAME}
EOF
"""
}
}
}
- withCredentials:使用凭据连接远程服务器
- 此处使用 sshpass 连接到远程服务器,这里部署 Jenkins 的容器/服务器中需要提前安装 sshpass
- 此处 << 'EOF' ... EOF 不能缺失,末尾的EOF需要独占一行且前方不得有任何字符字符
- cd /app/${IMAGE_NAME}
- 定位至远程服务器的工作目录
- sed -i '/ <math xmlns="http://www.w3.org/1998/Math/MathML"> I M A G E N A M E / c S E R V I C E V = {IMAGE_NAME}/c\\SERVICE_V= </math>IMAGENAME/cSERVICEV={BUILT_IMAGE}' .env
- 修改 docker-compose 的配置文件,将 docker-compose 启动使用的镜像修改为前述步骤推送至仓库的镜像
- 停止服务:docker-compose stop ${IMAGE_NAME}
- 删除目标容器:docker-compose rm -f ${IMAGE_NAME}
- 启动目标容器:docker-compose up -d ${IMAGE_NAME}
脚本源码
Groovy
pipeline {
agent { label 'master' }
environment {
// Git 仓库配置
GIT_URL = 'git@git.xxx.com:xxx.git' // 替换为你的代码仓库
GIT_KEY_ACR_CRED = 'git-ssh-key'
// Docker 镜像相关配置
DOCKER_IMAGE_REGISTRY = 'registry.xxx.aliyuncs.com' // 替换为你的镜像仓库
DOCKER_IMAGE_ACR_CRED = 'docker-cred'
IMAGE_PRE_NAME = 'xxxx' // 镜像名称前缀
IMAGE_NAME = 'xxx-service' // 替换为你的应用名称
// 远程服务器
REMOTE_HOST = 'xxx.xxx.xxx.xx' // 服务器 IP
REMOTE_HOST_ACR_CRED = 'host-cred'
}
tools {
// Install the Maven version configured as "M3" and add it to the path.
maven "Maven3"
}
stages {
stage('Checkout') {
steps {
// Get some code from a GitHub repository
git branch: 'master',
url: GIT_URL,
credentialsId: GIT_KEY_ACR_CRED
}
}
stage('Build') {
steps {
// Run Maven on a Unix agent.
sh "mvn -Dmaven.test.failure.ignore=true clean package"
// To run Maven on a Windows agent, use
// bat "mvn -Dmaven.test.failure.ignore=true clean package"
}
post {
// If Maven was able to run the tests, record the test results and archive the jar file.
success {
archiveArtifacts 'target/*.jar'
}
}
}
stage('Build and Push Docker Image') {
steps {
script {
// 生成时间戳
def timestamp = new Date().format('yyyyMMddHHmm', TimeZone.getTimeZone('Asia/Shanghai'))
def imageTag = "${IMAGE_NAME}:${timestamp}"
def fullImageNameWithTimestamp = "${DOCKER_IMAGE_REGISTRY}/${IMAGE_PRE_NAME}/${imageTag}"
// 使用宿主机 Docker 构建镜像
echo "Building Docker image: ${fullImageNameWithTimestamp}"
sh "docker build -t ${fullImageNameWithTimestamp} ."
withCredentials([usernamePassword(credentialsId: DOCKER_IMAGE_ACR_CRED, usernameVariable: 'REG_USER', passwordVariable: 'REG_PASS')]) {
// 登录到镜像仓库
echo "Login Docker..."
sh "docker login --username \$REG_USER -p \$REG_PASS \$DOCKER_IMAGE_REGISTRY"
// 推送镜像
echo "Push Docker Image ${fullImageNameWithTimestamp}"
sh "docker push ${fullImageNameWithTimestamp}"
}
// 输出构建信息
echo "Successfully built and pushed image: ${fullImageNameWithTimestamp}"
// 将镜像名称保存到环境变量,供后续步骤使用
env.BUILT_IMAGE = fullImageNameWithTimestamp
}
}
}
stage('Deploy to Remote Host') {
steps {
withCredentials([usernamePassword(credentialsId: REMOTE_HOST_ACR_CRED, usernameVariable: 'REMOTE_USER', passwordVariable: 'REMOTE_PWD')]) {
sh """
sshpass -p \$REMOTE_PWD ssh -T -o StrictHostKeyChecking=no \
\$REMOTE_USER@${REMOTE_HOST} << 'EOF'
# 去往工作目录
cd /app/${IMAGE_NAME}
# 修改 .env 文件
sed -i '/${IMAGE_NAME}/c\\SERVICE_V=${BUILT_IMAGE}' .env
# 停止目标服务
docker-compose stop ${IMAGE_NAME}
# 删除目标容器
docker-compose rm -f ${IMAGE_NAME}
# 启动目标容器
docker-compose up -d ${IMAGE_NAME}
EOF
"""
}
}
}
}
}