在k8s上安裝Jenkins及常見問題

  • 2019 年 11 月 4 日
  • 筆記

持續集成和部署是DevOps的重要組成部分,Jenkins是一款非常流行的持續集成和部署工具,最近試驗了一下Jenkins,發現它是我一段時間以來用過的工具中最複雜的。一個可能的原因是它需要與各種其它工具集成才能完成任務,而集成的方法又各不相同。在這些工具中,Docker是最簡單的,真的非常好用。K8s比較複雜,開始要花些時間熟悉,但它的整體設計十分合理,一旦搞清核心概念,掌握脈絡之後,就非常順利。它的命令格式即規範又統一,使得有些命令自己都能猜出來,這就是好的設計帶來的福利。。但Jenkins給人的感覺就是開始的時候沒有設計得很好,後面在不斷地打修補程式,導致一件事情有好幾種不同的做法,對不熟悉的人來講無所適從。沒有統一的風格,處處都是意外,使得整個系統看起來既龐雜又沒有章法,當然這也跟它出來的時間比較長有關。雖然它可能不是最好的,但它是免費的,因此不能要求太高。

由於種種原因,我的Jenkins安裝碰到了各種各樣的問題,為此我查看了大量的資料。但遺憾的是每個人安裝Jenkins的方法都有些不同,很難找到一篇文章能解決所有問題。在我看來,Jenkins的安裝有兩三個關鍵之處,非常容易出錯,一定要理解透徹才能成功。

本文分成兩部分,第一部分講正常安裝步驟,如果一切順利,就不需要看第二部分了。我只能說恭喜你,你的運氣太好了。第二部分是講各種問題及解決辦法,這應該是本文最有價值的部分。

第一部分:在 k8s上部署Jenkins

1. 安裝在什麼地方?

容器化是大勢所趨,它不但包括應用程式的容器化,還包括與之相關的工具的容器化。當把Jenkins部署在K8s上時,Jenkins的主節點會根據情況自動生成子節點(新的容器)來完成任務,任務結束後會自動銷毀子節點。

我先在Windows上部署了VirtulBox虛機,並用Vagrant來管理虛機,再在虛機上部署了k8s。並通過Vagrant設置虛機和宿主機之間的網路共享,這樣就可以在宿主機上用遊覽器直接訪問k8s上的Jenkins。另外還要把宿主機的硬碟掛載到Jenkins上,這樣Jenkins的物理存儲還是在宿主機上,即使虛機出了問題,所有的配置和數據都不會丟失。

2. 選擇鏡像文件

這個看起來不是問題,但是一不留神就容易出錯。我就是因為選錯了鏡像,導致安裝了很多遍,最後才成功,在本文的第二部分會詳細說明。我最終用的鏡像文件是「jenkinsci/jenkins:2.154-slim」,後來發現這個是比較舊的版本,新的鏡像 是「jenkins/jenkins:lts」, 但因為已經安裝成功了,就沒有再換。Jenkins真的很坑人,有三個鏡像「Jenkins」,「jenkinsci/jenkins」, "jenkins/jenkins", 其中正確的是"jenkins/jenkins"。

選好鏡像之後,可以先運行下面命令,下載Jenkins鏡像文件到本地(虛機上)。

vagrant@ubuntu-xenial:~$ docker pull jenkinsci/jenkins:2.154-slim

3. 安裝Jenkins鏡像:

在安裝之前,需要先把宿主機的Jenkins安裝目錄掛載到虛機上,這樣可以在本地直接操作Jenkins。
下面是Vagrant的配置文件(Vagrantfile)中的設置,它把宿主機的app目錄掛載到虛機的"/home/vagrant/app"。Jenkins就安裝在app目錄下。

config.vm.synced_folder "app/", "/home/vagrant/app", id: "app"

下面就是在宿主機上安裝好了的Jenkins目錄

file

安裝Jenkins鏡像分成四部分,創建服務賬戶,安裝持久卷,安裝部署和安裝服務,需要按順序進行。其中的關鍵是創建服務賬戶,這個是必須的,沒有它不會成功。不知為什麼網上的有些文章沒有提到它。

服務賬戶配置文件(service-account.yaml):

