[教學] 用Drone, Kubernetes跟Helm,以及RBAC來建置你的CI/CD流程 — Part 1
CI/CD with Drone, Kubernetes and Helm — Part 1

有陣子沒寫文章,翻譯點東西來練習練習。最近公司要導入Drone/K8s/Helm,加上本來公司大多數的解決方案都放在Google Cloud 上,於是開始動手在新的專案做測試。不過這幾個解決方案都算新的,也很難找到適合的說明一次全部到位。剛好看到 這篇 在介紹這個組合,不但救了我幾天的時間,還幫我暖了一下這幾個技術的概念,聯絡了一下作者,決定來翻譯這篇文章。不過應該很難避免加入自己的話啦,英文的部分除非比較常見的翻譯法,否則還是以原文為主。另外原作者列了許多參考連結,在此就沒有再針對那些連結在做翻譯了
- 介紹(步驟、用到的技術(Drone/K8s/Helm))
- K8s Cluster
- Helm/Tiller
- Deploying Drone(靜態IP/整合/Chart跟配置文件)
- TLS(部署cert-manager/建立ACME Issuer/憑證/更新Drone的內容)
- 結論
- 感謝
介紹
現在大部分的人都同意CI/CD(持續整合與交付)是個很困難的工作。不過現在我們有許多好用的技術,所以主要的問題變成:「我要怎麼把這個裝到這個上」 或「我要怎麼把這兩個東西合在一起用」。
這些問題永遠都不會有一個簡單又通用的答案。在這系列的文章裡,我們會用這幾個技術,一步一步地把整個CI/CD的流程建起來
首先第一篇文章就是介紹會用到的技術,這是讓初學者對這些技術有些概念。如果你已經有個正在跑的k8s叢集跟一個drone的機器在上面,你可以直接忽略掉這個步驟。
步驟
- 在Google Kubernetes Engine上建立一個k8s叢集
- 建立一個Tiller的服務帳戶
- 初始化Helm
- 在Helm新增一個Repo
- 把Drone部署到k8s叢集
- 在Drone的服務上啟用https
用到的技術
Drone
Drone 是一個用來做持續交付的平台,是用Go寫的,建立在Docker的技術上。 Drone用一個YAML的設定檔(基於docker-compose的文件)來定義並且執行整個Docker容器內的流程。
他的做法跟Travis類似,你在你的repository裡面用文件來描述整個流程。最酷的部分是他所有的流程都是在Docker的容器裡面執行。這個一開始看起來會有點反直覺,但他讓我們可以執行一個很棒的套件系統:每一個你想在Drone裡面做的功能,都可以是一個image,所以你不需要把它全部都裝在Drone裡面。只有當你想要使用某個功能的時候,讓他去抓他需要的image。
另外一個在Docker裡面執行的好處是安裝Drone很簡單。不過我們不會把它直接裝在一個伺服器/虛擬機裡面。後面我們會再提到怎麼處理。
Kubenetes
Kubernetes(常簡稱為K8s)是用於自動部署、擴展和管理容器化(containerized)應用程式的開源系統。Google設計並捐贈給Cloud Native Computing Foundation(今屬Linux基金會)來使用的。
它旨在提供「跨主機集群的自動部署、擴展以及運行應用程式容器的平台」。它支持一系列容器工具, 包括Docker等。 Wikipedia
維基百科把k8s解釋得不錯,基本上k8s把在上面執行的容器抽象化,並且提供一個平台讓我們可以部署我們的應用程式。他負責把我們的容器正確的分配到不同的節點上,所以如果有其中一個節點停機,或者沒有辦法連上線,你還是可以正常的使用你的服務,這個時候k8s會去修復這個節點,或是再重新部署這個節點。
我建議看完這篇文章(英文)再繼續這個教學
Helm
Helm是一個k8s上的套件管理系統。他讓我們可以建立、維護、並且部署應用程式到k8s叢集上。
基本上如果你想要在你的k8s上安裝某個東西,你可以直接看是不是有一個現成的Chart可以用。例如我們會用這個方式在k8s上安裝Drone
Helm讓你可以把你的應用部署到不同的命名空間(namespaces),改變不同image的tag,甚至可以複寫所有你在k8s的參數。簡單的說,你可以用同一份的Chart檔,不同的設定檔,就把它部署到你的Staging環境跟Production環境。(註:例如你可以直接去網路上找Drone的Chart,再寫一些客製的參數,讓他可以部署到你自己的環境)
在這篇文章我們會看怎麼使用一個已經存在的Chart,下一篇會教你怎麼從頭開始建立一個新的。
聲明
這個教學裡面,我們會用 Google Cloud Platform,因為在GCP上你可以很簡單的建立k8s的叢集,上面也有私人的容器管理服務,在晚一點的教學裡面也會用到
Kubernetes Cluster
如果你已經有一個k8s叢集,並且k8s的版本>1.8,你可以直接略過這個步驟(註:目前GCP上提供的k8s>1.9)
在這個步驟裡,我們會需要gcloud 跟 kubectl這兩個工具。這篇文章教你怎麼在你的環境安裝Google Cloud SDK
如同前面提到的,這個教學不會教你怎麼建立一個k8s叢集。我們會用 Google Kubenetes Engine來建立我們的版本。你可以直接用GCP提供的網頁介面,也可以直接用glcoud的指令來做。目前GCP上支援的是1.8.8(註,現在是1.9.7),不過只要你的版本>1.8,你都可以用這篇教學的方法。是說也沒有必要不去用最新的穩定版本啦….
之所以要選1.8以上,是因為它預設內建了RBAC(基於角色的權限管理),並且用RBAC來做預設的權限管理系統。
為了降低機器的成本,你可以修改他的機器類型(註:建議記憶體要在2G以上,不然會跑不動Drone),不過建議至少要三個節點以上。這會讓你在進行整合或移轉服務的時候,你的服務不會因此停止。
為了確認你的叢集有執行,你可以用這個命令來看他的結果
$ gcloud container clusters list
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
mycluster europe-west1-b 1.10.2-gke.1 <master ip> custom-1-2048 1.10.2-gke.1 3 RUNNING你會得到Master IP/ Project/ Location,接下來的步驟以及指令,請用你自己的location/cluster name/Project來替換
當你確定你的叢集執行了,你可以用接下來的指令來把credentials下載到你的電腦
$ gcloud container clusters get-credentials $NAME --zone $LOCATION --project $PROJECT
Fetching cluster endpoint and auth data.
kubeconfig entry generated for mycluster.
$ kubectl cluster-info
Kubernetes master is running at https://<master ip>
GLBCDefaultBackend is running at https://<master ip>/api/v1/namespaces/kube-system/services/default-http-backend/proxy
Heapster is running at https://<master ip>/api/v1/namespaces/kube-system/services/heapster/proxy
KubeDNS is running at https://<master ip>/api/v1/namespaces/kube-system/services/kube-dns/proxy
kubernetes-dashboard is running at https://<master ip>/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy
Metrics-server is running at https://<master ip>/api/v1/namespaces/kube-system/services/metrics-server/proxy現在kubectl已經設定成可以執行在你的叢集上了,後面的指令是把所有你的叢集相關的資訊的位置輸出
Helm and Tiller
首先我們需要helm命令工具,你可以參考這篇文章
Helm其實是兩個部分的組成。Helm自己是一個用戶端,Tiller是他的server。你需要把Tiller安裝在我們的k8s系統上,這樣Helm才可以使用。不過首先我們必須建立一個服務帳戶給Tiller用。 Tiller需要跟我們的叢集互動,所以他需要能夠建立部署、設定配置文件、密鑰等等,歡迎來到RBAC。
首先我們先建立一個檔案叫tiller-rbac-config.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
在這份yaml裡面,我們宣告了一個ServiceAccount叫tiller,然後我們宣告一個ClusterRoleBinding,把tiller跟他能夠執行的任務連結
然後我們先把這組配置部署到k8s上
$ kubectl create -f rbac-config.yaml接下來我們就可以把tiller用這個帳戶部署
$ helm init --service-account tiller注意這在實務上不是一個好的方式。用RBAC你應該可以敘述好這個tiller帳戶只可以執行在你要他執行的命名空間跟叢集,這篇文章告訴你怎麼做這些限制
這個步驟對接下來的部分很重要,我們會用這個帳戶來處理drone跟k8s的互動
部署 Drone
靜態IP
如果你有個網域,你要把他的子網域指向你的Drone服務,你需要先在GCP上建立一個外部IP,給他一個名字,並且把前面的子網域指向這個外部IP
在接下來的文章,我們用drone-kube代表這個gcp上的外部ip名稱,用drone.myhost.io代表你的網域
整合服務
首先我們需要把github跟我們的Drone整合在一起,這篇文章告訴你怎麼整合其他的服務(註:我們使用gitlab,一切順利),目前Drone支援以下的服務
如果你不是用github,請注意後面的文章配置要修改成你自己的服務
Chart and configuration
google搜尋一下,就會看到其實已經有寫好給Drone的Chart。我們可以快速地看一下所使用的配置文件。我們會建立一個values.yaml,裡面包含我們自己需要的設定
service:
httpPort: 80
nodePort: 32015
type: NodePort
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: "gce"
kubernetes.io/ingress.global-static-ip-name: "drone-kube"
kubernetes.io/ingress.allow-http: "true"
hosts:
- drone.myhost.io
server:
host: "http://drone.myhost.io"
env:
DRONE_PROVIDER: "github"
DRONE_OPEN: "false"
DRONE_GITHUB: "true"
DRONE_ADMIN: "me"
DRONE_GITHUB_CLIENT: "the github client secret you created earlier"
DRONE_GITHUB_SECRET: "same thing with the secret"好,前面我們把我們的網域指向這個ip,現在我們要把這個靜態ip放在我們的ingress的資訊裡面。目前我們會用Google提供的GCE load balancer,但因為我們沒有使用TLS的證書,所以我們告訴他我們接受http的請求(後面會再教怎麼使用https)
我們也會在這份文件裡面告訴Drone怎麼跟我們的版控系統合作,這裡我們用Github
就這樣,我們準備好了,上吧
$ helm install --name mydrone -f values.yaml stable/drone如果你的網域已經設定好,現在連到drone.myhost.io就可以看到你剛部署好的服務了
TLS
部署cert-manager
過去我們可以用kube-lego來做憑證發放,現在我們用cert-manager來做這件事。
這份文件告訴你只要這樣就可以安裝cert-manger在你的k8s上
$ helm install --name cert-manager --namespace kube-system stable/cert-manager建立一個ACME Issuer
Cert-manager是由幾個部分組成的,他使用 Custom Resource Definitions 而我們可以用kubectl來控制權限發放等等的動作
Issuer或ClusterIssuer代表一個可以發放x509憑證的發放機構,這兩個的差別是Issuer只能發放屬於他的命名空間的憑證,而且只能被這個命名空間呼叫,ClusterIssuer則是不需要在特定的命名空間裡
我們會用Let’sEncrypt的ClusterIssuer(註:Let’sEncrypt是個免費發憑證的服務,一般來講三個月要重新註冊一次,但certmanager會幫你自動三個月處理一次),這樣我們可以發憑證給我們的Drone,接下來也可以用來做https的憑證。我們先開一個檔案叫 acme-issuer.yaml:
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v01.api.letsencrypt.org/directory
email: your.email.address@gmail.com
privateKeySecretRef:
name: letsencrypt-production
http01: {}好,現在我們建立了一個ClusterIssuer,並且http的驗證機制可以請用,除了這http的驗證之外,還有幾個可以用來驗證的機制,不過在這篇文章我們就只用http的機制。記得把email換成你的聯絡資訊
$ kubectl apply -f acme-issuer.yaml我們可以用Let’sEncrypt的測試環境,這樣比較不容易被Ban,也比較容易看到更多的資訊,如果你只是要測試一下,不想要實際發憑證,直接用後面這個方式即可。建立一個 acme-staging-issuer.yaml:
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging.api.letsencrypt.org/directory
email: your.email.address@gmail.com
privateKeySecretRef:
name: letsencrypt-staging
http01: {}完成後再執行
$ kubectl apply -f acme-staging-issuer.yaml
憑證
現在我們有一個用Let’sEncrypt的正式環境的ClusterIssuer,我們可以再寫一段yaml來幫我們處理ACME Challenge(註:這個流程可以把它想成是用來驗證你的網域是不是真的屬於你的)。首先我們需要一個目前Drone的Chart所使用的ingress的名字
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
mydrone-drone drone.myhost.io xx.xx.xx.xx 80 1h在這裡我們可以看到這個ingress的名字是mydrone-drone,我們在建立一個叫做drone-cert.yaml的檔案
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: mydrone-drone
namespace: default
spec:
secretName: mydrone-drone-tls
issuerRef:
name: letsencrypt # This is where you put the name of your issuer
kind: ClusterIssuer
commonName: drone.myhost.io # Used for SAN
dnsNames:
- drone.myhost.io
acme:
config:
- http01:
ingress: mydrone-drone # The name of your ingress
domains:
- drone.myhost.io這裡有許多欄位需要解釋,詳情可以看這個文件裡的說明,基本上比較重要的部份如下:
spec.secretName:憑證所使用的密鑰。通常會在後面加一個-tls這樣才不會跟其他的密鑰錯亂spec.issuerRef.name: 我們前面定義的 ClusterIssuer的名字spec.issuerRef.kind: 我們所使用的 Issuer 的類型,在這個例子裡就是ClusterIssuerspec.acme.config.http01.ingress: 我們前面用來部署Drone的ingress名稱
現在我們套用這個設定到k8s上
$ kubectl apply -f drone-cert.yaml
$ kubectl get certificate
NAME AGE
mydrone-drone 7m
$ kubectl describe certificate mydrone-drone
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning ErrorCheckCertificate 33s cert-manager-controller Error checking existing TLS certificate: secret "mydrone-drone-tls" not found
Normal PrepareCertificate 33s cert-manager-controller Preparing certificate with issuer
Normal PresentChallenge 33s cert-manager-controller Presenting http-01 challenge for domain drone.myhost.io
Normal SelfCheck 32s cert-manager-controller Performing self-check for domain drone.myhost.io
Normal ObtainAuthorization 6s cert-manager-controller Obtained authorization for domain drone.myhost.io
Normal IssueCertificate 6s cert-manager-controller Issuing certificate...
Normal CertificateIssued 5s cert-manager-controller Certificate issued successfully發放憑證需要一些時間,我們要等到CertificateIssued事件,確定憑證發放之後,再來更新我們Drone裡面的Ingress的數字。這個流程可能需要一點時間。Google Cloud Load Balancers可能也需要幾分鐘的時間update(註:憑證發放實際上的流程大概一分鐘,部署docker image到k8s的時候大約是5~10分鐘)
更新 Drone的參數
現在我們有一組包含TLS憑證的密鑰,回到前面我們部署的Drone,我們需要讓他用這個密鑰來實作https,回到前面編輯的values.yaml,這裡我們需要更改兩件事,一個是停用Http的服務,一個是把本來的server.host修改成可以使用憑證
service:
httpPort: 80
nodePort: 32015
type: NodePort
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: "gce"
kubernetes.io/ingress.global-static-ip-name: "drone-kube"
kubernetes.io/ingress.allow-http: "false" # ← Let's disable HTTP and allow only HTTPS
hosts:
- drone.myhost.io
# Add this ↓
tls:
- hosts:
- drone.myhost.io
secretName: mydrone-drone-tls
# End
server:
host: "https://drone.myhost.io" # ← Modify this too
env:
DRONE_PROVIDER: "github"
DRONE_OPEN: "false"
DRONE_GITHUB: "true"
DRONE_ADMIN: "me"
DRONE_GITHUB_CLIENT: "the github client secret you created earlier"
DRONE_GITHUB_SECRET: "same thing with the secret"接下來我們需要去更新這組參數到k8s上
$ helm upgrade mydrone -f values.yaml stable/drone也記得要去你的Github去更新callback的url
結論
在這篇文章裡面我們看到怎麼部署一組k8s到Google k8s engine,看到如何建立一個有權限的帳戶來部署Tiller,也看到怎麼使用Helm以及用它把來部署一個Drone的Chart到k8s上。
下一篇文章我們會介紹怎麼建立一個部署的流程,在這之中我們會用Go來當範例,並且部署到Google Cloud Registry上
後記
翻譯文章比想像中多花了一些時間,這篇文章感謝原作者depado,我在用他的教學路上出了一些問題,寄信之後他也很快的回覆給我,考慮到devOps在台灣的發展,就想說剛好有機會來推廣一下這幾個服務,希望有遇到任何問題不吝指教 原文見此
系列文連結如下: