本文示例如何使用 Jenkins 自动编译打包, 构建 Docker 镜像, 提交到本地 Docker Registry 镜像服务, 并经过 Helm 发布到 K8S 集群.java
总体的流程图以下:node
本文涉及到的环境:linux
服务端 ttg12
:git
应用:web
本地工做电脑spring
应用:docker
在 ttg12
上安装好 docker, 并配置好国内镜像.数据库
在 ttg12
上经过 docker 安装本地 Docker Registry (v2.6.1). 参考私有化部署极简 Docker Registry, 并将自签名证书复制到 /etc/docker/certs.d/<registry_domain>:<registry_port>/ca.crt
.segmentfault
准备一个 K8S 集群或者单节点 K8S, 在 ttg12
上安装好 kubectl 工具, 并配置好 /etc/.kube/config
.tomcat
在 ttg12
上安装好 helm 3 客户端. Linux 下 Helm 3 的安装很简单, 直接下载二进制文件下来, 解压, 移动到/usr/local/bin/
目录下便可.
# 这里以 helm v3.3.0 为例 curl https://get.helm.sh/helm-v3.3.0-rc.1-linux-amd64.tar.gz > /tmp/helm-v3.3.0-rc.1-linux-amd64.tar.gz tar zxf /tmp/helm-v3.3.0-rc.1-linux-amd64.tar.gz -C /tmp/helm-v3.3.0-rc.1-linux-amd64/helm `/usr/local/bin/helm` sudo cp /tmp/helm-v3.3.0-rc.1-linux-amd64/helm /usr/local/bin/helm sudo rm -rf /tmp/helm-v3.3.0-rc.1-linux-amd64.tar.gz /tmp/helm-v3.3.0-rc.1-linux-amd64/helm
本文使用 Docker 方式安装 Jenkins. 具体步骤参考本文另一篇博文 Docker安装Jenkins并支持Maven,Docker,Helm.
启动容器后, 登陆 Jenkins, 安装插件 Docker plugin
and Docker Pipeline
. 不然会报异常:
Obtained Jenkinsfile from git [https://gitee.com/facelessdemos/SpringBootDemo.git](https://gitee.com/facelessdemos/SpringBootDemo.git) Running in Durability level: MAX\_SURVIVABILITY org.codehaus.groovy.control.MultipleCompilationErrorsExcept.ion: startup failed: WorkflowScript: 34: Invalid agent type "docker" specified. Must be one of \[any, label, none\] @ line 34, column 17. docker { ^
参考 Gitee 官方文档, 安装 Gitee
插件.
在项目根目录下新建 Dockerfile, Jenkinsfile 文件 以及 helm 目录.
FROM frolvlad/alpine-java:jdk8-slim #在build镜像时能够经过 --build-args profile=xxx 进行修改 ARG profile ENV SPRING_PROFILES_ACTIVE=${profile} #项目的端口 EXPOSE 8080 WORKDIR /mnt #修改时区 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \ && apk add --no-cache tzdata \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/Shanghai" > /etc/timezone \ && apk del tzdata \ && rm -rf /var/cache/apk/* /tmp/* /var/tmp/* $HOME/.cache COPY ./web/target/web-1.0-SNAPSHOT.jar ./app.jar ENTRYPOINT ["java", "-jar", "/mnt/app.jar"]
image_tag = "1.0.0-snapshot" //定一个全局变量,存储Docker镜像的tag(版本) pipeline { agent any environment { //GIT_REPO = "${env.gitlabSourceRepoName}" //从Jenkins Gitlab插件中获取Git项目的名称 GIT_REPO = "springbootdemo" GIT_BRANCH = "${env.gitlabTargetBranch}" //项目的分支 GIT_TAG = sh(returnStdout: true,script: 'git describe --tags --always').trim() //commit id或tag名称 KUBE_CONFIG_LOCAL = credentials('local-k8s-kube-config') //开发测试环境的kube凭证 KUBE_CONFIG_PROD = "" //credentials('prod-k8s-kube-config') //生产环境的kube凭证 DOCKER_REGISTRY = "registry.faceless.com:5443" //Docker仓库地址 DOCKER_NAMESPACE = "facelessdemo" //命名空间 DOCKER_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${GIT_REPO}" //Docker镜像地址 INGRESS_HOST_DEV = "dev.springbootdemo.cn" //开发环境的域名 INGRESS_HOST_TEST = "test.springbootdemo.cn" //测试环境的域名 INGRESS_HOST_PROD = "prod.springbootdemo.cn" //生产环境的域名 K8S_NAMESPACE = "demo-dev" } parameters { string(name: 'ingress_path', defaultValue: '/', description: '服务上下文路径') string(name: 'replica_count', defaultValue: '2', description: '容器副本数量') } stages { stage('Code Analyze') { agent any steps { echo "1. 代码静态检查" } } stage('Maven Build') { agent any steps { echo "2. 代码编译打包" sh 'mvn clean package -Dfile.encoding=UTF-8 -Dmaven.test.skip=true' } } stage('Docker Build') { agent any steps { echo "3. 构建Docker镜像" echo "镜像地址: ${DOCKER_IMAGE}" script { def profile = "dev" image_tag = "dev." + env.GIT_TAG if (env.gitlabTargetBranch == "develop") { image_tag = "dev." + env.GIT_TAG profile = "dev" } else if (env.gitlabTargetBranch == "pre-release") { image_tag = "test." + env.GIT_TAG profile = "test" } else if (env.gitlabTargetBranch == "master"){ // master分支则直接使用Tag image_tag = env.GIT_TAG profile = "prod" } //经过--build-arg将profile进行设置,以区分不一样环境进行镜像构建 sh "docker build --build-arg profile=${profile} -t ${DOCKER_IMAGE}:${image_tag} ." sh "docker push ${DOCKER_IMAGE}:${image_tag}" sh "docker rmi ${DOCKER_IMAGE}:${image_tag}" } } } stage('Helm Deploy') { agent any steps { echo "4. 部署到K8s" sh "helm upgrade -i --namespace=${K8S_NAMESPACE} --set image.repository=${DOCKER_IMAGE} --set image.tag=${image_tag} ${GIT_REPO} ./helm/" } } } }
经过helm 客户端建立对应版本的 chart 配置文件清单:
helm create springbootdemo
获得相应的文件清单以下:
1) 咱们须要修改其中的 values.yaml
文件. 注意如下带注释的部分.
# 启动容器副本数 replicaCount: 2 image: # 项目打包出来的 docker image 提交在本地镜像仓库中 repository: registry.faceless.com:5443/facelessdemo/springbootdemo pullPolicy: IfNotPresent # 默认image标签. 需在Jenkinsfile中的 `sh helm upgrade` 命令中覆盖. tag: "dev.latest" imagePullSecrets: [] # 项目名 nameOverride: "springbootdemo" fullnameOverride: "" # 容器的端口暴露及环境变量配置 container: port: 8080 env: [] serviceAccount: create: true annotations: {} name: "" podAnnotations: {} podSecurityContextext: {} securityContext: {} # 本示例经过Ingress提供对外服务, 因此Service使用ClusterIP模式便可 service: type: ClusterIP port: 8080 # Ingress配置域名和路径. 测试时须要将`dev.springbootdemo.cn`域名指向k8s的任一台worker的IP. ingress: enabled: true annotations: {} hosts: - host: dev.springbootdemo.cn paths: [/] tls: [] resources: {} autoscaling: enabled: false minReplicas: 1 maxReplicas: 4 nodeSelector: {} tolerations: [] affinity: {} # Probe延时参数 probe: livenessDelaySeconds: 120 readinessDelaySeconds: 120
2) templates/depolyment.yaml 文件. templates
文件夹下的文件是 K8S deploymnet, service, ingress 等通用配置模板, 通常状况下无需修改. 可是对于 SrpingBoot 项目, 特别是涉及到启动过程当中须要链接数据库/消息队列/其余第三方服务的状况, 默认在启动后就进行服务探测(Probe). 而此时可能 tomcat 还没起来, 因此容易误报为服务失败, 致使无故重启容器. 所以咱们须要修改 templates/depolyment.yaml
文件, 将其中 Probe 延时改成一个相对比较合理的时间.
咱们修改 templates/depolyment.yaml
文件, 在 .spec.template.spec.containers
节点下增长 .livenessProbe
和 .readinessProbe
两个节点. 其余部分没有任何改动, 因此这里只给出这一段代码片断:
containers: - name: {{ .Chart.Name }} # 省略... ports: - name: http containerPort: {{ .Values.container.port }} protocol: TCP livenessProbe: httpGet: path: / port: http initialDelaySeconds: {{ .Values.probe.livenessDelaySeconds }} readinessProbe: httpGet: path: / port: http initialDelaySeconds: {{ .Values.probe.readinessDelaySeconds }}
1) 新建一个 Item, 取一个名字 (好比: SpringBootDemo-K8S), 选择 Pipeline 类型.
2) 进入到配置页面, General -> Description
字段输入项目描述.
3) 若是你的 Jenkins 能够外网访问, 或者跟你的 Gitlab 处于同一局域网, 能够由 Gitee 或者 Gitlab 触发 WebHook, 那么在 Build Triggers
模块进行相应的配置. 本示例由于 Gitee 没法触达内网部署的 Jenkins, 因此没有配置 Build Triggers
. 后面须要手动触发编译流程.
4) Pipeline的定义以下图所示 (注意, 我这里取的是base
分支, 若是你须要取master
分支, 将下图中的3处base
改成master
:
最后, 手动触发编译, Pipeline 的 Stage View 以下图所示: