Kubernetes入門(八)

30.12 基於K8S部署Jenkins


前面我們已經安裝了集羣相關插件,包括harbor倉庫。有了鏡像倉庫,那部署應用就很方便。接下來繼續部署Jenkins及Gitlab。


部署helm

相信很多人都使用過Ubuntu下的ap-get或者CentOS下的yum, 這兩者都是Linux系統下的包管理工具。採用apt-get/yum,應用開發者可以管理應用包之間的依賴關係,發佈應用;用戶則可以以簡單的方式查找、安裝、升級、卸載應用程序。

我們可以將Helm看作Kubernetes下的apt-get/yum。Helm是Deis (https://deis.com/) 開發的一個用於kubernetes的包管理器。每個包稱爲一個Chart,一個Chart是一個目錄(一般情況下會將目錄進行打包壓縮,形成name-version.tgz格式的單一文件,方便傳輸和存儲)。

對於應用發佈者而言,可以通過Helm打包應用,管理應用依賴關係,管理應用版本併發布應用到軟件倉庫。

對於使用者而言,使用Helm後不用需要了解Kubernetes的Yaml語法並編寫應用部署文件,可以通過Helm下載並在kubernetes上安裝需要的應用。

除此以外,Helm還提供了kubernetes上的軟件部署,刪除,升級,回滾應用的強大功能。

在線安全安裝helm

在helm客戶端和tiller服務器間建立安全的SSL/TLS認證機制;tiller服務器和helm客戶端都是使用同一CA簽發的client cert,然後互相識別對方身份。

在deploy節點執行安裝步驟:

  • 如果已經安裝非安全模式的helm,先卸載:
# helm reset
  • 修改配置:
# vim /etc/ansible/roles/helm/defaults/main.yml
helm_namespace: kube-system
helm_cert_cn: helm001
tiller_sa: tiller
tiller_cert_cn: tiller001
tiller_image: jmgao1983/tiller:v2.12.3
#repo_url: https://kubernetes-charts.storage.googleapis.com
# 如果默認官方repo 網絡訪問不穩定可以使用如下的阿里雲鏡像repo
repo_url: https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
#repo_url: http://127.0.0.1:8879
  • 執行安裝:
# ansible-playbook /etc/ansible/roles/helm/helm.yml
  • 查看版本:
# helm version --tls
Client: &version.Version{SemVer:"v2.11.0", GitCommit:"2e55dbe1fdb5fdb96b75ff144a339489417b146b", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.12.3", GitCommit:"eecf22f77df5f65c823aacd2dbd30ae6c65f186e", GitTreeState:"clean"}

注意

因爲使用了TLS認證,所以helm命令執行分以下兩種情況:

  • 執行與tiller服務有關的命令,比如 helm ls helm version helm install等需要加--tls參數
  • 執行其他命令,比如helm search helm fetch helm home等不需要加--tls參數
  • helm v2.11.0及以上版本,啓用環境變量 export HELM_TLS_ENABLE=true,可以都不用加--tls參數

離線安全安裝helm

在內網環境中,由於不能訪問互聯網,無法連接repo地址,使用上述的在線安裝helm的方式會報錯。因此需要使用離線安裝的方法來安裝。

  • 創建本地repo:
# mkdir /opt/helm-repo
  • 啓動helm repo server,如果有其它服務器訪問,則改爲本地IP:
# nohup helm serve --address 127.0.0.1:8879 --repo-path /opt/helm-repo &
  • 修改配置:
# vim /etc/ansible/roles/helm/defaults/main.yml
helm_namespace: kube-system
helm_cert_cn: helm001
tiller_sa: tiller
tiller_cert_cn: tiller001
tiller_image: jmgao1983/tiller:v2.12.3
#repo_url: https://kubernetes-charts.storage.googleapis.com
# 如果默認官方repo 網絡訪問不穩定可以使用如下的阿里雲鏡像repo
#repo_url: https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
repo_url: http://127.0.0.1:8879
  • 執行安裝:
# ansible-playbook /etc/ansible/roles/helm/helm.yml

部署DNS

DNS是k8s集羣首先需要部署的,集羣中的其他pods使用它提供域名解析服務;主要可以解析集羣服務名SVC和Pod hostname。

目前 k8s v1.9+ 版本可以有兩個選擇:kube-dns 和 coredns,可以選擇其中一個部署安裝。

  • 安裝dns:
安裝 kube-dns
# kubectl create -f /etc/ansible/manifests/kubedns

或者安裝 coredns
# kubectl create -f /etc/ansible/manifests/coredns
  • 驗證dns:

新建一個測試nginx服務

# kubectl run nginx --image=nginx --expose --port=80
service/nginx created
deployment.apps/nginx created

# kubectl get pod |grep nginx
nginx-6f858d4d45-zrmz7     1/1       Running   0          1m

測試pod alpine

# kubectl run test --rm -it --image=alpine /bin/sh

/ # cat /etc/resolv.conf 
nameserver 10.68.0.2
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options ndots:5

測試集羣內部服務解析

/ # nslookup nginx.default.svc.cluster.local
Server:    10.68.0.2
Address 1: 10.68.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx.default.svc.cluster.local
Address 1: 10.68.189.45 nginx.default.svc.cluster.local

測試外部域名解析

/ # nslookup www.baidu.com
Server:    10.68.0.2
Address 1: 10.68.0.2 kube-dns.kube-system.svc.cluster.local

Name:      www.baidu.com
Address 1: 115.239.211.112
Address 2: 115.239.210.27

部署集羣存儲

在kubernetes(k8s)中對於存儲的資源抽象了兩個概念,分別是PersistentVolume(PV)、PersistentVolumeClaim(PVC)。

其中PV是集羣中的資源;PVC是對這些資源的請求。而PV又有兩種提供方式:靜態或動態。這裏以NFS存儲爲例,講解k8s 衆多存儲方案中的一個實現。

靜態PV

首先我們需要一個NFS服務器,用於提供底層存儲。這裏省略,因爲之前我們已經搭建了一個NFS服務。

  • 創建靜態pv,指定容量,訪問模式,回收策略,存儲類等:
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany         #讀寫權限,允許被多個Node掛載
  volumeMode: Filesystem
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: "nfs-class-01"
  nfs:
    path: /data/k8s             #指定共享目錄
    server: 192.168.30.150              #指定nfs服務器ip
  • 再創建PVC,自動實現綁定:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

動態PV

在一個工作k8s 集羣中,PVC請求會很多,如果每次都需要管理員手動去創建對應的PV資源,那就很不方便。因此K8S還提供了多種 provisioner來動態創建PV,不僅節省了管理員的時間,還可以根據StorageClasses封裝不同類型的存儲供PVC 選用。項目中的role: cluster-storage目前支持自建nfs和aliyun_nas的動態provisioner

  • 修改配置文件:
# vim roles/cluster-storage/defaults/main.yml 
# 動態存儲類型, 目前支持自建nfs和aliyun_nas
storage:
  # nfs server 參數
  nfs:
    enabled: "yes"
    server: "192.168.30.150"
    server_path: "/data/k8s"
    storage_class: "class-nfs-01"
    provisioner_name: "nfs-jenkins-01"

  # aliyun_nas 參數
  aliyun_nas:
    enabled: "no"               # no表示不啓用
    server: "xxxxxxxxxxx.cn-hangzhou.nas.aliyuncs.com"
    server_path: "/"
    storage_class: "class-aliyun-nas-01"
    controller_name: "aliyun-nas-controller-01"
  • 創建nfs provisioner:
# ansible-playbook /etc/ansible/roles/cluster-storage/cluster-storage.yml

# kubectl get pod --all-namespaces |grep nfs-jenkins
kube-system   nfs-jenkins-01-7ffdb44d9d-mvxkk               1/1       Running   0          1h

驗證使用動態PV

  • 創建測試的PVC:
# kubectl apply -f /etc/ansible/manifests/storage/test.yaml
# kubectl get pv
pvc-00de6ba5-4602-11e9-89a3-000c29d20ca7   1Mi        RWX            Delete           Bound     default/test-claim    class-nfs-01             1h
# kubectl get pvc
NAME          STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
test-claim    Bound     pvc-00de6ba5-4602-11e9-89a3-000c29d20ca7   1Mi        RWX            class-nfs-01   1h

可以發現掛載的時候,nfs-client根據PVC自動創建了一個目錄,我們Pod中掛載的/mnt,實際引用的就是該目錄,而我們在/mnt下創建的SUCCESS文件,也自動寫入到了這個目錄下面。


部署ingress

ingress就是從外部訪問k8s集羣的入口,將用戶的URL請求轉發到不同的service上。ingress相當於nginx反向代理服務器,它包括的規則定義就是URL的路由信息;它的實現需要部署Ingress controller(比如 traefik ingress-nginx 等),Ingress controller通過apiserver監聽ingress和service的變化,並根據規則配置負載均衡並提供訪問入口,達到服務發現的作用。

  • 未配置ingress:

集羣外部 → NodePort → K8S Service

  • 配置ingress:

集羣外部 → Ingress → K8S Service

  • 注意:ingress 本身也需要部署Ingress controller時使用以下幾種方式讓外部訪問
    • 使用NodePort方式
    • 使用hostPort方式
    • 使用LoadBalancer地址方式

下面就以部署traefik爲例。

部署traefik

  • 安裝traefik ingress-controller:
# kubectl create -f /etc/ansible/manifests/ingress/traefik/traefik-ingress.yaml

這裏需要注意:

  • 注意需要配置 RBAC授權

  • 注意trafik pod中 80端口爲 traefik ingress-controller的服務端口,8080端口爲 traefik 的管理WEB界面;爲後續配置方便指定80 端口暴露NodePort端口爲 23456(對應於在hosts配置中NODE_PORT_RANGE範圍內可用端口)

  • 驗證traefik ingress-controller:

# kubectl get deploy -n kube-system traefik-ingress-controller 
NAME                         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
traefik-ingress-controller   1         1         1            1           1d

# kubectl get svc -n kube-system traefik-ingress-service 
NAME                      TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                       AGE
traefik-ingress-service   NodePort   10.68.89.151   <none>        80:23456/TCP,8080:27528/TCP   1d

可以看到traefik-ingress-service 服務端口80暴露的nodePort確實爲23456。

  • 測試ingress:

首先創建測試用K8S應用,並且該應用服務不用nodePort暴露,而是用ingress方式讓外部訪問。

# kubectl run test-hello --image=nginx --expose --port=80

然後爲這個應用創建 ingress。

# kubectl create -f /etc/ansible/manifests/ingress/test-hello.ing.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-hello
spec:
  rules:
  - host: hello.test.com
    http:
      paths:
      - path: /
        backend:
          serviceName: test-hello
          servicePort: 80

集羣內部嘗試訪問: curl -H Host:hell.test.com 10.68.89.151(traefik-ingress-service的服務地址) 能夠看到歡迎頁面 Welcome to nginx!;

集羣外部嘗試訪問(假定集羣一個NodeIP爲 192.168.30.130): curl -H Host:hello.test.com 192.168.30.130:23456,也能夠看到歡迎頁面 Welcome to nginx!,說明ingress測試成功。

  • 爲traefik web管理頁面創建 ingress 規則:
# kubectl create -f /etc/ansible/manifests/ingress/traefik/traefik-ui.ing.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: traefik-web-ui
  namespace: kube-system
spec:
  rules:
  - host: traefik-ui.test.com
    http:
      paths:
      - path: /
        backend:
          serviceName: traefik-ingress-service
          servicePort: 8080

在本機hosts文件中增加一條記錄:192.168.30.130 traefik-ui.test.com,即可通過traefik-ui.test.com:23456訪問traefik的web管理界面。

部署ingress-service的負載均衡

上面的訪問已經實現了ingress功能,但是帶有端口,這樣不方便,我們需要再次做個代理轉發來去掉23456端口。

利用nginx/haproxy等集羣,可以做代理轉發以去掉23456這個端口。如果你的集羣根據本項目部署了高可用方案,那麼可以利用LB節點haproxy來做,當然如果生產環境K8S應用已經部署非常多,建議還是使用獨立的 nginx/haproxy集羣。

  • 配置負載轉發ingress nodeport:

向集羣外暴露ingress-controller本身的服務端口(80/443/8080)一般有以下三種方法:

  1. 部署ingress-controller時使用hostNetwork: true,這樣就可以直接使用上述端口,可能與host已listen端口衝突
  2. 部署ingress-controller時使用LoadBalancer類型服務,需要集羣支持LoadBalancer
  3. 部署ingress-controller時使用nodePort類型服務,然後在集羣外使用 haproxy/f5 等配置 virtual server 集羣

這裏以使用 haproxy 配置 ingress的 VS 集羣,前提是多主多節點集羣並且配置了自建lb節點。

  1. 配置lb參數開啓轉發ingress nodeport:
# vim /etc/ansible/roles/lb/defaults/main.yml
# 啓用 ingress NodePort服務的負載均衡 (yes/no)
INGRESS_NODEPORT_LB: "yes"
# 啓用 ingress tls NodePort服務的負載均衡 (yes/no)          
INGRESS_TLS_NODEPORT_LB: "no"               #如果不配置https ingress,這裏選擇no
  1. 重新配置啓動LB節點服務:
# ansible-playbook /etc/ansible/roles/lb/lb.yml
  1. 驗證 lb 節點的 haproxy 服務配置:
# cat /etc/haproxy/haproxy.cfg

listen kube-master
        bind 0.0.0.0:8443
        mode tcp
        option tcplog
        balance roundrobin 
        server 192.168.1.51 192.168.1.51:6443 check inter 2000 fall 2 rise 2 weight 1
        server 192.168.1.52 192.168.1.52:6443 check inter 2000 fall 2 rise 2 weight 1

listen ingress-node
	bind 0.0.0.0:80
	mode tcp
        option tcplog
        balance roundrobin
        server 192.168.1.233 192.168.1.233:23456 check inter 2000 fall 2 rise 2 weight 1
        server 192.168.1.253 192.168.1.253:23456 check inter 2000 fall 2 rise 2 weight 1

listen ingress-node-tls
	bind 0.0.0.0:443
	mode tcp
        option tcplog
        balance roundrobin
        server 192.168.1.233 192.168.1.233:23457 check inter 2000 fall 2 rise 2 weight 1
        server 192.168.1.253 192.168.1.253:23457 check inter 2000 fall 2 rise 2 weight 1

配置文件中有上面的配置就說明配置沒問題,其實這一步應該放到部署traefik前面來完成。

這樣我們就直接去掉了23456端口,在hosts文件中加上相應的域名記錄,就可以直接通過域名訪問到相應的k8s應用了。


部署jenkins

之所以部署前面的東西,就是爲了部署jenkins,基於k8s部署jenkins可以實現的Jenkins動態Slave CI/CD流程。

  • 編輯鏡像:
# docker pull jenkins/jenkins:lts

# docker run -d -p  8090:8080 -p 50000:50000 -v /var/jenkins_home --name jenkins jenkins/jenkins:lts
941d70ec5d60bff9fa621794c0a2cedcbb8a761d564aa4b32b990166abb92088

# docker cp jenkins.war jenkins:/usr/share/jenkins/             #準備最新版本的war包,替換

# docker commit 941d7 jenkins/jenkins:latest
  • 推送鏡像到harbor:
# docker login harbor.lzxlinux.com

# docker tag jenkins/jenkins:latest harbor.lzxlinux.com/jenkins/jenkins:latest

# docker push !$

在這裏插入圖片描述

  • 修改配置文件:
# vim /etc/ansible/manifests/jenkins/values.yaml
Master:
  Name: jenkins-master
  ImagePullSecret: my-secret
  Image: "harbor.lzxlinux.com/jenkins/jenkins"
  ImageTag: "latest"
  ImagePullPolicy: "IfNotPresent"
  Component: "jenkins-master"
  UseSecurity: true
  AdminUser: admin
  AdminPassword: lzxlzx
  
HostName: jenkins.test.com

    - kubernetes:1.14.8             #相關插件
    - workflow-aggregator:2.6
    - workflow-job:2.26
    - credentials-binding:1.18
    - git:3.9.3
    - gitlab:1.5.11

Agent:
  Enabled: true
  Image: jenkinsci/jnlp-slave
  ImageTag: alpine
# ImagePullSecret: jenkins
  Component: "jenkins-slave"
  Privileged: false
    
  StorageClass: "class-nfs-01"              #這裏一定要與之前配置的動態PV中的一致,否則報錯
  • 通過helm安裝:
# helm install /etc/ansible/manifests/jenkins/ --name jenkins           #若報錯請加上 --tls
# kubectl get pod 
NAME                       READY     STATUS     RESTARTS   AGE
jenkins-5cfc5fcd4b-779cs   0/1       Init:0/1   0          2m

# kubectl describe pod jenkins-5cfc5fcd4b-779cs
  Normal   Pulling           1s               kubelet, 192.168.30.129  pulling image "harbor.lzxlinux.com/jenkins/jenkins:latest"
  Normal   Pulled            <invalid>        kubelet, 192.168.30.129  Successfully pulled image "harbor.lzxlinux.com/jenkins/jenkins:latest"
  Normal   Created           <invalid>        kubelet, 192.168.30.129  Created container
  Normal   Started           <invalid>        kubelet, 192.168.30.129  Started container
  Normal   Pulled            <invalid>        kubelet, 192.168.30.129  Container image "harbor.lzxlinux.com/jenkins/jenkins:latest" already present on machine
  Normal   Created           <invalid>        kubelet, 192.168.30.129  Created container
  Normal   Started           <invalid>        kubelet, 192.168.30.129  Started container
  
# kubectl get pod 
NAME                       READY     STATUS    RESTARTS   AGE
jenkins-5cfc5fcd4b-779cs   1/1       Running   0          3m

# kubectl get svc
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
jenkins         ClusterIP   10.68.174.118   <none>        8080/TCP    3m
jenkins-agent   ClusterIP   10.68.172.23    <none>        50000/TCP   3m
kubernetes      ClusterIP   10.68.0.1       <none>        443/TCP     1h

通過這樣的安裝方式,可以自動安裝配置文件中包含的插件。

# kubectl logs -f jenkins-5cfc5fcd4b-779cs              #查看啓動進度
  • 如果安裝有問題:
# helm delete --purge jenkins

可以這樣刪除,檢查配置文件無誤後重新安裝。

  • 訪問web界面:

在本地hosts文件中增加一條hosts記錄:192.168.30.129 jenkins.test.com,這裏ip可以是集羣中任一ip。通過域名訪問jenkins界面。

在這裏插入圖片描述

賬號:admin,密碼:lzxlzx。這是之前在配置文件中設置的。

  • 新增一個雲:

這一步其實在安裝jenkins過程中就已經設置好了,沒特殊要求我們可以不做改動。

在這裏插入圖片描述

在這裏插入圖片描述

只要連接測試成功就說明沒有問題。

  • 新增流水線任務:

在這裏插入圖片描述

在這裏插入圖片描述

  • cloud:插件配置中的Name

  • label:插件配置中的Images → Kubernetes Pod Tempalte → Labels

  • node:與label一致即可

  • 構建:

點擊立即構建,查看控制檯輸出

在這裏插入圖片描述

查看pod情況:

# kubectl get pod
NAME                       READY     STATUS              RESTARTS   AGE
default-cxj8q              0/1       ContainerCreating   0          8s
jenkins-5cfc5fcd4b-779cs   1/1       Running             1          30m

構建完成

在這裏插入圖片描述

查看pod情況:

# kubectl get pod
NAME                       READY     STATUS              RESTARTS   AGE
default-cxj8q              0/1       Terminating         0          1m
jenkins-5cfc5fcd4b-779cs   1/1       Running             1          30m

# kubectl get pod
NAME                       READY     STATUS              RESTARTS   AGE
jenkins-5cfc5fcd4b-779cs   1/1       Running             1          30m

可以看到pod副本是根據任務構建來動態創建和終止的,這就實現了K8s集羣Jenkins 動態Slave CI/CD流程。


更多參考資料:

jenkins k8s 動態增減(1)

jenkins k8s 動態增減(2)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章