Jenkins pipeline + Docker + harbor部署GO项目(保姆级教程)

本项目代码已经上传到github, 点击跳转

大家好,我是甜苦瓜。
之前的文章有讲过Jenkins怎么部署Go项目点击跳转,不过那种部署是基于二进制文件的方式来部署,今天要分享的是基于Docker来部署。

目标:

  1. 创建基于ginweb项目
  2. 编写Dockerfile,编写生成镜像的逻辑
  3. 编写Jenkinsfile(安装相关插件),编写推送镜像和运行容器的逻辑
  4. 配置Jenkins pipeline风格的自动化构建
  5. 配置Jenkins参数化构建

1. 部署流程图

和直接二进制部署的方式不同,本次采用的是Docker部署的方式,部署的流程如下:
在这里插入图片描述
从上面的图不难看出,整体的部署逻辑还是十分清晰的,Jenkins主要负责的就是自动化构建:

  1. 拉取git仓库最新的代码
  2. 编译并打包项目,使其成为一个镜像
  3. 将镜像推送到Docker仓库
  4. 通知目标服务器拉取最新的镜像并运行容器

实际上的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 可以看到返回的内容。

到这里,一个简单的基于ginWeb项目就搭建好了。下面开始自动化部署

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 “参数说明”

  1. --file:指定Dockerfile文件的路径
  2. -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

  1. -d: 后台运行
  2. -p: 端口映射,将容器的8181端口映射到宿主机的18181端口
  3. –name: 容器的名称
  4. 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,直接原神!启动!
在这里插入图片描述
如果不出错的话,就会看到下面的页面
在这里插入图片描述
同时也可以去HarborWeb页面检查镜像是否上传成功
在这里插入图片描述
最后登录远端服务器检查镜像是否拉取成功,容器是否启动成功
在这里插入图片描述
到此,所有的步骤都已经完成,一个简单的Go项目已经成功部署到了远端服务器上。

总结

其实搞懂部署的流程比操作Jenkins本身要重要。至于大家是选择用Jenkinsfile来部署,还是说直接Shell命令/脚本来执行这些自动化部署流程,看个人习惯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一根甜苦瓜

祝老板身体棒棒,夜夜笙歌

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值