kind: ClusterRole  apiVersion: rbac.authorization.k8s.io/v1  metadata:    namespace: default    name: service-reader  rules:    - apiGroups: [""] # "" indicates the core API group      resources: ["services"]      verbs: ["get", "watch", "list"]    - apiGroups: [""]      resources: ["pods"]      verbs: ["create","delete","get","list","patch","update","watch"]    - apiGroups: [""]      resources: ["pods/exec"]      verbs: ["create","delete","get","list","patch","update","watch"]    - apiGroups: [""]      resources: ["pods/log"]      verbs: ["get","list","watch"]    - apiGroups: [""]      resources: ["secrets"]      verbs: ["get"]  

這裡創建了一個名為「service-reader」的「ClusterRole」,並把特定的許可權(例如["get", "watch", "list"])賦給特定的資源(例如["services"])。

運行如下命令,創建一個名為「service-reader-pod」的集群角色綁定,它的「clusterrole」是「service-reader」,它的名字是「default:default」,其中第一個「default」是名空間(namespace),第二個「default」是服務賬戶名字,後面的部署配置文件會引用這個名字(default)。這裡由於我沒有給Jenkins創建單獨的名空間,因此它用的默認名空間(「default」)。

kubectl create clusterrolebinding service-reader-pod --clusterrole=service-reader  --serviceaccount=default:default

關於服務賬戶的許可權定義,請參閱「Kubernetes plugin for Jenkins」 .

持久卷配置文件(jenkins-volumn.yaml):

apiVersion: v1  kind: PersistentVolume  metadata:    name: k8sdemo-jenkins-pv    labels:      app: k8sdemo-jenkins  spec:    capacity:      storage: 1Gi    # volumeMode field requires BlockVolume Alpha feature gate to be enabled.    volumeMode: Filesystem    accessModes:      - ReadWriteOnce    storageClassName: standard    local:      path: /home/vagrant/app/jenkins    nodeAffinity:      required:        nodeSelectorTerms:          - matchExpressions:              - key: kubernetes.io/hostname                operator: In                values:                  - minikube  ---  apiVersion: v1  kind: PersistentVolumeClaim  metadata:    name: k8sdemo-jenkins-pvclaim    labels:      app: k8sdemo-jenkins  spec:    accessModes:      - ReadWriteOnce    # storageClassName: local-storage    resources:      requests:        storage: 1Gi #1 GB

部署配置文件(jenkins-deployment.yaml):

apiVersion: apps/v1  kind: Deployment  metadata:    name: k8sdemo-jenkins-deployment    labels:      app: k8sdemo-jenkins  spec:    selector:      matchLabels:        app: k8sdemo-jenkins    strategy:      type: Recreate    template:      metadata:        labels:          app: k8sdemo-jenkins      spec:        serviceAccountName: default # 服務賬戶的名字是default        containers:          - image: jenkinsci/jenkins:2.154-slim            name: k8sdemo-jenkins-container            imagePullPolicy: Never            ports:              - containerPort: 8080              - containerPort: 50000            volumeMounts:              - name: k8sdemo-jenkins-persistentstorage                mountPath: /var/jenkins_home        volumes:          - name: k8sdemo-jenkins-persistentstorage            persistentVolumeClaim:              claimName: k8sdemo-jenkins-pvclaim  

注意,這裡引用了服務賬戶「default」(serviceAccountName: default)。

服務配置文件(jenkins-service.yaml):

apiVersion: v1  kind: Service  metadata:    name: k8sdemo-jenkins-service    labels:      app: k8sdemo-jenkins  spec:    type: NodePort    selector:      app: k8sdemo-jenkins    ports:      - port: 8080        name: http        protocol : TCP        nodePort: 30080        targetPort: 8080      - port: 50000        name: agent        protocol: TCP        targetPort: 50000  

這裡面的一個關鍵點是部署和服務都暴露了兩個容器埠,一個是8080,另一個是50000. 「8080」是外部訪問Jenkins的埠,「50000」是Jenkins內部集群之間的互相通訊埠。這裡的Jenkins集群不需要你搭建,而是Jenkins根據需要自動生成的,因此這兩個埠是必須配置的。這裡的配置命令都是比較標準的k8s配置,因此沒有詳細解釋。

如果你想了解k8s命令詳情(包括Vagrant配置),請參閱「通過搭建MySQL掌握k8s(Kubernetes)重要概念(上):網路與持久卷」.

運行下面命令創建Jenkins:

