實操進階 | Jenkins 和 Kubernetes 雲上的神祕代理

最近咱們構建和部署服務的方式與原來相比簡直日新月異,像那種笨拙的、單一的、用於構建單體式應用程序的方式已是過去式了。咱們努力了這麼久,終於達到了如今的效果。如今的應用爲了提供更好的拓展性和可維護性,都會去拆解成各類相互依賴小、解耦性強的微服務,這些服務有各自的依賴和進度。若是想去構建服務,從一開始,就應該使用 CI/CD 的方式;固然,若是你走上了這條路, Jenkins 就是你的良師益友。node

若是你是作微服務,那讓咱們在開始以前先花些時間想想。若是隻在 Jenkins 上構建單體式應用程序,那你確定天天都會運行不少 Jenkins job, 並且要不厭其煩地運行不少次。因此,咱們應該好好想清楚如何來作出一些改變。其實只須要付出一些努力,Jenkins 就能夠很好地解決這種事情。golang

Jenkins 進階之路docker

做爲 DevOps 從業者,我遇到的最大問題是如何管理並優化本身的 Jenkins agent 結構。若是隻是實驗性地用 Jenkins跑一些流水線,那根本不用考慮 agent 的事情。若是天天要跑成百上千條流水線的話,那怎麼去作優化就是一件很是很是重要的事情。在 Jenkins 進階之路中,我也嘗試了各類不一樣的方式來尋找Jenkins agent 的最佳使用方式。若是你也和我同樣經歷過,那下面這些事情你必定會很熟悉。express

下面是我在這些年中使用 Jenkins 的各個階段:bash

  1. 全部構建都在 master 節點上跑,在這個節點上運行全部的組件。(我給這個階段起了個可愛的名字, Hello Jenkins)服務器

  2. 建立一個 Jenkins EC2 代理,而且在這個代理上運行全部的構建, 就是大而全,這個節點什麼都能作。若是須要同時作多條任務,那就把這個大而全的節點克隆一份。(這個階段我命名爲 Monster Agent.)網絡

  3. 爲每種服務建立不一樣的 Jenkins EC2 的節點(這個階段叫Snowflake Agent.)socket

  4. 在容器中運行流水線的全部步驟。 好比,在 Jenkins 中使用 Docker Plugin 插件將代理掛載到容器中,或者使用 multi-stage Dockerfiles 把全部構建,測試打包的流程都封裝起來。這兩種方法都是很好的容器抽象化的開端,而且容許輕鬆地將製品從一個容器複製到另外一個容器。固然了,每種方法都須要訪問 Docker engine 。爲了讓個人 Jenkins 代理可以正常工做,如今我用如下幾種方式來管理 docker host:ide

  • 在Jenkins 主容器中運行一個Docker engine - Docker in Docker (DinD);
  • 把主機上的 Docker socket 掛載到容器中,讓容器可以以 sidecar 的方式運行;
  • 爲 Jenkins 主服務器配置單個外部 EC2 Docker 主機,以用於在容器中啓動構建;
  • 使用 EC2 插件和包含 Docker Engine 的 AMI 動態啓動代理,而後運行多階段 Dockerfile 中的全部步驟。 以上這些階段各有利弊,但都是爲了讓咱們從管理 Jenkins 節點中解放出來。不過,最近我又進階到了另一個階段:Jenkins on Kubernetes。

一旦在 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 集羣的資源,首先須要按照如下步驟建立一些憑據:

  1. 進入 Jenkins 的 UI 界面,點擊左邊導航欄裏的憑據連接
  2. 點擊 Stores scoped to Jenkins 列表下 global 中的 Add credentials (將鼠標懸停在連接旁邊便可看到箭頭)
  3. 點擊添加憑證
  4. 寫好 Kubernetes Service Account
  5. 將範圍設置爲全局
  6. 點擊 OK 按鈕 這樣 Jenkins 就可使用這個憑據去訪問 K8s 的資源。

在 Jenkins Master 中配置雲

