Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Jenkins+K8s实现Go后端服务自动化部署

Jenkins+K8s实现Go后端服务自动化部署教程

作者:码刀攻城

本文详细介绍了使用Jenkins和Kubernetes(K8s)实现Go后端服务的自动化部署流程,涵盖前期环境准备、Jenkins流水线配置、K8s资源编排、部署验证、常见问题排查以及相关技术拓展和优化建议,旨在提升开发和运维效率,保障服务的高可用性

在云原生时代,Kubernetes(K8s)已成为容器化应用编排的标准,而Jenkins作为老牌持续集成/持续部署(CI/CD)工具,能完美与K8s结合,实现应用从代码提交到服务上线的全流程自动化。

本文将聚焦Go语言后端程序,详细讲解如何通过Jenkins+K8s搭建自动化部署体系,实现代码拉取、编译、镜像构建、镜像推送与K8s滚动更新的一体化流程,同时补充相关技术细节与拓展知识,让零基础的开发者也能快速上手。

一、整体部署流程概述

与Java、前端服务的自动化部署逻辑一致,Go后端服务基于Jenkins+K8s的自动化部署核心围绕容器化自动化流水线展开,整体流程分为5个核心步骤,相比Java的Maven打包、前端的Node打包,Go服务采用原生编译方式生成可执行文件,更轻量、更适配容器环境。

整个流程实现了开发提交代码→Jenkins自动触发流水线→服务自动部署到K8s的闭环,大幅提升部署效率,减少人工操作的失误。

二、前期环境准备

在开始搭建流水线前,需要确保基础环境已部署完成,这是自动化流程的前提,核心环境包括以下几类:

关键环境配置:Jenkins连接K8s与Harbor

三、Go后端服务项目基础准备

本次以一个简单的Go Web服务为例,演示整个部署流程,项目结构简洁,包含核心业务代码、Dockerfile,确保能正常编译和容器化。

3.1 简单的Go Web服务代码

项目目录结构:

go-demo/
├── main.go       # 核心业务代码
├── go.mod        # Go模块依赖
├── go.sum
└── Dockerfile    # 构建Docker镜像的配置文件

go.mod文件(模块初始化)

module go-demo
go 1.21  # 建议使用稳定版本,与编译环境一致
require github.com/gin-gonic/gin v1.9.1  # 以Gin框架为例,轻量Web框架

main.go文件(简单的Web接口)

实现一个健康检查接口/health和测试接口/hello,方便后续验证服务部署是否成功:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	// 初始化Gin引擎
	r := gin.Default()
	// 健康检查接口,K8s存活探针可调用
	r.GET("/health", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"status":  "success",
			"message": "service is healthy",
		})
	})
	// 测试接口
	r.GET("/hello", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "hello, Jenkins+K8s Go demo!",
		})
	})
	// 监听8080端口
	_ = r.Run(":8080")
}

3.2 编写Dockerfile:构建Go服务镜像

Go语言的一大优势是编译后为单可执行文件,无依赖,因此可以采用多阶段构建的Dockerfile,大幅减小镜像体积。多阶段构建分为编译阶段运行阶段,编译阶段使用包含Go SDK的镜像,运行阶段使用轻量的基础镜像(如alpine、scratch)。

Dockerfile(多阶段构建,推荐)

# 第一阶段:编译阶段,使用官方Go镜像作为基础镜像
FROM golang:1.21-alpine AS builder
# 设置工作目录
WORKDIR /app
# 复制go.mod和go.sum,先下载依赖(利用Docker层缓存,避免每次编译都重新下载)
COPY go.mod go.sum ./
RUN go mod download
# 复制所有源码到工作目录
COPY . .
# 编译Go代码:生成Linux环境的64位可执行文件,关闭CGO,减小体积
# CGO_ENABLED=0:关闭CGO,生成静态链接的可执行文件
# GOOS=linux:目标系统为Linux
# GOARCH=amd64:目标架构为amd64
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o go-demo main.go

# 第二阶段:运行阶段,使用轻量的alpine镜像(比ubuntu小很多,约5MB)
FROM alpine:3.18
# 安装基础依赖(如时区,可选)
RUN apk add --no-cache tzdata
# 设置时区为上海
ENV TZ=Asia/Shanghai
# 创建工作目录
WORKDIR /app
# 从编译阶段复制可执行文件到当前镜像
COPY --from=builder /app/go-demo ./
# 暴露服务端口
EXPOSE 8080
# 启动服务的命令
ENTRYPOINT ["./go-demo"]

拓展:如果追求极致的镜像体积,可将运行阶段的基础镜像替换为scratch(Docker空镜像),但需要注意scratch镜像无任何系统命令,无法进行日志查看等操作,适合生产环境对镜像体积要求极高的场景,修改后运行阶段代码:

# 极致轻量版运行阶段
FROM scratch
WORKDIR /app
COPY --from=builder /app/go-demo ./
EXPOSE 8080
ENTRYPOINT ["./go-demo"]