kubectl apply -f jenkins-volume.yaml  kubectl apply -f jenkins-deployment.yaml  kubectl apply -f jenkins-service.yaml

驗證安裝:

獲得Jenkins的Pod名

vagrant@ubuntu-xenial:~$ kubectl get pod  NAME                                           READY   STATUS    RESTARTS   AGE  envar-demo                                     1/1     Running   15         27d  k8sdemo-backend-deployment-6b99dc6b8c-tbl4v    1/1     Running   7          11d  k8sdemo-database-deployment-578fc88c88-mm6x8   1/1     Running   9          16d  k8sdemo-jenkins-deployment-675dd574cb-bt7rx    1/1     Running   2          24h

查看Jenkins日誌

vagrant@ubuntu-xenial:~$ kubectl logs k8sdemo-jenkins-deployment-675dd574cb-bt7rx    Running from: /usr/share/jenkins/jenkins.war  webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")  Nov 02, 2019 1:33:30 AM org.eclipse.jetty.util.log.Log initialized  INFO: Logging initialized @3749ms to org.eclipse.jetty.util.log.JavaUtilLog    。。。    INFO: Invalidating Kubernetes client: kubernetes null  Nov 02, 2019 1:35:50 AM hudson.WebAppMain$3 run  INFO: Jenkins is fully up and running  --> setting agent port for jnlp  --> setting agent port for jnlp... done

當看到「INFO: Jenkins is fully up and running」,就說明Jenkins已經運行好了,Jenkins的第一次啟動需要一定時間,要耐心等待。

4. 登錄

前面已經講過,你可以在Vagrant里設置宿主機和虛機之間的網路互訪,我的虛機的地址是「192.168.50.4」,「30080」是Jenkins服務的NodePort的對外地址,因此可以用「http://192.168.50.4:30080/」 訪問Jenkins。

登錄之前先要獲得初始口令,你可以在Jenkins的「secretsinitialAdminPassword」目錄里獲得管理員用戶初始口令,我掛載Jenkins的宿主機目錄是「E:app2kubappjenkins」, 因此口令文件是「E:app2kubappjenkinssecretsinitialAdminPassword」。口令是「072d7157c090479195e0acaa97bc1049」。第一次登錄之後,需要重新設置用戶和口令。

5. 安裝推薦插件

登錄之後,先要安裝必要的插件才能完成整個安裝工程, 直接選「Install suggested plugins」就可以了。
file

6 安裝Kubernetes Plugin

用管理員賬戶登錄 Jenkins主頁面後,找到
Manage Jenkins-》Manage Plugins-》Available, 勾選安裝「Kubernetes plugin」即可。

下圖是安裝之後的圖:

file

7. 配置Kubernetes Plugin

用管理員賬戶登錄 Jenkins Master主頁面後,找到
Manage Jenkins-》Configure System-》,然後配置Kubernetes Plugin。如下圖所示:

file

這是最重要的一個配置,決定整個安裝的成敗。默認的「name」是「Kubernetes「,這個不需要修改,但以後配置Pipelines時要用到。「Kubernetes URL」用 「https://kubernetes.default」 就可以了。設置之後點擊「Test Connection」,見到「Connection test successful」就成功了。

「Jenkins URL」是從外部(從虛擬機而不是宿主機)訪問Jenkins的地址。
你可以用如下命令,找到Kubernetes的「Jenkins Url」:

vagrant@ubuntu-xenial:~$  sudo minikube service k8sdemo-jenkins-service  --url  http://10.0.2.15:30080  http://10.0.2.15:32289

另外一個參數是「Jenkins tunnel」,這個參數是Jenkins Master和Jenkins Slave之間通訊必須配置的,但不知道為什麼,網上的很多文章都沒提這個參數,也許是Jenkins的版本不同,有些版本可能不需要。

查看容器名

vagrant@ubuntu-xenial:~$ docker ps -a  CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS                      PORTS               NAMES  d15e30169568        f793ea0abe00            "/sbin/tini -- /usr/…"   15 minutes ago      Up 15 minutes                                   k8s_k8sdemo-jenkins-container_k8sdemo-jenkins-deployment-675dd574cb-2thn2_default_fb10e438-0231-4fd2-8dbd-d9e2f0bb9d09_0

查看容器地址:

