本项目代码已经上传到github, 点击跳转
大家好,我是甜苦瓜。
之前的文章有讲过Jenkins怎么部署Go项目点击跳转,不过那种部署是基于二进制文件的方式来部署,今天要分享的是基于Docker来部署。
目标:
- 创建基于
gin的web项目 - 编写
Dockerfile,编写生成镜像的逻辑 - 编写
Jenkinsfile(安装相关插件),编写推送镜像和运行容器的逻辑 - 配置
Jenkins pipeline风格的自动化构建 - 配置
Jenkins参数化构建
1. 部署流程图
和直接二进制部署的方式不同,本次采用的是Docker部署的方式,部署的流程如下:

从上面的图不难看出,整体的部署逻辑还是十分清晰的,Jenkins主要负责的就是自动化构建:
- 拉取
git仓库最新的代码 - 编译并打包项目,使其成为一个镜像
- 将镜像推送到
Docker仓库 - 通知目标服务器拉取最新的镜像并运行容器
实际上的CI/CD流程比上面的部署方案要更自动化,比如代码提交到远端代码仓库后,Jenkins会自动触发构建任务,同时运行所有单元测试,集成测试,更新Swagger文档等。这里只是一个简单的部署方案。
其核心逻辑就是 代码 -> 镜像 -> 容器 的转变
当然本文为了节省成本,就只弄了2台服务器,(将开发服务器和build服务器合二为一了)。如下图:

2. 创建Gin项目
创建一个Golang项目,名字为docker-build,然后控制台执行下面命令,初始化go mod文件
cd 目录目录/docker-build
go mod init docker-build
# 创建main.go文件
touch main.go
此时,项目的目录结构如下:
.
├── go.mod
└── main.go
在main函数中写入以下代码,使用gin框架实现一个简单的web服务
package main
import "github.com/gin-gonic/gin"
func main() {
engine := gin.Default()
engine.GET("hello", func(ctx *gin.Context) {
ctx.String(200, "你干嘛~~~~~诶哟~~~~!!!")
})
engine.Run(":8181")
}
打开控制台输入 go mod tidy 同步相关依赖,然后执行 go run main.go 启动服务,浏览器访问 http://localhost:8181/hello 可以看到返回的内容。
到这里,一个简单的基于
gin的Web项目就搭建好了。下面开始自动化部署
3. 编写Dockerfile
1. 基本Dockerfile
在项目根目录下创建 Dockerfile文件,并写入下面内容:
FROM golang:1.21-alpine
# 为我们的镜像设置必要的环境变量
ENV GO111MODULE=on \
GOPROXY=goproxy.io \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# 设置build目录
WORKDIR /build
# 拷贝项目文件 将项目所有文件全部拷贝到当前目录下(容器的/build目录下)
COPY . .
# 下载相关依赖
RUN go mod tidy
# 编译成可执行文件,其文件名为hello 编译的文件为 main.go
RUN go build -o hello main.go
# 暴露端口
EXPOSE 8181
CMD ["./hello"]
需要注意的是,Dockerfile中的go版本要和 go.mod 文件中的版本对应上,如下图:

先在本地打包一个镜像测试一下,使用命令为 docker build . --file=./Dockerfile -t hello:v1.0.1
!!! note “参数说明”
--file:指定Dockerfile文件的路径-t:指定镜像的名称和版本号
经过漫长的等待后,不出意外打包成功,使用 docker images | grep hello 可以查看到刚才打包成功的镜像。如下
% docker images | grep hello
hello v1.0.1 f8c9d5c53896 37 seconds ago 598MB
不难看出,一个简单的 gin 项目打包后的镜像竟然有598MB????这有点离谱。先不管他了,来看看那能不能跑起来先。
根据刚才的镜像启动容器,docker run -d -p 18181:8181 --name hello-container hello:v1.0.1
- -d: 后台运行
- -p: 端口映射,将容器的
8181端口映射到宿主机的18181端口 - –name: 容器的名称
- hello:v1.0.1: 镜像名称
然后我们curl测试一下(用浏览器访问也行)
$ docker ps | grep hello-container
9757b0981d63 hello:v1.0.1 "./hello" 42 seconds ago Up 41 seconds 0.0.0.0:18181->8181/tcp hello-container
$ curl localhost:18181/hello
你干嘛~~~~~诶哟~~~~!!!%
自此,本地的gin项目已经成功打包成一个docker镜像,并且成功运行在容器中。那么接下来的工作就是将所有步骤依靠Jenkins来实现自动化。但是在自动化部署之前,先来对容器进行瘦身,毕竟一个hello接口镜像就有500多MB,说不过去
2. 容器瘦身
具体瘦身的方法有很多,这里我们采用多阶段构建的方式来实现。其核心原理是在一个镜像中构建项目,然后在另一个镜像中运行项目。这样就可以避免在运行时带上构建工具和源代码等。
下面是一个多阶段构建的Dockerfile文件
# 阶段一: 打包阶段
FROM golang:1.22-alpine as builder
# 为我们的镜像设置必要的环境变量
ENV GO111MODULE=on \
GOPROXY=goproxy.io \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# 设置build目录
WORKDIR /build
# 先复制go.mod和go.sum文件,下载依赖
COPY go.mod ./
COPY go.sum ./
RUN go mod download
# 再复制其他代码文件
COPY . .
# 编译成可执行文件,其文件名为hello 编译的文件为 main.go
RUN go build -ldflags "-s -w" -o hello main.go
## 阶段二 : 运行阶段
FROM alpine:3.14
# 设置工作目录
WORKDIR /app
# 从阶段一中复制二进制文件到这个新的阶段
COPY --from=builder /build/hello /app
# 暴露端口
EXPOSE 8181
CMD ["./hello"]
其中
go build -ldflags "-s -w"可以去掉二进制文件的调试信息,进一步缩小编译后二进制文件的体积。
再次打包镜像,命令为docker build . --file=./Dockerfile -t hello:v1.0.1,(这里为了方便比对,指定版本为v1.0.2)。打包结束,查看镜像大小,如下:
$ docker images | grep hello
hello v1.0.2 e85092a41dd9 4 seconds ago 12.6MB
hello v1.0.1 f8c9d5c53896 43 minutes ago 598MB
很明显,镜像大小从598MB降到了12.6MB!