下一步就是在 Jenkins 中設置雲的配置:

  1. 進入 Jenkins UI 界面,點擊 系統管理 → 系統設置;
  2. 進入管理界面後查找 『雲』(通常在下面),而後點擊 『新增一個雲』,選擇 kubernetes 類型;
  3. 以下這些是必填的參數: Name 自定義, 默認是 kubernetes; Kubernetes URL kubernetes.default -通常是從 service account 自動配置; Kubernetes Namespace 通常是 default 除非要在一個特殊的命名空間 ,不然不要動; Credentials 選擇上一步建立的憑據; Jenkins URL http://<your_jenkins_hostname>:8080; Jenkins tunnel <your_jenkins_hostname>:5555 - 用來和 Jenkins 啓動的 agent 進行交互的端口;

你看,只須要幾個參數就能在 K8s 集羣中啓動一些節點了,固然你的環境須要的話也能夠作一些其餘的調整。

如今你已經能夠經過定義一些 pod 實現Jenkins master 訪問 K8s 集羣了。pod實際上是 K8s 中的概念,在一個 pod 裏面會有一個或者多個容器,它們共享網絡還有存儲,咱們能夠在這個 pod 中執行一些構建工做。每個 Jenkins 節點都是做爲 K8s pod 來啓動的。這個 pod 裏面常常會包含一個默認的 JNLP 容器,還有一些pod 模板中定義的容器。如今有至少兩種方法來定義pod template。

經過 Jenkins UI 配置一個 pod template

  1. Manage Jenkins → Configure Systems;
  2. 找到以前配置 Jenkins K8s 的地方;
  3. 點擊 Add Pod Template button 選擇 Kubernetes Pod Template;
  4. 輸入下面的值: Name 自定義; Namespace default -除非想換個在上一步自定義的命名空間; Labels 自定義 - 這個將用來匹配你在 jenkinsfile 中的 label 值; Usage 若是想讓這個 pod 做爲默認節點的話,就選擇 "Use this node as much as possible", 若是選擇 "Only build jobs with label matching expressions matching this node" 的話 那就是隻有在 Jenkins 腳本中定義的label匹配的構建才能使用這個節點; The name of the pod template to inherit from 這個能夠置空,如今還用不到; Containers 在 pod 中啓動的容器,下面會有詳細介紹; EnvVars 在 pod 中注入的環境變量; Volumes 在 pod 中掛載的任何一種卷;

須要記住,在一個 pod 中會有不止一個容器,它們都是共存的。若是你是用 Helm chart 安裝 Jenkins 的話,pod 中就會包含 JNLP 這個容器,這個容器也是 Jenkins agent 中必須包含的。爲了完成更多服務的構建,還須要添加一些其餘工具鏈的容器。

添加容器模板

  1. 進入 Jenkins UI 界面,回到上一步建立 pod template ;
  2. 點擊 Add Container 按鈕, 選擇 Container Template;
  3. 輸入下面的值: Name 自定義; Docker image 根據需求來寫,好比在構建一個go 寫的應用時,能夠輸入 golang:1.11-alpine3.8; Label 代表要用在流水線腳本中引用此容器模板的標籤字符串; Always pull image 若是想讓 pod 啓動的時候都去拉取鏡像就選擇這個;

你能夠保留其餘參數的默認值,可是能夠看到該插件能夠對 pod 以及在其中運行的各個容器進行詳細控制。你能夠經過此插件設置在 Kubernetes pod 配置中的任何值。你還能夠經過輸入原始 YAML 來注入配置數據。無需因選項過多而分心,選擇配置它們中的一小部分就能夠得到工做環境。

單擊容器模板中的「添加環境變量」按鈕,將環境變量注入特定容器,也能夠單擊模板中的「添加環境變量」按鈕,將環境變量注入全部的容器。

如下環境變量會自動注入默認的 JNLP 容器,來保障它能自動鏈接到 Jenkins 主服務器:

  • JENKINS_URL: Jenkins 網頁界面網址
  • JENKINS_JNLP_URL: Jenkins 特定 slave 中 jnlp 的 url
  • JENKINS_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 定義中可用的任何配置參數添加到 podTemplatecontainerTemplate 部分。

在下面的示例中,我已經定義了一個包含兩個容器模板的 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 代理,構建和部署示例應用程序其實只花了幾個小時。但以後,我花了一個週末的時間才深刻了解該平臺。若是你學得夠快,相信你在幾天內就能夠徹底掌握而且靈活運用這個平臺了。

相關文章
相關標籤/搜索