vagrant@ubuntu-xenial:~$ docker inspect d15e |grep _8080                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP_ADDR=10.100.3.79",                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP=tcp://10.100.3.79:8080",                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP_PROTO=tcp",                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP_PORT=8080",

根據上面資訊,Jenkins的地址是「tcp://10.100.3.79:8080」,把8080換成50000就可以了。最終結果是「10.100.3.79:50000」,注意不要添加「http」。

8. 測試Jenkins:

現在Jenkins已經全部安裝好了, 下面進行測試。在Jenkins主頁面點擊「New Item」創建新項目,如下圖所示,輸入項目名,然後選擇「Pipeline」。

file

進入項目配置頁面,如下圖所示,腳本文件是jenkinsfile-test:

file

這是最簡單的測試,它直接使用Jenkins主節點(主節點名是master),不需要啟動子節點,因此基本上都不會有什麼問題。
在Jenkins主頁面選項目「test」,然後選「Build Now」運行項目,再到「Console Output」中查看結果如下:

Running in Durability level: MAX_SURVIVABILITY  [Pipeline] Start of Pipeline[Pipeline] podTemplate[Pipeline] {[Pipeline] nodeRunning on Jenkins in /var/jenkins_home/workspace/test  [Pipeline] {[Pipeline] stage[Pipeline] { (Run shell)[Pipeline] sh+ echo hello world.  hello world.  [Pipeline] }  [Pipeline] // stage  [Pipeline] }  [Pipeline] // node  [Pipeline] }  [Pipeline] // podTemplate  [Pipeline] End of Pipeline  Finished: SUCCESS  

9. 測試子節點:

這是複雜一點的測試,需要啟動子節點,這個才能真正檢測出安裝的成敗。先創建一個新的項目「slave-test」。

  def POD_LABEL = "testpod-${UUID.randomUUID().toString()}"  podTemplate(label: POD_LABEL, cloud: 'kubernetes', containers: [      containerTemplate(name: 'build', image: 'jfeng45/k8sdemo-backend:1.0', ttyEnabled: true, command: 'cat'),      containerTemplate(name: 'run', image: 'jfeng45/k8sdemo-backend:1.0', ttyEnabled: true, command: 'cat')    ]) {        node(POD_LABEL) {          stage('build a go project') {              container('build') {                  stage('Build a go project') {                      sh 'echo hello'                  }              }          }            stage('Run a Golang project') {              container('run') {                  stage('Run a Go project') {                      sh '/root/main.exe'                  }              }          }        }  }  

上面是腳本(jenkins-salve-test)。其中「POD_LABEL」取任何名字都可以(在Kubernetes-plugin 1.17.0 版本之後,系統會自動命名,但以前需要自己取名),「cloud: ‘kubernetes’」要與前面定義的「Kubernetes Plugin」 相匹配。它有兩個stage,一個是「build」,另一個是「run」。在「podTemplate」里定義了每一個stage的鏡像(這樣後面的stage腳本里就可以引用),這裡為了簡便把兩個鏡像設成是一樣的。因為是測試,第一個stage只是輸出「echo hello」, 第二個運行鏡像「jfeng45/k8sdemo-backend:1.0」里的main.exe程式。

在Jenkins主頁面選項目「slave-test」,然後選「Build Now」運行項目,再到「Console Output」中查看結果如下:

Running in Durability level: MAX_SURVIVABILITY  [Pipeline] Start of Pipeline[Pipeline] podTemplate[Pipeline] {[Pipeline] node  Still waiting to schedule task  『testpod-f754a7a4-6883-4be0-ba4f-c693906041ae-fjwqs-kbb7l』 is offline    Agent testpod-f754a7a4-6883-4be0-ba4f-c693906041ae-fjwqs-kbb7l is provisioned from template Kubernetes Pod Template  Agent specification [Kubernetes Pod Template] (testpod-f754a7a4-6883-4be0-ba4f-c693906041ae):  * [build] jfeng45/k8sdemo-backend:1.0  * [run] jfeng45/k8sdemo-backend:1.0    Running on testpod-f754a7a4-6883-4be0-ba4f-c693906041ae-fjwqs-kbb7l in /home/jenkins/workspace/slave-test  [Pipeline] {  [Pipeline] stage[Pipeline] { (build a go project)  [Pipeline] container[Pipeline] {  [Pipeline] stage  [Pipeline] { (Build a go project)[Pipeline] sh  + echo heollo  heollo    [Pipeline] }  [Pipeline] // stage  [Pipeline] }  [Pipeline] // container  [Pipeline] }    [Pipeline] // stage  [Pipeline] stage  [Pipeline] { (Run a Golang project)[Pipeline] container  [Pipeline] {[Pipeline] stage[Pipeline] { (Run a Go project)  [Pipeline] sh  + /root/main.exe  time="2019-11-03T01:56:59Z" level=debug msg="connect to database "  time="2019-11-03T01:56:59Z" level=debug msg="dataSourceName::@tcp(:)/?charset=utf8"  time="2019-11-03T01:56:59Z" level=debug msg="FindAll()"  time="2019-11-03T01:56:59Z" level=debug msg="user registere failed:dial tcp :0: connect: connection refused"    [Pipeline] }  [Pipeline] // stage    [Pipeline] }  [Pipeline] // container  [Pipeline] }    [Pipeline] // stage  [Pipeline] }  [Pipeline] // node    [Pipeline] }  [Pipeline] // podTemplate    [Pipeline] End of Pipeline  Finished: SUCCESS

運行成功,測試階段就完成了。

用腳本來寫Pipeline有兩種方法,「Scripted Pipleline」和「Declarative Pipleline」,這裡用的是第一種方法。詳情請見「Using a Jenkinsfile」. 「Declarative Pipleline」是新的方法,我在以後的文章里會講到。這裡因為是測試,只要通過了就行。

不必須的安裝步驟:

還有些安裝步驟在某些文章中提到了,但它們只是錦上添花,不是必須的。如果你的配置出現了問題,不要懷疑是這些步驟沒執行造成的。

  1. 配置名空間(namespace):
    有些安裝步驟為Jenkins配置了單獨的名空間,這樣當然更好,但你即使沒有配置也不會出現問題。
  2. Kubernetes server certificate key:
    有些安裝步驟提到要配置「Kubernetes server certificate key 」,但我並沒有設置它,也沒有影響運行。

第二部分: 常見問題

1. Jenkins版本不對:

最開始用的是jenkins:2.60.3-alpine(這個已經是Jenkins鏡像的最高版本了),這個版本太低,在安裝插件時基本上都不成功,如下圖

file

後來換成jenkins:latest,這個應該是最新的吧,結果 版本還是一樣的,只不過Linux不是Apline的。

後來終於明白了是鏡像錯了(而不是版本的問題),是要用Jenkinsci, 而不是Jenkins。我用了當時排在第一位的jenkinsci/jenkins:2.150.1-slim,安裝之後,上面的插件錯誤全部消失了,真不容易。

2. 不支援Kubernetes Plugin

但當安裝Kubernetes Plugin插件時,提示需要 2.150.3(我的是2.150.1),這也太坑了。只好再次重裝,這次用的是jenkinsci/jenkins:2.154-slim,還好終於成功了。不過這個其實還是以前的鏡像,最新的在「jenkins/jenkins」。

3. 不能訪問Kubernetes

錯誤資訊如下:

Forbidden!Configured service account doesn't have access. Service account may have been revoked. User "system:serviceaccount:default:default" cannot get services in the namespace "default"

詳情請參見Kubernetes log, User 「system:serviceaccount:default:default」 cannot get services in the namespace

錯誤原因是沒有建立service account。解決辦法是先創建「service-account.yaml」文件,然後運行如下命令:

kubectl create clusterrolebinding service-reader-pod --clusterrole=service-reader  --serviceaccount=default:default

再次運行,錯誤消失。

4. Jenkins URL地址不對

在Jenkins主頁面,進入Manage Jenkins-》System Log-》All Jenkins Logs, 錯誤資訊如下。

SEVERE: http://192.168.50.4:30080/ provided port:50000 is not reachable  java.io.IOException: http://192.168.50.4:30080/ provided port:50000 is not reachable       at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:303)       at hudson.remoting.Engine.innerRun(Engine.java:527)       at hudson.remoting.Engine.run(Engine.java:488)

這個錯誤主要是和Kubernetes-plugin配置有關。在Jenkins主頁面,進入Manage Jenkins-》Configure System》,在「http://192.168.50.4:30080/configure」 里有兩個「Jenkins URL」,不要弄混了。
第一個是「Jenkins Location」下的「Jenkins URL」, 它是宿主機訪問Jenkins的地址。

file

第二個是「Cloud」下的「Jenkins URL」, 它是從虛擬機訪問Jenkins的地址。

file

在上圖中,我開始時用的是「http://192.168.50.4:30080/」 ,但這個是從宿主機訪問Jenkins的Url,不是從虛機內部訪問的Url。
你可以用如下命令,找到Kubernetes的「Jenkins Url」

vagrant@ubuntu-xenial:~$  sudo minikube service k8sdemo-jenkins-service  --url  http://10.0.2.15:30080  http://10.0.2.15:32289

鍵入如下命令測試URL。

vagrant@ubuntu-xenial:~$ curl http://10.0.2.15:30080  <html><head><meta http-equiv='refresh' content='1;url=/login?from=%2F'/><script>window.location.replace('/login?from=%2F');</script></head><body style='background-color:white; color:white;'>    Authentication required  <!--  You are authenticated as: anonymous  Groups that you are in:    Permission you need to have (but didn't): hudson.model.Hudson.Read  ... which is implied by: hudson.security.Permission.GenericRead  ... which is implied by: hudson.model.Hudson.Administer  -->    </body></html> 

這就說明URL是好的。

5. 不能連接slave

「Jenkins Url」改了之後,地址是對的,但還是不通。運行項目時,頁面顯示如下資訊:

file

「Console Output」(在Jenkins->salve-test->#13中,其中#13是build #)顯示如下資訊:

Running in Durability level: MAX_SURVIVABILITY  [Pipeline] Start of Pipeline[Pipeline] podTemplate[Pipeline] {[Pipeline] node  Still waiting to schedule task  『testpod-d56038a0-45a2-41d1-922d-2879e3610900-0hr0m-sfv8s』 is offline

後來發現還有一個參數要填寫,就是「Jenkins tunnel」。如下圖所示。

file

詳情請見 Kubernetes Jenkins plugin – slaves always offline.

填寫之後原來的資訊沒有了,而且出現了「Agent discovery successful」,這個資訊是原來沒有的。但又有新的錯誤。
可用如下方法查看系統日誌,在Jenkins主頁面,選擇Manage Jenkins-》System Log-》All Jenkins Logs, 資訊是這樣的:

INFO: Agent discovery successful    Agent address: http://10.0.2.15    Agent port:    50000    Identity:      3e:1b:5f:48:f7:5b:f8:6d:ea:49:1d:b9:44:9a:2f:6c  Oct 30, 2019 12:18:51 AM hudson.remoting.jnlp.Main$CuiListener status  INFO: Handshaking  Oct 30, 2019 12:18:51 AM hudson.remoting.jnlp.Main$CuiListener status  INFO: Connecting to http://10.0.2.15:50000  Oct 30, 2019 12:18:51 AM hudson.remoting.jnlp.Main$CuiListener error  SEVERE: null  java.nio.channels.UnresolvedAddressException       at sun.nio.ch.Net.checkAddress(Net.java:101)       at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:622)       at java.nio.channels.SocketChannel.open(SocketChannel.java:189)       at org.jenkinsci.remoting.engine.JnlpAgentEndpoint.open(JnlpAgentEndpoint.java:203)       at hudson.remoting.Engine.connectTcp(Engine.java:678)       at hudson.remoting.Engine.innerRun(Engine.java:556)       at hudson.remoting.Engine.run(Engine.java:488)

它的原因是「JenkinsTunnel」的地址還是不對,可用如下方法找到「Jenkins tunnel」地址:

vagrant@ubuntu-xenial:~$ docker ps -a  CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS                      PORTS               NAMES  d15e30169568        f793ea0abe00            "/sbin/tini -- /usr/…"   15 minutes ago      Up 15 minutes                                   k8s_k8sdemo-jenkins-container_k8sdemo-jenkins-deployment-675dd574cb-2thn2_default_fb10e438-0231-4fd2-8dbd-d9e2f0bb9d09_0    vagrant@ubuntu-xenial:~$ docker inspect d15e |grep _8080                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP_ADDR=10.100.3.79",                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP=tcp://10.100.3.79:8080",                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP_PROTO=tcp",                  "K8SDEMO_JENKINS_SERVICE_PORT_8080_TCP_PORT=8080",

根據上面資訊,Jenkins容器地址是「tcp://10.100.3.79:8080」,把8080換成50000就可以了。最終結果是「10.100.3.79:50000」,注意不要添加「http」。
詳情請見 What setting to use for jenkins tunnel?

6. 鏡像問題

當使用的鏡像文件是「k8sdemo-backend:latest」或「k8sdemo-backend:1.0」時,「Console Output」顯示錯誤如下:

Running in Durability level: MAX_SURVIVABILITY  [Pipeline] Start of Pipeline[Pipeline] podTemplate[Pipeline] {[Pipeline] nodeStill waiting to schedule task  All nodes of label 『testpod-2971e0ce-e023-475f-b0ec-6118c5699188』 are offline  Aborted by admin[Pipeline] // node  [Pipeline] }  [Pipeline] // podTemplate  [Pipeline] End of Pipeline  Finished: ABORTED

查看Pod, 錯誤是「ImagePullBackOff」:

vagrant@ubuntu-xenial:~$ kubectl get pod  NAME                                                       READY   STATUS             RESTARTS   AGE  envar-demo                                                 1/1     Running            15         28d  k8sdemo-backend-deployment-6b99dc6b8c-tbl4v                1/1     Running            7          12d  k8sdemo-database-deployment-578fc88c88-mm6x8               1/1     Running            9          17d  k8sdemo-jenkins-deployment-675dd574cb-bt7rx                1/1     Running            2          2d  testpod-2971e0ce-e023-475f-b0ec-6118c5699188-xwwqq-vv59p   2/3     ImagePullBackOff   0          38s

查看鏡像:

vagrant@ubuntu-xenial:~$ docker image ls  REPOSITORY                                TAG                 IMAGE ID            CREATED             SIZE  jfeng45/k8sdemo-backend                   1.0                 f48d362fdebf        11 days ago         14.4MB  k8sdemo-backend                           1.0                 f48d362fdebf        11 days ago         14.4MB  k8sdemo-backend                           latest              f48d362fdebf        11 days ago         14.4MB

這裡一共有三個「k8sdemo-backend」鏡像,它們的「Image ID」都是一樣的,之所以有三個是因為我用如下命令創建了tag

docker tag k8sdemo-backend jfeng45/k8sdemo-backend:1.0

但創建了之後,就只有「jfeng45/k8sdemo-backend:1.0」(最晚創建的)能夠用在Jenkins的Pipeline腳本里,其他兩個都會報錯。修改了正確的鏡像文件之後就運行成功了。

7. pv和pvc刪除慢

當用以下命令刪除pv時,命令遲遲不能返回。

kubectl delete pv k8sdemo-jenkins-pv

當你查看時,狀態(status)顯示一直是「Terminating」,但總是不能結束退出。pvc也是一樣。

vagrant@ubuntu-xenial:~$ kubectl get pv  NAME                  CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS        CLAIM                              STORAGECLASS   REASON   AGE  k8sdemo-backend-pv    1Gi        RWO            Retain           Bound         default/k8sdemo-backend-pvclaim    standard                13d  k8sdemo-database-pv   1Gi        RWO            Retain           Bound         default/k8sdemo-database-pvclaim   standard                18d  k8sdemo-jenkins-pv    1Gi        RWO            Retain           Terminating   default/k8sdemo-jenkins-pvclaim    standard                6d8h

這個主要原因是用到它們的服務和部署還在運行,先把服務和部署刪除之後,pv和pvc的刪除操作就馬上結束,順利返回了。

源碼:

完整源碼的github鏈接

注意,本文的程式在0.1(tag)下,這個程式的主分支以後還會修改。

下面是程式的目錄結構,黃色部分是與本文有關的配置文件。

file

索引:

  1. Kubernetes plugin for Jenkins
  2. 通過搭建MySQL掌握k8s(Kubernetes)重要概念(上):網路與持久卷
  3. Using a Jenkinsfile
  4. Kubernetes log, User 「system:serviceaccount:default:default」 cannot get services in the namespace
  5. Kubernetes Jenkins plugin – slaves always offline.
  6. What setting to use for jenkins tunnel?
  7. 初試 Jenkins 使用 Kubernetes Plugin 完成持續構建與發布

本文由部落格一文多發平台 OpenWrite 發布!