4. 创建pipeline
在操作Jenkins之前,确保已经安装了下面的插件
- Pipeline
- Pipeline Stage
- Pipeline Groovy
- Git Prarmeter


在项目的配置页面中,勾选 This project is parameterized,然后添加一个 Git Parameter 参数,如下图:

然后填写相关参数,这里建议直接照抄(如果你默认分支名不是main可以改,其他的全抄)

点击【高级】,并填写过滤条件,如下:

选择添加【参数】,点击【String Parameter】

填写参数名称,如下:

在项目的配置页面中,选择 Pipeline,然后在 Definition 选择 Pipeline script from SCM,然后填写相关参数

这里填入项目的git地址,然后选择Credentials,需要注意的是分支名称一定要写 ${branch},这样才能在Jenkinsfile中获取到分支名称。

最后取消勾选轻量检出,至于Script Path,代表了Jenkinsfile文件的相对路径,由于我们的Jenkinsfile 就在项目的根目录下,所以这里填写 Jenkinsfile即可。

保存就创建成功了,接下来就是要在项目的根目录下创建`Jenkinsfile·了,他是自动部署的主要逻辑
5. 编写Jenkinsfile
在编写Jenkins之前,先去Harbor仓库下创建一个项目名为deploy-demo,如下图:

关于harbor,的安装我会出一个博客。
在项目的根目录下创建一个Jenkinsfile文件,然后写入下面的内容:
pipeline{
agent any
environment {
IMAGE_NAME = 'hello'
CONTAINER_NAME = 'hello-app'
HARBOR_URL = '填写harbor仓库的地址'
HARBOR_PROJECT = 'deploy-demo'
// Docker tag
TAG = "v1.0.1"
// Docker 用户名和密码(可以通过 Jenkins 凭据管理)
DOCKER_USER = 'harbor账号'
DOCKER_PASS = 'harbor密码'
//
}
stages{
stage("打包镜像"){
steps {
echo '开始打包'
sh 'docker build --no-cache -t ${IMAGE_NAME} -f ./Dockerfile .'
echo "打包成功"
}
}
stage("推送到harbor"){
steps {
// 登录harbor镜像仓库
sh "docker login -u ${DOCKER_USER} -p ${DOCKER_PASS} ${HARBOR_URL}"
// 给镜像打标签
sh "docker tag ${IMAGE_NAME} ${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG}"
// 推送到harbor
sh "docker push ${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG}"
// 登出
sh "docker logout ${HARBOR_URL}"
}
}
stage ("远端服务器拉取镜像"){
steps {
sh "ssh 用户名@IP地址 'docker login -u ${DOCKER_USER} -p ${DOCKER_PASS} ${HARBOR_URL} '"
sh "ssh 用户名@IP地址 linlin@124.223.67.129 'docker pull ${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG} '"
}
}
stage("停止远端服务器上的旧容器"){
steps {
script{
def containerId = sh(script: "docker ps -q -f name=${CONTAINER_NAME}", returnStdout: true).trim()
if (containerId) {
echo "Found running container with ID: ${containerId}. Stopping and removing it."
// stop and remove the old container
sh "docker stop ${CONTAINER_NAME}"
sh "docker rm ${CONTAINER_NAME}"
} else {
echo "No running container found with name: ${CONTAINER_NAME}."
}
echo "Old container stopped and removed."}
}
}
stage("远端服务器启动新容器"){
steps {
sh "ssh 用户名@IP地址 'docker run -d --name ${CONTAINER_NAME} -p 18181:8181 ${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG} '"
}
}
}
post{
always{
echo 'always say goodbay'
// sh "docker rmi ${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG} || true"
}
}
}
接着我们 git add commit pull push 素质四连,将Jenkinsfile的修改推送到远端仓库,然后回到浏览器上的Jenkins,直接原神!启动!

如果不出错的话,就会看到下面的页面

同时也可以去Harbor的Web页面检查镜像是否上传成功

最后登录远端服务器检查镜像是否拉取成功,容器是否启动成功

到此,所有的步骤都已经完成,一个简单的Go项目已经成功部署到了远端服务器上。
总结
其实搞懂部署的流程比操作Jenkins本身要重要。至于大家是选择用Jenkinsfile来部署,还是说直接Shell命令/脚本来执行这些自动化部署流程,看个人习惯。
&spm=1001.2101.3001.5002&articleId=142936984&d=1&t=3&u=0628fabff4054649b002c30ac23fdad0)
1787

被折叠的 条评论
为什么被折叠?



