朋友的真实操作流程,使用 Jenkins 和 Kubernetes 完成持续集成和持续部署,有搭建,有入门,手把手教学文档,干得拧不出水来,分享一波。 本文作者:孙丹丹,女,单身,DevOps 运维工程师,CKA 认证。就职于某容器云平台服务公司,负责国内多家知名企业 DevOps 运维交付。 安装 Jenkins 启动 Jenkins 容器docker run -d -u root -v /workspace/jenkins-home/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -v "$HOME":/home -p 8080:8080 -p 50000:50000 jenkinsci/blueocean 参数 说明 -d d 指 daemon,后台启动 -u 指定运行用户 -v-v /workspace/jenkins-home:/var/jenkins_home hostDir:containerDir 表示将容器中 jenkins_home 映射到宿主机 jenkins-home 目录 -p-p 8080:8080 hostPort:containerPort 检查 Jenkins 服务状态docker ps | grep jenkins 启动成功后,就可以通过 ip:port 在浏览器上访问了。ip 为 docker 所在机器的 ip, port 为 Jenkins 容器映射的宿主机端口 配置 Jenkins Jenkins 启动成功后,需要执行一些快速的 "一次性" 步骤。当你第一次访问一个新的 Jenkins 实例时, 要求你使用自动生成的密码对其进行解锁。密码为 Jenkins 所在容器的 /var/jenkins_home/secrets/initialAdminPassword 的内容:docker exec -itbash -c "cat /var/jenkins_home/secrets/initialAdminPassword" 或者用 K8S 命令 kubectl kubectl exec -it bash -c "cat /var/jenkins_home/secrets/initialAdminPassword" 在 Unlock Jenkins 页面, 粘贴该密码到 Administrator password 字段并点击 Continue。解锁 Jenkins 后,插件安装页面出现,点击 Install suggested plugins 即可。 这个过程会耗时一段时间。 创建第一个管理员用户,可以填写,也可以跳过,直接使用 admin 账户继续。 这边我选择了跳过,点击开始使用 Jenkins,就可以使用 Jenkins 了。 实现 Java 应用持续集成和持续发布 Fork 和 Clone GitHub 示例仓库 登录 GitHub 账号:https://github.com 上传代码到 GitHub 代码仓库中,或 Fork 到你的 GitHub 仓库中,演示代码 prometheus-test-demo,地址为 https://github.com/0820sdd/prometheus-test-demo 将你的 GitHub 账户中的 prometheus-test-demo 仓库 Clone 到本地机器: 打开一个终端/命令提示符,并且进入正确的目录路径: Mac OS 系统路径为 /Users//Documents/GitHub/ Linux 系统路径为/home//GitHub/ Windows 系统路径为 C:UsersDocumentsGitHub (推荐使用 Git bash 命令行,而不是通常的 Microsoft 命令提示符) 运行以下命令完成仓库的 clone:git clone https://github.com/YOUR-GITHUB-ACCOUNT-NAME/simple-java-maven-app 其中 YOUR-GITHUB-ACCOUNT-NAME 是你的 GitHub 账户的名称。 在 Jenkins 中创建流水线 登录 Jenkins,点击页面创建一个新任务。若是你无法看见该内容,点击左上方的新建 item。 为新建的流水线项目指定名称(例如 prometheus-test-demo),选择流水线,点击确定。 安装 K8S 插件 登录 Jenkins,系统管理→ 插件管理 → 搜索 kubernetes,选择第二个 Kubernetes,点击 安装,安装完成后重启 Jenkins 。 对接 K8S 集群 申请 K8S 凭据 因为 Jenkins 服务器在 kubernetes 集群之外,所以我们准备以下文件才能从外面连接到 kubernetes 集群。 登录 Jenkins,点击右上角「用户」 → 左下角「凭据」: 然后点击 Jenkins,选择全局凭据(Unrestricted) 添加凭据,类型选择 X.509 Client Certificate Client Key: .kube/config文件中 client-key 对应的 key 文件 Client Certificate: .kube/config文件中 client-certificate 对应的 crt 或是 pem 文件 Server CA Certificate:.kube/config 文件中 certificate-authority 对应的 crt 或是 pem 文件,K8S 的最高权限证书 ID:可不填写,默认会自动生成一串字符串,也可以自行设置 描述:描述下这个凭据的作用,比如这个可以写 对接 K8S 集群凭据 填写完毕,点击确定。 配置 K8S 集群的对接 登录 Jenkins,点击 系统管理 → 系统配置 → 滑动到页面最下面 点击 a separate configuration page: Kubernetes 地址:kubernetes服务地址,也就是 apiserver 的地址,一般是master 节点 NodeIP+6443 端口 Kubernetes 服务证书 key:kube-ca.crt 文件的内容 凭据:刚才创建的 certificate 凭据 Jenkins 地址:Agent 连接 Jenkins Master 的地址 其他都使用默认配置,点击连接测试,连接测试成功,点击 Save 存储。 K8S pod template 配置 Jenkins 的 kubernetes-plugin 在执行构建时会在 kubernetes 集群中自动创建一个 Pod,并在 Pod 内部创建一个名为 jnlp 的容器,该容器会连接 Jenkins 并运行 Agent 程序,形成一个 Jenkins 的 Master 和 Slave 架构,然后 Slave 会执行构建脚本进行构建,但如果构建内容是要创建 Docker Image 就要实现 Docker In Docker 方案(在 Docker 里运行 Docker),如果要在集群集群内部进行部署操作可以使用 kubectl 执行命令,要解决 kubectl 的安装和权限分配问题。 为了方便配置一个 Pod Templates,在配置 kubernetes 连接内容的下面,这里的模板只是模板(与类一样使用时还要实例化过程),名称和标签列表不要以为是 Pod 的 name 和 label,这里的名称和标签列表只是 Jenkins 查找选择模板时使用的,Jenkins 自动创建 Pod 的 name 是项目名称+随机字母的组合,所以我们填写 jenkins-slave,命名空间填写对应的 namespace。 这边要注意,添加 2 个 container,第一个,Pod 内添加一个容器名称是 jnlp,Docker 镜像填写:jenkins/jnlp-slave:4.3-7,后面的使用默认的即可,然后在添加一个 container,容器名称是 jnlp-kubectl,是这个容器里面有 kubectl 的命令,镜像名称填写 harbor.edu.cn/library/centos-docker-kubectl:v1.0,下面增加了 Host Path Volume:/var/run/docker.sock、/root/.kube/、/etc/kubernetes/pki,这边便是为了 jenkins-slave 下有足够的权限可以执行 docker 及 kubectl 部署到 k8s 集群的权限,因为 jenkins-slave pod 有可能会被调度到任一 worker 节点,所以所有的 worker 节点上都必须有 /root/.kube/、/etc/kubernetes/pki,配置好之后点击保存。 Jenkins pipeline 说明 Pipeline,简单来说,就是一套运行在 Jenkins 上的工作流框架,将原来独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排和可视化的工作。 Jenkins Pipeline 有几个核心概念: Node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行环境,比如我们之前动态运行的 Jenkins Slave 就是一个 Node 节点 Stage:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作,比如:Build、Test、Deploy,Stage 是一个逻辑分组的概念,可以跨多个 Node Step:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像,由各类 Jenkins 插件提供,比如命令:sh "make",就相当于我们平时 shell 终端中执行 make 命令一样。 Pipeline的使用: Pipeline 脚本是由 Groovy 语言实现的 Pipeline 支持两种语法:Declarative(声明式)和 Scripted Pipeline(脚本式)语法 Pipeline 也有两种创建方法:可以直接在 Jenkins 的 Web UI 界面中输入脚本;也可以通过创建一个 Jenkinsfile 脚本文件放入项目源码库中 一般我们都推荐在 Jenkins 中直接从源代码控制(SCMD)中直接载入 Jenkinsfile Pipeline 这种方法,但是本次为了更直观的展示,我们在 Web UI 界面中输入脚本 Jenkins pipeline 入门 创建并运行 pipeline 进入到 Jenkins 首页,点击项目 prometheus-test-demo,点击左侧 配置 点击页面顶部的 Pipeline 选项卡,向下滚动到 Pipeline 部分 在定义域中,选择 Pipeline script 选项node { stage("Clone") { echo "1.Clone Stage" } stage("Test") { echo "2.Test Stage" } stage("Build") { echo "3.Build Stage" } stage("Deploy") { echo "4. Deploy Stage" }} 点击保存,切换到 Jenkins 页面,点击左侧的 打开 Blue Ocean 进入Jenkins的Blue Ocean界面,进入到 相应的项目下,点击 运行 。 也可以在 Jenkins prometheus-test-demo 项目下,点击左侧菜单 立即构建,然后点击正在构建的任务,就可以看到 Console Output: 在 Slave 中运行 Pipeline 上面对 Jenkins 的 Pipeline 做了简单的测试,但是其并未在我们的 Slave中运行,如果要在 Slave 中运行,其就要使用我们在对接 K8S 集群时 Pod Template 指定的标签列表 ,点击进 prometheus-test-demo 项目,点击左侧菜单 配置,进入到 pipeline scripts 部分,修改 pipeline scripts 如下:node("slave") { stage("Clone") { echo "1.Clone Stage" } stage("Test") { echo "2.Test Stage" } stage("Build") { echo "3.Build Stage" } stage("Deploy") { echo "4. Deploy Stage" }} 点击 立即构建,同时可以登录到k8s集群,使用 kubectl get po -w 可以看到 jenkins-slave pod 的生命周期,就是我们开始构建这个任务,选择了使用 jenkins slave,所以在执行过程中jenkins-slave就会自动创建,任务执行完成,jenkins-slave 对应的pod会自动回收: 在构建日志里我们也可以看到 jenkins 启动了 jenkins-slave-dj3vc pod 进行这个任务的执行,也和上面的 pod 名称对应起来了。 完整 pipeline 示例 部署应用的流程如下: 拉取 Github 代码 maven 打包 编写 Dockerfile 构建打包 Docker 镜像 推送 Docker 镜像到仓库 编写 Kubernetes YAML 文件 更改 YAML 文件中 Docker 镜像 TAG 利用 kubectl 工具部署应用 最终的 Pipeline 脚本如下:pipeline { agent none stages { stage("Clone Code") { agent { label "master" } steps { echo "1.Git Clone Code" git url: "https://github.com/0820sdd/prometheus-test-demo.git" } } stage("Maven Build") { agent { docker { image "maven:latest" args "-v /root/.m2:/root/.m2" } } steps { echo "2.Maven Build Stage" sh "mvn -B clean package -Dmaven.test.skip=true" } } stage("Image Build") { agent { label "master" } steps { echo "3.Image Build Stage" sh "docker build -f Dockerfile --build-arg jar_name=target/prometheus-test-demo-0.0.1-SNAPSHOT.jar -t prometheus-test-demo:${BUILD_ID} . " sh "docker tag prometheus-test-demo:${BUILD_ID} harbor.edu.cn/library/prometheus-test-demo:${BUILD_ID}" } } stage("Push") { agent { label "master" } steps { echo "4.Push Docker Image Stage" sh "docker login --username=admin harbor.edu.cn -p Harbor12345" sh "docker push harbor.edu.cn/library/prometheus-test-demo:${BUILD_ID}" } } }}node("slave") { container("jnlp-kubectl") { stage("Clone YAML") { echo "5. Git Clone YAML To Slave" git url: "https://github.com/0820sdd/prometheus-test-demo.git" } stage("YAML") { echo "6. Change YAML File Stage" sh "sed -i "s#{VERSION}#${BUILD_ID}#g" ./jenkins/scripts/prometheus-test-demo.yaml" } stage("Deploy") { echo "7. Deploy To K8s Stage" sh "kubectl apply -f ./jenkins/scripts/prometheus-test-demo.yaml" } }} 注意,prometheus-test-demo.yaml 可放在 GitHub 的 prometheus-test-demo 仓库里,也可以另外新建一个仓库专门放 YAML 文件。 下面我们分解讲下上面过程的具体含义: 克隆代码 stage("Clone to master") { agent { label "master" } steps { echo "1.Git Clone Stage" git url: "https://github.com/0820sdd/prometheus-test-demo.git" } } 这步就是从 GitHub 上拉取代码,注意这边的 GitHub 仓库仓库比如是 公开的,因为 private 的需要各种权限配置,Jenkins 必须有一个公网 IP 或者是公网域名,但因资源问题,这部分暂时没有办法实现。注意,这边 agent 里面指定运行环境,选择了 master,即是这个步骤在 Jenkins master节点执行。 maven 打包 stage("Maven Build") { agent { docker { image "maven:latest" args "-v /root/.m2:/root/.m2" } } steps { echo "2.Maven Build Stage" sh "mvn -B clean package -Dmaven.test.skip=true" } } maven 构建,我们指定了 maven 打包的 agent 是在 Jenkins 所在节点另起一个 docker 容器,容器的 image 为 maven:latest,并且使用 -v 参数把本地的 /root/.m2 挂载到 容器的 /root/.m2 目录下,下面 steps 的步骤即是在这个 maven 容器里面的具体操作:mvn -B clean package -Dmaven.test.skip=true。 构建镜像 stage("Image Build") { agent { label "master" } steps { echo "3.Image Build Stage" sh "docker build -f Dockerfile --build-arg jar_name=target/prometheus-test-demo-0.0.1-SNAPSHOT.jar -t prometheus-test-demo:${BUILD_ID} . " sh "docker tag prometheus-test-demo:${BUILD_ID} harbor.edu.cn/library/prometheus-test-demo:${BUILD_ID}" } } maven 构建成功,下一步就是使用 maven build 生成的 prometheus-test-demo-0.0.1-SNAPSHOT.jar 包进行 docker build,docker build 的具体命令有2条 bash 命令 组成,第一步 docker build 使用 -f 指定了 Dockerfile 的文件,使用 --build-arg 参数指定了一些参数,比如上面指定了 jar_name 是 target/prometheus-test-demo-0.0.1-SNAPSHOT.jar,最后使用 -t 参数指定了 docker build 的 image 的名称及版本号。第二步就是 使用 docker tag 命令把上一步 docker build 完成的镜像 打 tag 为 harbor.edu.cn/library/prometheus-test-demo:${BUILD_ID},这步打 tag 的步骤是为了上传到 harbor 镜像仓库,可以随时使用。 推送镜像 stage("Push") { agent { label "master" } steps { echo "4.Push Docker Image Stage" sh "docker login --username=admin harbor.edu.cn -p Harbor12345" sh "docker push harbor.edu.cn/library/prometheus-test-demo:${BUILD_ID}" } } 镜像 build 完成,就可以使用 docker push 命令推送到 harbor.edu.cn 镜像仓库。 拉取镜像 node("slave") { container("jnlp-kubectl") { stage("Clone YAML") { echo "5. Git Clone YAML To Slave" git url: "https://github.com/0820sdd/prometheus-test-demo.git" } } } 现在镜像已经打包完成,并推送到了镜像仓库,后面我们所要做的就是拉取 k8s 编排文件,这一步和第一步的 拉取代码实际是一样的,只不过上面的拉取代码是为了 build image,这一步是为了进行部署到 K8S。 注意:这边指定了运行此步骤的节点是在 Jenkins 的 slave 节点下的 jnlp-kubectl container 下,这个 slave 是指在配置 对接 K8S 集群时,在 Pod Template 下指定的 标签列表的名称,必须与这个名称一致,不然 jenkins 执行过程中就会报找不到对应的 label 。还有这边指定了 jnlp-kubectl container ,这是因为 jnlp-kubectl container下有 kubectl 命令,且配置 对接 k8s 集群时,指定了把宿主机的 /root/.kube /etc/kubernetes/pki 目录分别挂载到 container 的 /root/.kube /etc/kubernetes/pki目录下,这边就是 jnlp-kubectl container 可以访问 K8S 集群的原因。 替换 YAML 文件变量 stage("YAML") { echo "6. Change YAML File Stage" sh "sed -i "s#{VERSION}#${BUILD_ID}#g" ./jenkins/scripts/prometheus-test-demo.yaml" } yaml文件拉取完毕,替换其中的变量。 部署 stage("Deploy") { echo "5. Deploy To K8s Stage" sh "kubectl apply -f ./jenkins/scripts/prometheus-test-demo.yaml -n default" } 使用 kubectl 命令部署 prometheus-test-demo 应用到 K8S 集群。 最终执行效果如下 -- END -- 如果你都阅读到这里了,那么我默认你是对这篇文章或者该文作者感兴趣的,可以私信问我要联系方式。 QQ群二维码如下, 添加请注明:地区+职位,否则不予通过