最近咱們構建和部署服務的方式與原來相比簡直日新月異,像那種笨拙的、單一的、用於構建單體式應用程序的方式已是過去式了。咱們努力了這麼久,終於達到了如今的效果。如今的應用爲了提供更好的拓展性和可維護性,都會去拆解成各類相互依賴小、解耦性強的微服務,這些服務有各自的依賴和進度。若是想去構建服務,從一開始,就應該使用 CI/CD 的方式;固然,若是你走上了這條路, Jenkins 就是你的良師益友。node
若是你是作微服務,那讓咱們在開始以前先花些時間想想。若是隻在 Jenkins 上構建單體式應用程序,那你確定天天都會運行不少 Jenkins job, 並且要不厭其煩地運行不少次。因此,咱們應該好好想清楚如何來作出一些改變。其實只須要付出一些努力,Jenkins 就能夠很好地解決這種事情。golang
Jenkins 進階之路docker
做爲 DevOps 從業者,我遇到的最大問題是如何管理並優化本身的 Jenkins agent 結構。若是隻是實驗性地用 Jenkins跑一些流水線,那根本不用考慮 agent 的事情。若是天天要跑成百上千條流水線的話,那怎麼去作優化就是一件很是很是重要的事情。在 Jenkins 進階之路中,我也嘗試了各類不一樣的方式來尋找Jenkins agent 的最佳使用方式。若是你也和我同樣經歷過,那下面這些事情你必定會很熟悉。express
下面是我在這些年中使用 Jenkins 的各個階段:bash
全部構建都在 master 節點上跑,在這個節點上運行全部的組件。(我給這個階段起了個可愛的名字, Hello Jenkins)服務器
建立一個 Jenkins EC2 代理,而且在這個代理上運行全部的構建, 就是大而全,這個節點什麼都能作。若是須要同時作多條任務,那就把這個大而全的節點克隆一份。(這個階段我命名爲 Monster Agent.)網絡
爲每種服務建立不一樣的 Jenkins EC2 的節點(這個階段叫Snowflake Agent.)socket
在容器中運行流水線的全部步驟。 好比,在 Jenkins 中使用 Docker Plugin 插件將代理掛載到容器中,或者使用 multi-stage Dockerfiles 把全部構建,測試打包的流程都封裝起來。這兩種方法都是很好的容器抽象化的開端,而且容許輕鬆地將製品從一個容器複製到另外一個容器。固然了,每種方法都須要訪問 Docker engine 。爲了讓個人 Jenkins 代理可以正常工做,如今我用如下幾種方式來管理 docker host:ide
一旦在 Jenkins 中把構建節點和 job 都容器化了,遷移工做平臺將變得十分簡單易行。這裏鄭重聲明一下,在使用這個方法前我一直沒有接觸過 Kubernetes。也就是說,在 Google Cloud Platform(GCP)GKE 中建立 Kubernetes 集羣,使用 Helm Chart啓動 Jenkins master ,並在 Kubernetes 集羣中的 Jenkins 代理中運行構建是很是簡單的。微服務
流水線腳本中啓動 K8s 中的代理
如何配置 Jenkins 才能使流水線腳本可以在 K8s 集羣中啓動 Jenkins 節點。首先要先安裝 Kubernetes plugin 插件。有意思的是,當我用 Helm chart 來安裝Jenkins 時,安裝好的 Jenkins 裏面已經有了該插件。還有一個前提,啓動的 Jenkins 節點要和 Jenkins master 在同一個 K8s 集羣裏。
一旦在 K8s 中運行了 Jenkins master 節點,只須要簡單配置幾步,就能啓動一個小構建。
配置 Jenkins Master
爲了保證 Jenkins 可以訪問 K8s 集羣的資源,首先須要按照如下步驟建立一些憑據:
在 Jenkins Master 中配置雲
下一步就是在 Jenkins 中設置雲的配置:
你看,只須要幾個參數就能在 K8s 集羣中啓動一些節點了,固然你的環境須要的話也能夠作一些其餘的調整。
如今你已經能夠經過定義一些 pod 實現Jenkins master 訪問 K8s 集羣了。pod實際上是 K8s 中的概念,在一個 pod 裏面會有一個或者多個容器,它們共享網絡還有存儲,咱們能夠在這個 pod 中執行一些構建工做。每個 Jenkins 節點都是做爲 K8s pod 來啓動的。這個 pod 裏面常常會包含一個默認的 JNLP 容器,還有一些pod 模板中定義的容器。如今有至少兩種方法來定義pod template。
經過 Jenkins UI 配置一個 pod template
須要記住,在一個 pod 中會有不止一個容器,它們都是共存的。若是你是用 Helm chart 安裝 Jenkins 的話,pod 中就會包含 JNLP 這個容器,這個容器也是 Jenkins agent 中必須包含的。爲了完成更多服務的構建,還須要添加一些其餘工具鏈的容器。
添加容器模板
你能夠保留其餘參數的默認值,可是能夠看到該插件能夠對 pod 以及在其中運行的各個容器進行詳細控制。你能夠經過此插件設置在 Kubernetes pod 配置中的任何值。你還能夠經過輸入原始 YAML 來注入配置數據。無需因選項過多而分心,選擇配置它們中的一小部分就能夠得到工做環境。
單擊容器模板中的「添加環境變量」按鈕,將環境變量注入特定容器,也能夠單擊模板中的「添加環境變量」按鈕,將環境變量注入全部的容器。
如下環境變量會自動注入默認的 JNLP 容器,來保障它能自動鏈接到 Jenkins 主服務器:
JENKINS_URL
: Jenkins 網頁界面網址JENKINS_JNLP_URL
: Jenkins 特定 slave 中 jnlp 的 urlJENKINS_SECRET
: 身份驗證的密鑰JENKINS_NAME
: Jenkins 代理的名稱若是單擊「添加捲」按鈕,將看到幾個用於添加捲的選項,這裏使用 Host Path Volume 選項將 docker socket 安裝在 pod 中。而後,能夠運行安裝了 Docker 客戶端的容器,而且來構建和推送 Docker 鏡像。
此時,咱們爲 Kubernetes 集羣建立了一個雲配置,並定義了一個由一個或多個容器組成的 pod。如今如何使用它來運行 Jenkins 工做?
很簡單,只須要咱們在 Jenkins 流水線腳本中經過標籤引用 pod 和容器就能夠了。 本文中的示例是使用腳本流水線,固然您可使用聲明式流水線語法實現相同的結果:
node('test-pod') {
stage('Checkout') {
checkout scm
}
stage('Build'){
container('go-agent') {
// This is where we build our code.
}
}
}
複製代碼
用 jenkinsfile 來實現相同的功能
經過 UI 配置插件如今看起來很不錯。但有一個明顯的問題,配置不能像源代碼同樣進行版本控制和存儲。幸運的是,您能夠直接在 Jenkinsfile 中建立整個 pod 定義。哈哈,在 Jenkinsfile 中有什麼不能作的呢?
能夠將 UI 或 YAML 定義中可用的任何配置參數添加到 podTemplate
和containerTemplate
部分。
在下面的示例中,我已經定義了一個包含兩個容器模板的 pod。
pod 標籤將會用於節點,表示想要啓動此 pod 實例。
直接在節點內定義但沒有在容器塊中定義的任何步驟,均可以在默認的 JNLP 容器中運行。
容器塊用於表示該容器塊內的步驟應在具備給定標籤的容器內運行。我已經定義了一個標籤爲 golang
的容器模板,我將用它來構建 Go 可執行文件,最終將其打包成 Docker 鏡像。在 volumes
中,已經指出想要掛載主機的 Docker 套接字,但仍然須要 Docker 客戶端使用 Docker API 與它進行交互。所以,已經定義了一個標籤爲 docker
的容器模板,該模板使用安裝了 Docker 客戶端的鏡像。
podTemplate(
name: 'test-pod',
label: 'test-pod',
containers: [
containerTemplate(name: 'golang', image: 'golang:1.9.4-alpine3.7'),
containerTemplate(name: 'docker', image:'trion/jenkins-docker-client'),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock',
hostPath: '/var/run/docker.sock',
],
{
//node = the pod label
node('test-pod'){
//container = the container label
stage('Build'){
container('golang'){
// This is where we build our code.
}
}
stage('Build Docker Image'){
container(‘docker’){
// This is where we build the Docker image
}
}
}
})
複製代碼
在基於 Docker 的流水線腳本中,我構建了 Docker 鏡像並將其推送到 Docker 倉庫,對我來講,可以複製這些配置信息很是重要。完成後,我已準備好使用gcloud
(Google Cloud SDK)構建鏡像,並將該鏡像推送到 Google Container Registry,以便部署到 K8s 羣集。
使用 gcloud 鏡像指定了一個容器模板,並將 docker 命令更改成 gcloud 命令。、
就這麼簡單!
podTemplate(
name: 'test-pod',
label: 'test-pod',
containers: [
containerTemplate(name: 'golang', image: 'golang:1.9.4-alpine3.7'),
containerTemplate(name: 'gcloud', image:'gcr.io/cloud-builders/gcloud'),
],
{
//node = the pod label
node('test-pod'){
//container = the container label
stage('Build'){
container('golang'){
// This is where we build our code.
}
}
stage('Build Docker Image'){
container(‘gcloud’){
//This is where we build and push our Docker image.
}
}
}
})
複製代碼
在 Kubernetes 上運行 Jenkins master、Jenkins 代理,構建和部署示例應用程序其實只花了幾個小時。但以後,我花了一個週末的時間才深刻了解該平臺。若是你學得夠快,相信你在幾天內就能夠徹底掌握而且靈活運用這個平臺了。