通过前面两篇文章,我们已经有了一个“嗷嗷待哺”的K8s集群环境,也对相关的概念与组件有了一个基本了解(前期对概念有个印象即可,因为只有实践了才能对其有深入理解,所谓“纸上得来终觉浅,绝知此事要躬行”),本文从实践角度介绍如何结合我们常用的Gitlab与Jenkins,通过K8s来实现项目的自动化部署,示例将包括基于SpringBoot的服务端项目与基于Vue.js的Web项目。

本文涉及到的工具与技术包括:

  • Gitlab —— 常用的源代码管理系统
  • Jenkins, Jenkins Pipeline —— 常用的自动化构建、部署工具,Pipeline以流水线的方式将构建、部署的各个步骤组织起来
  • Docker,Dockerfile —— 容器引擎,所有应用最终都要以Docker容器运行,Dockerfile是Docker镜像定义文件
  • Kubernetes —— Google开源的容器编排管理系统
  • Helm —— Kubernetes的包管理工具,类似Linux的yum,apt,或Node的npm等包管理工具,能将Kubernetes中的应用及相关依赖服务以包(Chart)的形式组织管理

环境背景:

  1. 已使用Gitlab做源码管理,源码按不同的环境建立了develop(对应开发环境),pre-release(对应测试环境),master(对应生产环境)分支
  2. 已搭建了Jenkins服务
  3. 已有Docker Registry服务,用于Docker镜像存储(基于Docker Registry或Harbor自建,或使用云服务,本文使用阿里云容器镜像服务)
  4. 已搭建了K8s集群

预期效果:

  1. 分环境部署应用,开发环境、测试环境、生产环境分开来,部署在同一集群的不同namespace,或不同集群中(比如开发测试部署在本地集群的不同namespace中,生产环境部署在云端集群)
  2. 配置尽可能通用化,只需要通过修改少量配置文件的少量配置属性,就能完成新项目的自动化部署配置
  3. 开发测试环境在push代码时自动触发构建与部署,生产环境在master分支上添加版本tag并且push tag后触发自动部署
  4. 整体交互流程如下图

项目配置文件

首先我们需要在项目的根路径中添加一些必要的配置文件,如下图所示