四、Jenkins流水线配置:核心自动化流程

Jenkins流水线推荐使用Declarative Pipeline(声明式流水线),通过Jenkinsfile文件定义整个部署流程,将流水线代码与项目源码一起托管到Git仓库,实现流水线即代码,方便版本管理和迭代。

4.1 编写Jenkinsfile(Go服务专属)

Jenkinsfile需放在Go项目的根目录,与main.goDockerfile同级,文件中定义了拉取代码、编译、构建镜像、推送镜像、更新K8s的全流程,包含环境变量定义各阶段步骤异常处理,代码注释详细,通俗易懂。

Jenkinsfile完整代码

// 声明式流水线
pipeline {
    // 执行节点:指定Jenkins的执行节点(可使用代理节点,这里使用master)
    agent any
    // 定义环境变量,方便后续修改,统一管理
    environment {
        // Git代码仓库地址
        GIT_REPO = "git@gitlab.xxx.com:dev/go-demo.git"
        // 项目名称
        PROJECT_NAME = "go-demo"
        // Harbor私有镜像仓库地址+项目名
        HARBOR_ADDR = "harbor.xxx.com/go-service"
        // 镜像标签:使用构建号作为标签,确保唯一性,也可使用Git提交ID ${GIT_COMMIT[:8]}
        IMAGE_TAG = "${BUILD_NUMBER}"
        // 完整的镜像名:仓库地址/项目名:标签
        IMAGE_NAME = "${HARBOR_ADDR}/${PROJECT_NAME}:${IMAGE_TAG}"
        // K8s命名空间:服务部署到K8s的哪个命名空间
        K8S_NAMESPACE = "dev"
        // K8s Deployment名称:与后续K8s配置文件中的名称一致
        K8S_DEPLOY_NAME = "go-demo-deploy"
    }
    // 流水线阶段定义
    stages {
        // 阶段1:拉取Go源码
        stage('Pull Code') {
            steps {
                echo "===== 开始拉取${PROJECT_NAME}源码 ====="
                // 拉取Git代码,使用SSH方式(需配置Jenkins的Git SSH密钥)
                git url: "${GIT_REPO}", branch: "main", credentialsId: "git-ssh-key"
            }
        }

        // 阶段2:编译Go代码,生成Linux可执行文件
        stage('Build Go Code') {
            steps {
                echo "===== 开始编译${PROJECT_NAME}Go代码 ====="
                // 进入项目目录,执行Go编译命令,与Dockerfile中的编译参数一致
                sh '''
                    cd ${WORKSPACE}
                    CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o go-demo main.go
                    # 验证编译结果,查看可执行文件是否存在
                    if [ -f "go-demo" ]; then
                        echo "Go代码编译成功,生成可执行文件go-demo"
                    else
                        echo "Go代码编译失败"
                        exit 1
                    fi
                '''
            }
        }

        // 阶段3:构建Docker镜像
        stage('Build Docker Image') {
            steps {
                echo "===== 开始构建Docker镜像:${IMAGE_NAME} ====="
                // 基于项目根目录的Dockerfile构建镜像
                sh "docker build -t ${IMAGE_NAME} ${WORKSPACE}"
                // 验证镜像是否构建成功
                sh "docker images | grep ${PROJECT_NAME}"
            }
        }

        // 阶段4:推送Docker镜像到Harbor
        stage('Push Docker Image') {
            steps {
                echo "===== 开始推送镜像到Harbor:${IMAGE_NAME} ====="
                // 推送镜像到Harbor
                sh "docker push ${IMAGE_NAME}"
                // 推送完成后,可选择删除本地镜像,释放磁盘空间
                sh "docker rmi ${IMAGE_NAME}"
            }
        }

        // 阶段5:更新K8s镜像,实现滚动更新
        stage('Update K8s Deployment') {
            steps {
                echo "===== 开始更新K8s ${K8S_NAMESPACE}命名空间下的${K8S_DEPLOY_NAME} ====="
                // 使用kubectl set image命令更新Deployment的镜像版本,触发滚动更新
                // go-demo-container为Deployment中定义的容器名称
                sh "kubectl set image deployment/${K8S_DEPLOY_NAME} go-demo-container=${IMAGE_NAME} -n ${K8S_NAMESPACE}"
                // 验证更新是否成功,查看Deployment的状态
                sh "kubectl rollout status deployment/${K8S_DEPLOY_NAME} -n ${K8S_NAMESPACE}"
                echo "===== ${K8S_DEPLOY_NAME}更新成功,服务已滚动上线 ====="
            }
        }
    }
    // 流水线后置操作:无论成功还是失败,都执行的步骤
    post {
        success {
            echo "===== 本次${PROJECT_NAME}服务自动化部署流水线执行成功!镜像版本:${IMAGE_TAG} ====="
        }
        failure {
            echo "===== 本次${PROJECT_NAME}服务自动化部署流水线执行失败,请检查日志! ====="
            // 可添加失败通知:如钉钉、企业微信、邮件通知
        }
    }
}

4.2 Jenkins流水线创建与触发

拓展:Jenkins流水线触发方式优化

五、K8s资源配置文件:服务部署与编排

完成Jenkins流水线配置后,需要在K8s集群中创建对应的Deployment(部署服务,实现滚动更新、副本管理)和Service(暴露服务,实现集群内/外部访问),资源配置文件为YAML格式,放在Git仓库中(也可直接在K8s集群中创建)。

5.1 Deployment配置文件(go-demo-deploy.yaml)

核心作用:管理Go服务的Pod副本,配置镜像版本、滚动更新策略、存活/就绪探针,确保服务高可用。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-demo-deploy  # 与Jenkinsfile中的K8S_DEPLOY_NAME一致
  namespace: dev        # 与Jenkinsfile中的K8S_NAMESPACE一致
  labels:
    app: go-demo
spec:
  replicas: 2  # 启动2个Pod副本,实现高可用
  selector:
    matchLabels:
      app: go-demo
  # 滚动更新策略:核心,确保更新时无停机
  strategy:
    rollingUpdate:
      maxSurge: 1        # 滚动更新时最多额外创建1个Pod
      maxUnavailable: 0  # 滚动更新时最少可用Pod数为0,确保服务不中断
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: go-demo
    spec:
      containers:
      - name: go-demo-container  # 与Jenkinsfile中的容器名称一致
        image: harbor.xxx.com/go-service/go-demo:1  # 初始镜像版本,后续由Jenkins更新
        imagePullPolicy: Always  # 每次启动都拉取最新镜像
        ports:
        - containerPort: 8080    # 容器端口,与Go服务的监听端口一致
        # 存活探针:检测Pod是否存活,存活则保留,否则重启Pod
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5  # 容器启动5秒后开始检测
          periodSeconds: 10       # 每10秒检测一次
          timeoutSeconds: 3       # 检测超时时间3秒
        # 就绪探针:检测Pod是否就绪,就绪则加入Service的负载均衡
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
          timeoutSeconds: 3
        # 资源限制:限制Pod的CPU和内存使用,避免占用过多集群资源
        resources:
          limits:
            cpu: "0.5"
            memory: "512Mi"
          requests:
            cpu: "0.2"
            memory: "256Mi"

5.2 Service配置文件(go-demo-svc.yaml)

核心作用:将Deployment管理的Pod暴露出去,实现集群内或外部访问,这里采用NodePort方式(适合测试环境),生产环境推荐Ingress+LoadBalancer

apiVersion: v1
kind: Service
metadata:
  name: go-demo-svc
  namespace: dev
  labels:
    app: go-demo
spec:
  selector:
    app: go-demo  # 与Deployment的Pod标签一致,实现服务发现
  type: NodePort  # 节点端口方式,暴露到K8s节点的端口
  ports:
  - port: 8080     # Service的集群内端口
    targetPort: 8080  # 指向Pod的容器端口
    nodePort: 30080   # 节点端口,范围30000-32767

5.3 K8s资源创建

将上述两个YAML文件推送到Git仓库,或直接在K8s集群的master节点执行以下命令,创建资源:

# 创建命名空间(若未创建)
kubectl create namespace dev
# 创建Deployment
kubectl apply -f go-demo-deploy.yaml -n dev
# 创建Service
kubectl apply -f go-demo-svc.yaml -n dev
# 验证资源创建是否成功
kubectl get deploy,svc,pod -n dev

拓展:生产环境服务暴露方式

测试环境使用NodePort简单便捷,但生产环境存在端口冲突、安全性低等问题,推荐使用Ingress Nginx+LoadBalancer

六、部署验证与常见问题排查

6.1 部署成功验证

6.2 常见问题排查

七、相关技术拓展与优化建议

7.1 Go服务编译优化

7.2 Jenkins流水线优化

7.3 K8s部署优化

7.4 生产环境最佳实践

八、总结

本文详细讲解了如何通过Jenkins+K8s实现Go后端服务的自动化部署,从整体流程、前期环境准备,到Go项目基础代码、Dockerfile编写、Jenkins流水线配置、K8s资源编排,再到部署验证、问题排查和技术优化,形成了一套完整的落地方案。

相比Java、前端服务,Go服务因编译后为单可执行文件,容器化更轻量、部署更高效,而Jenkins+K8s的组合,实现了Go服务从代码到上线的全自动化,大幅提升了开发和运维效率,同时K8s的滚动更新、副本管理、弹性伸缩等特性,保障了Go服务的高可用。

在实际生产环境中,可根据企业的业务需求,对本文的方案进行灵活调整和优化,如添加监控、日志、灰度发布、多环境隔离等功能,搭建一套更完善、更稳定的云原生自动化部署体系。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文