包括:

  1. Dockerfile文件,用于构建Docker镜像的文件(参考 Docker笔记(十一):Dockerfile详解与最佳实践
  2. Helm相关配置文件,Helm是Kubernetes的包管理工具,可以将应用部署相关的Deployment,Service,Ingress等打包进行发布与管理(Helm的具体介绍我们后面再补充)
  3. Jenkinsfile文件,Jenkins的pipeline定义文件,定义了各个阶段需执行的任务

Dockerfile

在项目根目录中添加一个Dockerfile文件(文件名就叫Dockerfile),定义如何构建Docker镜像,以Spring Boot项目为例,

  1. FROM frolvlad/alpine-java:jdk8-slim
  2. #在build镜像时可以通过 --build-args profile=xxx 进行修改
  3. ARG profile
  4. ENV SPRING_PROFILES_ACTIVE=${profile}
  5. #项目的端口
  6. EXPOSE 8000
  7. WORKDIR /mnt
  8. #修改时区
  9. RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \
  10. && apk add --no-cache tzdata \
  11. && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
  12. && echo "Asia/Shanghai" > /etc/timezone \
  13. && apk del tzdata \
  14. && rm -rf /var/cache/apk/* /tmp/* /var/tmp/* $HOME/.cache
  15. COPY ./target/your-project-name-1.0-SNAPSHOT.jar ./app.jar
  16. ENTRYPOINT ["java", "-jar", "/mnt/app.jar"]

将SPRING_PROFILES_ACTIVE通过参数profile暴露出来,在构建的时候可以通过 --build-args profile=xxx 来进行动态设定,以满足不同环境的镜像构建要求。

SPRING_PROFILES_ACTIVE本可以在Docker容器启动时通过docker run -e SPRING_PROFILES_ACTIVE=xxx来设定,因这里使用Helm进行部署不直接通过docker run运行,因此通过ARG在镜像构建时指定

Helm配置文件

Helm是Kubernetes的包管理工具,将应用部署相关的Deployment,Service,Ingress等打包进行发布与管理(可以像Docker镜像一样存储于仓库中)。如上图中Helm的配置文件包括:

  1. helm - chart包的目录名
  2. ├── templates - k8s配置模版目录
  3. ├── deployment.yaml - Deployment配置模板,定义如何部署Pod
  4. ├── _helpers.tpl - 以下划线开头的文件,helm视为公共库定义文件,用于定义通用的子模版、函数、变量等
  5. ├── ingress.yaml - Ingress配置模板,定义外部如何访问Pod提供的服务,类似于Nginx的域名路径配置
  6. ├── NOTES.txt - chart包的帮助信息文件,执行helm install命令成功后会输出这个文件的内容
  7. └── service.yaml - Service配置模板,配置访问Pod的服务抽象,有NodePortClusterIp
  8. |── values.yaml - chart包的参数配置文件,各模版文件可以引用这里的参数
  9. ├── Chart.yaml - chart定义,可以定义chart的名字,版本号等信息
  10. ├── charts - 依赖的子包目录,里面可以包含多个依赖的chart包,一般不存在依赖,我这里将其删除了

我们可以在Chart.yaml中定义每个项目的chart名称(类似安装包名),如

  1. apiVersion: v2
  2. name: your-chart-name
  3. description: A Helm chart for Kubernetes
  4. type: application
  5. version: 1.0.0
  6. appVersion: 1.16.0

在values.yaml中定义模板文件中需要用到的变量,如

  1. #部署Pod的副本数,即运行多少个容器
  2. replicaCount: 1
  3. #容器镜像配置
  4. image:
  5. repository: registry.cn-hangzhou.aliyuncs.com/demo/demo
  6. pullPolicy: Always
  7. # Overrides the image tag whose default is the chart version.
  8. tag: "dev"
  9. #镜像仓库访问凭证
  10. imagePullSecrets:
  11. - name: aliyun-registry-secret
  12. #覆盖启动容器名称
  13. nameOverride: ""
  14. fullnameOverride: ""
  15. #容器的端口暴露及环境变量配置
  16. container:
  17. port: 8000
  18. env: []
  19. #ServiceAccount,默认不创建
  20. serviceAccount:
  21. # Specifies whether a service account should be created
  22. create: false
  23. # Annotations to add to the service account
  24. annotations: {}
  25. name: ""
  26. podAnnotations: {}
  27. podSecurityContext: {}
  28. # fsGroup: 2000
  29. securityContext: {}
  30. # capabilities:
  31. # drop:
  32. # - ALL
  33. # readOnlyRootFilesystem: true
  34. # runAsNonRoot: true
  35. # runAsUser: 1000
  36. #使用NodePort的service,默认为ClusterIp
  37. service:
  38. type: NodePort
  39. port: 8000
  40. #外部访问Ingress配置,需要配置hosts部分
  41. ingress:
  42. enabled: true
  43. annotations: {}
  44. # kubernetes.io/ingress.class: nginx
  45. # kubernetes.io/tls-acme: "true"
  46. hosts:
  47. - host: demo.com
  48. paths: ["/demo"]
  49. tls: []
  50. # - secretName: chart-example-tls
  51. # hosts:
  52. # - chart-example.local
  53. #.... 省略了其它默认参数配置

这里在默认生成的基础上添加了container部分,可以在这里指定容器的端口号而不用去改模板文件(让模板文件在各个项目通用,通常不需要做更改),同时添加env的配置,可以在helm部署时往容器里传入环境变量。将Service type从默认的ClusterIp改为了NodePort。部署同类型的不同项目时,只需要根据项目情况配置Chart.yaml与values.yaml两个文件的少量配置项,templates目录下的模板文件可直接复用。

部署时需要在K8s环境中从Docker镜像仓库拉取镜像,因此需要在K8s中创建镜像仓库访问凭证(imagePullSecrets)

  1. # 登录Docker Registry生成/root/.docker/config.json文件
  2. sudo docker login --username=your-username registry.cn-shenzhen.aliyuncs.com
  3. # 创建namespace develop(我这里是根据项目的环境分支名称建立namespace)
  4. kubectl create namespace develop
  5. # 在namespace develop中创建一个secret
  6. kubectl create secret generic aliyun-registry-secret --from-file=.dockerconfigjson=/root/.docker/config.json --type=kubernetes.io/dockerconfigjson --namespace=develop

Jenkinsfile

Jenkinsfile是Jenkins pipeline配置文件,遵循Groovy语法,对于Spring Boot项目的构建部署, 编写Jenkinsfile脚本文件如下,

  1. image_tag = "default" //定一个全局变量,存储Docker镜像的tag(版本)
  2. pipeline {
  3. agent any
  4. environment {
  5. GIT_REPO = "${env.gitlabSourceRepoName}" //从Jenkins Gitlab插件中获取Git项目的名称
  6. GIT_BRANCH = "${env.gitlabTargetBranch}" //项目的分支
  7. GIT_TAG = sh(returnStdout: true,script: 'git describe --tags --always').trim() //commit id或tag名称
  8. DOCKER_REGISTER_CREDS = credentials('aliyun-docker-repo-creds') //docker registry凭证
  9. KUBE_CONFIG_LOCAL = credentials('local-k8s-kube-config') //开发测试环境的kube凭证
  10. KUBE_CONFIG_PROD = "" //credentials('prod-k8s-kube-config') //生产环境的kube凭证
  11. DOCKER_REGISTRY = "registry.cn-hangzhou.aliyuncs.com" //Docker仓库地址
  12. DOCKER_NAMESPACE = "your-namespace" //命名空间
  13. DOCKER_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${GIT_REPO}" //Docker镜像地址
  14. INGRESS_HOST_DEV = "dev.your-site.com" //开发环境的域名
  15. INGRESS_HOST_TEST = "test.your-site.com" //测试环境的域名
  16. INGRESS_HOST_PROD = "prod.your-site.com" //生产环境的域名
  17. }
  18. parameters {
  19. string(name: 'ingress_path', defaultValue: '/your-path', description: '服务上下文路径')
  20. string(name: 'replica_count', defaultValue: '1', description: '容器副本数量')
  21. }
  22. stages {
  23. stage('Code Analyze') {
  24. agent any
  25. steps {
  26. echo "1. 代码静态检查"
  27. }
  28. }
  29. stage('Maven Build') {
  30. agent {
  31. docker {
  32. image 'maven:3-jdk-8-alpine'
  33. args '-v $HOME/.m2:/root/.m2'
  34. }
  35. }
  36. steps {
  37. echo "2. 代码编译打包"
  38. sh 'mvn clean package -Dfile.encoding=UTF-8 -DskipTests=true'
  39. }
  40. }
  41. stage('Docker Build') {
  42. agent any
  43. steps {
  44. echo "3. 构建Docker镜像"
  45. echo "镜像地址: ${DOCKER_IMAGE}"
  46. //登录Docker仓库
  47. sh "sudo docker login -u ${DOCKER_REGISTER_CREDS_USR} -p ${DOCKER_REGISTER_CREDS_PSW} ${DOCKER_REGISTRY}"
  48. script {
  49. def profile = "dev"
  50. if (env.gitlabTargetBranch == "develop") {
  51. image_tag = "dev." + env.GIT_TAG
  52. } else if (env.gitlabTargetBranch == "pre-release") {
  53. image_tag = "test." + env.GIT_TAG
  54. profile = "test"
  55. } else if (env.gitlabTargetBranch == "master"){
  56. // master分支则直接使用Tag
  57. image_tag = env.GIT_TAG
  58. profile = "prod"
  59. }
  60. //通过--build-arg将profile进行设置,以区分不同环境进行镜像构建
  61. sh "docker build --build-arg profile=${profile} -t ${DOCKER_IMAGE}:${image_tag} ."
  62. sh "sudo docker push ${DOCKER_IMAGE}:${image_tag}"
  63. sh "docker rmi ${DOCKER_IMAGE}:${image_tag}"
  64. }
  65. }
  66. }
  67. stage('Helm Deploy') {
  68. agent {
  69. docker {
  70. image 'lwolf/helm-kubectl-docker'
  71. args '-u root:root'
  72. }
  73. }
  74. steps {
  75. echo "4. 部署到K8s"
  76. sh "mkdir -p /root/.kube"
  77. script {
  78. def kube_config = env.KUBE_CONFIG_LOCAL
  79. def ingress_host = env.INGRESS_HOST_DEV
  80. if (env.gitlabTargetBranch == "pre-release") {
  81. ingress_host = env.INGRESS_HOST_TEST
  82. } else if (env.gitlabTargetBranch == "master"){
  83. ingress_host = env.INGRESS_HOST_PROD
  84. kube_config = env.KUBE_CONFIG_PROD
  85. }
  86. sh "echo ${kube_config} | base64 -d > /root/.kube/config"
  87. //根据不同环境将服务部署到不同的namespace下,这里使用分支名称
  88. sh "helm upgrade -i --namespace=${env.gitlabTargetBranch} --set replicaCount=${params.replica_count} --set image.repository=${DOCKER_IMAGE} --set image.tag=${image_tag} --set nameOverride=${GIT_REPO} --set ingress.hosts[0].host=${ingress_host} --set ingress.hosts[0].paths={${params.ingress_path}} ${GIT_REPO} ./helm/"
  89. }
  90. }
  91. }
  92. }
  93. }

Jenkinsfile定义了整个自动化构建部署的流程:

  1. Code Analyze,可以使用SonarQube之类的静态代码分析工具完成代码检查,这里先忽略
  2. Maven Build,启动一个Maven的Docker容器来完成项目的maven构建打包,挂载maven本地仓库目录到宿主机,避免每次都需要重新下载依赖包
  3. Docker Build,构建Docker镜像,并推送到镜像仓库,不同环境的镜像通过tag区分,开发环境使用dev.commitId的形式,如dev.88f5822,测试环境使用test.commitId,生产环境可以将webhook事件设置为tag push event,直接使用tag名称
  4. Helm Deploy,使用helm完成新项目的部署,或已有项目的升级,不同环境使用不同的参数配置,如访问域名,K8s集群的访问凭证kube_config等

Jenkins配置

Jenkins任务配置

在Jenkins中创建一个pipeline的任务,如图

配置构建触发器,将目标分支设置为develop分支,生成一个token,如图

记下这里的“GitLab webhook URL”及token值,在Gitlab配置中使用。

配置流水线,选择“Pipeline script from SCM”从项目源码中获取pipeline脚本文件,配置项目Git地址,拉取源码凭证等,如图

保存即完成了项目开发环境的Jenkins配置。测试环境只需将对应的分支修改为pre-release即可

Jenkins凭据配置

在Jenkinsfile文件中,我们使用到了两个访问凭证——Docker Registry凭证与本地K8s的kube凭证,

  1. DOCKER_REGISTER_CREDS = credentials('aliyun-docker-repo-creds') //docker registry凭证
  2. KUBE_CONFIG_LOCAL = credentials('local-k8s-kube-config') //开发测试环境的kube凭证

这两个凭证需要在Jenkins中创建。

添加Docker Registry登录凭证,在Jenkins 凭据页面,添加一个用户名密码类型的凭据,如图

添加K8s集群的访问凭证,在master节点上将/root/.kube/config文件内容进行base64编码,

  1. base64 /root/.kube/config > kube-config-base64.txt
  2. cat kube-config-base64.txt

使用编码后的内容在Jenkins中创建一个Secret text类型的凭据,如图

在Secret文本框中输入base64编码后的内容。

Gitlab配置

在Gitlab项目的 Settings - Integrations 页面配置一个webhook,在URL与Secret Token中填入前面Jenkins触发器部分的“GitLab webhook URL”及token值,选中“Push events”作为触发事件,如图

开发、测试环境选择“Push events”则在开发人员push代码,或merge代码到develop,pre-release分支时,就会触发开发或测试环境的Jenkins pipeline任务完成自动化构建;生产环境选择“Tag push events”,在往master分支push tag时触发自动化构建。如图为pipeline构建视图

总结

本文介绍使用Gitlab+Jenkins Pipeline+Docker+Kubernetes+Helm来实现Spring Boot项目的自动化部署,只要稍加修改即可应用于其它基于Spring Boot的项目(具体修改的地方在源码的Readme文件中说明)。

本文涉及的所有配置文件(包括基于Spring Boot的服务端项目与基于Vue.js的Web项目)可在源码项目中获取(源码地址获取办法:关注公众号“半路雨歌”,首页输入“k8sops”即可)。

原文地址:http://blog.jboost.cn/k8s3-cd.html


作者:雨歌

欢迎关注作者微信公众号:半路雨歌,一起学习成长

Kubernetes笔记(三):Gitlab+Jenkins Pipeline+Docker+k8s+Helm自动化部署实践(干货分享!)的更多相关文章

  1. Jenkins+Git+Docker+K8s部署

    准备工作 Jenkins已安装 Docker和K8s部署运行成功 代码管理工具使用Git 最近公司项目使用Jenkins+Git+Docker+K8s进行持续化构建部署,这里笔者整理了一下构建部署的相 ...

  2. 基于 Docker 和 GitLab 的前端自动化部署实践笔记

    基于 Docker 和 GitLab 的前端自动化部署 实践笔记 随着接触的项目越来越多,在部署测试流程上重复耗时工作也越来越多,所以对前端工作的CI/CD实现愈发迫在眉睫. 前端开发由于三大框架的崛 ...

  3. devops持续集成,Centos7.6下gitlab+jenkins(pipeline)实现代码自动上线

    持续集成 gitlab+jenkins(pipeline)实现代码自动上线 环境准备:Centos7.6版本ip:192.168.0.13 主机名:gitip:192.168.0.23 主机名:jen ...

  4. Jenkins+SVN+Maven+shell 自动化部署实践

      JAVA环境中利用Jenkins+svn+maven进行自动化部署实践   一. 前言2 1.介绍jenkins2 1.本地项目打包2 2.通过secureCRT工具,手动传输到服务器2 3.然后 ...

  5. 持续集成之⑤:jenkins结合脚本实现代码自动化部署及一键回滚至上一版本

    持续集成之⑤:jenkins结合脚本实现代码自动化部署及一键回滚至上一版本 一:本文通过jenkins调用shell脚本的的方式完成从Git服务器获取代码.打包.部署到web服务器.将web服务器从负 ...

  6. AWS DevOps – 配合Jenkins和CodeDeploy实现代码自动化部署

    AWS DevOps – 配合Jenkins和CodeDeploy实现代码自动化部署 Amazon ElastiCache 连接至 Redis 节点 通过 AWS Command Line Inter ...

  7. jenkins结合脚本实现代码自动化部署及一键回滚至上一版本

    持续集成之⑤:jenkins结合脚本实现代码自动化部署及一键回滚至上一版本 一:本文通过jenkins调用shell脚本的的方式完成从Git服务器获取代码.打包.部署到web服务器.将web服务器从负 ...

  8. Kubernetes笔记(二):了解k8s的基本组件与概念

    前文 Kubernetes笔记(一):十分钟部署一套K8s环境 介绍了如何快速搭建一个k8s系统.为了继续使用k8s来部署我们的应用,需要先对k8s中的一些基本组件与概念有个了解. Kubernete ...

  9. 【Devops】【docker】【CI/CD】3.Jenkins+GitLab+docker+springboot 实现自动化部署

    ==================================================================================================== ...

随机推荐

  1. L12 Transformer

    Transformer 在之前的章节中,我们已经介绍了主流的神经网络架构如卷积神经网络(CNNs)和循环神经网络(RNNs).让我们进行一些回顾: CNNs 易于并行化,却不适合捕捉变长序列内的依赖关 ...

  2. Xshell 设置右键粘贴即是复制

    打开工具->选项->键盘和鼠标面板     1.鼠标部分的右击设置"粘贴剪切板的内容".    2.选择部分,在"自动将所选文本复制到剪切板"前打勾

  3. eclipse 集成git工具

    1.eclipse git插件下载 打开Eclipse,然后点击Help>Install New Software>Add name:git location:http://downloa ...

  4. java-锁膨胀的过程

    先来看个奇怪的demo public class A { int i=0; // boolean flag =false; public synchronized void parse(){ i++; ...

  5. shiro:自定义remle(二)

    SpringMVC+SpringMVC+Mybatis项目 1:导入相关依赖 <dependencies> <!--测试依赖--> <dependency> < ...

  6. python 携程asyncio实现高并发示例1

    import asyncio #携程(携程不是函数) async def print_hello(): while True: print("hello world") await ...

  7. ES6新增的Map和WeakMap 又是什么玩意?非常详细的解释

    上一篇文章讲了set和weakSet,这节咱就讲Map和weakMap是什么?这两篇文章并没有什么联系,主要知识用法类似而已.嘿嘿,是不是感觉舒服多了. 什么是Map 介绍什么是Map,就不得不说起O ...

  8. Asp.Net Core 3.1 的启动过程5

    前言 本文主要讲的是Asp.Net Core的启动过程,帮助大家掌握应用程序的关键配置点. 1.创建项目 1.1.用Visual Studio 2019 创建WebApi项目. 这里面可以看到有两个关 ...

  9. 关于宝塔下的项目中的php不能访问的问题

    遇到的问题是访问项目根目录的所有php文件都是报404错,而其他文件则可以,比如txt,最后查资料发现 在宝塔运行网站的时候会在项目的根目录自动生成一个.user.ini文件,这个文件主要是防止对指定 ...

  10. Python(4)

    lst = [1,2,4,8,16,32,64,128,256,512,1024,32769,65536,4294967296] # 输出 { 1:[1,2,3,8], 2:[16,32,64], 3 ...