大綱
- 基礎準備工作
-
- Kubernetes cluster
- Helm
- Helm repository(Optional)
- Nginx-Ingress-Conroller
- Docker Registry
- Gitlab-ce Service
- DNS
- Demo Service
- Helm Charts
對于想要在Gitlab上實施CICD到kubernetes的朋友,可能需要了解一下gitlab、k8s、helm、nginx-ingress相關技術 的概念和知識。
Kubernetes Cluster(已有k8s叢集的可以跳過本節)
對于K8s叢集的安裝部署,本篇文章使用的是GKE,當然你也可以選擇其它公有雲提供的k8s的産品。例如AWS的EKS, Microsoft的AKS。另外國内的有Alibaba的 容器服務kubernetes版,國内的公有雲現在貌似基本都有k8s 服務,包括專業做CDN的某牛雲。說明kubernetes早已成為容器編排領域的事實标準。
建立叢集
gcloud beta container --project "hi42-top" clusters create "demo-k8s" --zone "us-east1-b" --no-enable-basic-auth --cluster-version "1.12.6-gke.10" --machine-type "g1-small" --image-type "COS" --disk-type "pd-standard" --disk-size "30" --num-nodes "2" --enable-cloud-logging --enable-cloud-monitoring --no-enable-ip-alias --network "hi42-vpc" --subnetwork "hi42-east" --addons HorizontalPodAutoscaling --enable-autoupgrade --enable-autorepair
驗證叢集部署
檢視剛剛建立的 k8s cluster
$ gcloud container clusters list
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
demo-k8s us-east1-b 1.12.6-gke.10 35.196.230.33 g1-small 1.12.6-gke.10 2 RUNNING
安裝kubectl command 根據你用戶端選擇合适的
kubectl發行版使用gcloud自帶指令,自動配置本地的kubectl用戶端
$ gcloud container clusters get-credentials demo-k8s
Tips: 這一步gcloud用戶端會下載下傳kubeconfig,把内容導入并合并到kubeconfig預設檔案 ~/.kube/config
确認下目前使用的叢集配置是我們剛剛建立的叢集
$ kubectl config current-context
gke_hi42-top_us-east1-b_demo-k8s
Notes:預設config檔案裡可能會有很多叢集配置。如果目前顯示的不是我們想要的叢集,可以使用
kubectl config
進行設定,切換到正确的叢集配置。
部署個demo程式驗證一下
$ kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
建立一個類型為LoadBalance的Service,目的是将剛剛建立的服務暴露到叢集外部,可外部通路。
kubectl apply -f - <<EOF
kind: Service
apiVersion: v1
metadata:
name: hello-server
namespace: default
labels:
run: hello-server
annotations:
cloud.google.com/load-balancer-type: "Internal"
spec:
selector:
run: hello-server
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
EOF
Notes:cloud.google.com/load-balancer-type: “Internal” 表示内網LB
Tips:在k8s叢集内部暴露服務到外部,我們有多種選擇:Nodeport、LoadBalancer、Ingress。
檢視剛剛建立的Service 資源
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-server LoadBalancer 10.19.254.53 10.11.0.18 80:30105/TCP 7m
kubernetes ClusterIP 10.19.240.1 <none> 443/TCP 38m
名為hello-server 的 svc已經就緒,外部IP是
10.11.0.8
,下面使用curl通路該服務。
$ curl 10.11.0.18
Hello, world!
Version: 1.0.0
Hostname: hello-server-5cdf4854df-lf76k
Notes: 我的網絡已經和gcp 建立了隧道,是以兩地内網是互通的,可以直接通路VPC内部IP。當然沒有 條件的話,你也可以選擇ssh登陸gcp上的k8s Node節點上,直接通路該IP。
Gitlab CE Setup (1)
Gitlab這一節隻介紹一些安裝配置方法,因為gitlab配置項目涉及的細節有點多。
Gitlab 安裝
見Gitlab-安裝文檔:
https://about.gitlab.com/install/#centos-7Gitlab 正常配置
- https
- 自動備份
- LDAP
- …
Gitlab runner 配置
Gitlab Runner 是根據.gitlab-ci.yaml檔案定義的job來跑pipeline的。并且根據注冊的runner的執行者不同, 可選擇在shell、docker、kubernetes等環境中運作job指令内容。 見:
runner executor- 安裝gitlab runner
參考官方runner 安裝文檔
- 注冊runner到gitlab server
添加shell runner(當然也可以選擇docker executor)
參考runner 注冊文檔 :
Warning
:另外我們需要在該runner上安裝
Docker-ce配置Runner使用kubectl & helm
*Warning: 這裡我直接使用runner所在主機以shell executor跑pipeline,是以這裡直接将kubeconfig 放到了系統上。完整檔案位置在gitlab-runner使用者的家目錄:/home/gitlab-runner/.kube/config *
擷取kubeconfig檔案: >參考
kubernetes cheatsheet$ kubectl config view --flatten=true > /tmp/demo-k8s.kubeconfig
将該檔案demo-k8s.kubeconfig拷貝到runner家目錄 /home/gitlab-runner/.kube/config
Notes: 注意config是檔案名,不是目錄
驗證kubectl與叢集連接配接是否正常:
gitlab-runner@gitlab-slave:~$ kubectl cluster-info
Kubernetes master is running at https://35.196.230.33
Heapster is running at https://35.196.230.33/api/v1/namespaces/kube-system/services/heapster/proxy
KubeDNS is running at https://35.196.230.33/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://35.196.230.33/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
gitlab-runner@gitlab-slave:~$ kubectl get po
NAME READY STATUS RESTARTS AGE
hello-server-5cdf4854df-lf76k 1/1 Running 0 130m
下一步便是安裝在runner主機上安裝helm。
Helm Setup
給Helm 來個簡短的介紹? Helm是kubernetes裡面的包管理工具。把它想象成Linux中的yum、apt。它将 一個服務部署所需要的k8s資源(deployment、secret、configmap、service、ingress…)都打包在一個 Chart中,另外Chart可單獨存儲在本地。也可以存儲到遠端git倉庫中,或搭建個WebServer可供http下載下傳chart 就行。 Helm 分為client端 和 server端,Server 端(Tiller)部署在k8s叢集中,根據helm用戶端送出 的chart解析成相應k8s資源的yaml檔案,在k8s上建立對應類型的資源。
安裝helm
Warning:helm client不是安裝在本機,而且安裝在gitlab-runner所在主機上。
下載下傳 helm用戶端
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
HELM init with tiller (RBAC)
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
EOF
初始化并安裝helm tiller
$ helm init --service-account tiller --history-max 200
...
Happy Helming!
看到以上輸出,表示tiller已經正确部署到kube-system下。有興趣的可以檢視下pod在kube-system namespace下。
Ingress-controller setup
安裝ingress controller
helm install --name=nginx-ingress-intranet --namespace=ingress-controller \
--set controller.ingressClass=nginx-ingress-intranet \
--set controller.service.annotations."cloud\.google\.com/load-balancer-type"=Internal \
--set rbac.create=true \
stable/nginx-ingress
驗證 ingress controller
kubectl get svc -n ingress-controller
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-intranet-controller LoadBalancer 10.19.246.51 10.11.0.19 80:31604/TCP,443:30083/TCP 39m
nginx-ingress-intranet-default-backend ClusterIP 10.19.253.220 <none> 80/TCP 39m
從上面可以看出已經成功建立lb,注意這裡的外部IP是VPC内網IP。我們curl一下:
$ curl 10.11.0.19
default backend - 404
由于叢集裡面現在還沒有Ingress資源被建立。是以預設将請求轉發到default-backend這個服務,當然, 你也可以根據自己需要修改default-backend服務鏡像,定制404頁面。
建立 SSL Secret
如果你有證書的話,可以以這種方式建立Secret資源,供後面的Ingress資源使用。
$ kubectl create secret tls demo-hi42-top --key demo.hi42.top.key --cert fullchain.cer
secret "demo-hi42-top" created
DNS 配置
配置DNS泛域名解析
上一節我們建立了Ingress controller,現在需要将某域名系統下的所有服務配置指向該IP
*.demo.hi42.top A 10.11.0.19
Docker hub (Optional private docker reg)
這裡選擇了docker hub,省去了搭建私有倉庫的步驟。當然也可以選擇gcp的 container registry。 需要提供2個變量給後面gitlab-ci使用,docker login 使用者名和密碼。
Demo code
用go寫了簡單的web server。兩個二哈,兩個貓咪。 代碼:見
minionsHelm Charts 準備
建立 monions chart
$ mkdir charts && cd charts/ && helm create minions
目前項目根目錄charts/minions,簡單起見,我們隻對檔案内容做了一點點修改(包含修改readiness url), 另外我們在pipeline裡面deploy job時會定義。進行動态 set。
Gitlab project settings
Project開啟CI/CD,并設定環境變量。添加2變量:
- CI_REGISTRY_PASSWORD
- CI_REGISTRY_USER
添加gitlab-runner 到docker使用者組
由于我們直接使用gitlab runner shell作為executor,是以需要将gitlab-runner使用者加到docker 組中。在gitlab-runner 主機上執行。
$ usermod -aG docker gitlab-runner
Gitlab-PIPELINE 示例
.gitlab-ci.yml 完整内容
stages:
- build
- test
- release
- deploy
variables:
# CI_DEBUG_TRACE: "true"
DOCKER_DRIVER: overlay2
# application domain name
QA_DOMAIN_NAME: minions.demo.hi42.top
PROD_DOMAIN_NAME: minions.hi42.top
CHART_PATH: ./charts/minions
INGRESS_CLASS_NAME: nginx-ingress-intranet
CI_REGISTRY: docker.io
CONTAINER_PROJECT: hanyifeng/minions
CONTAINER_IMAGE: $CI_REGISTRY/$CONTAINER_PROJECT
CONTAINER_TAG: $CI_COMMIT_SHORT_SHA
# variables 好像不支援這樣嵌套指派。。。Ref:https://docs.gitlab.com/ee/ci/variables/where_variables_can_be_used.html#gitlab-runner-internal-variable-expansion-mechanism
# CONTAINER_BUILT_IMAGE: "$CONTAINER_IMAGE:$CONTAINER_TAG"
# CONTAINER_RELEASE_IMAGE: "$CONTAINER_IMAGE:latest"
# Kubernetes config
STAGE_NAMESPACE: qa
PROD_NAMESPACE: production
STAGE_RELEASE_NAME: minions-qa
PROD_RELEASE_NAME: minions-product
before_script:
- echo "Hello Minions"
build:
stage: build
script:
- docker login -u ${CI_REGISTRY_USER} -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- echo "Building image..."
- docker build --pull -t $CONTAINER_IMAGE:$CONTAINER_TAG .
- echo "Pushing to ${CI_REGISTRY}..."
- docker push $CONTAINER_IMAGE:$CONTAINER_TAG
tags:
- shell
test1:
stage: test
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- echo "do unit test"
- docker pull $CONTAINER_IMAGE:$CONTAINER_TAG
tags:
- shell
release-image:
stage: release
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker pull $CONTAINER_IMAGE:$CONTAINER_TAG
- docker tag $CONTAINER_IMAGE:$CONTAINER_TAG $CONTAINER_IMAGE:latest
- docker push $CONTAINER_IMAGE:latest
tags:
- shell
deploy_qa:
stage: deploy
script:
- helm upgrade --install
--set image.repository=$CONTAINER_IMAGE
--set image.tag=$CONTAINER_TAG
--set image.pullPolicy=Always
--set ingress.enabled=true
--set ingress.annotations."kubernetes\.io/ingress\.class"=$INGRESS_CLASS_NAME
--set ingress.hosts[0]=$QA_DOMAIN_NAME
--set ingress.enabled=true --set ingress.tls[0].hosts[0]=$QA_DOMAIN_NAME
--set ingress.tls[0].secretName=demo-hi42-top
--set service.port="8080"
--wait
--namespace=$STAGE_NAMESPACE
$STAGE_RELEASE_NAME $CHART_PATH
environment:
name: staging
url: $QA_DOMAIN_NAME
tags:
- shell
Warning:由于這裡加了ssl,是以需要引用證書,我們之前建立的secret是在default namespace裡, 是以需要重新在qa環境下建立一個secret證書。
$ kubectl create secret tls demo-hi42-top --key demo.hi42.top.key --cert fullchain.cer -n qa
驗證部署狀态
可以在gitlab pipeline上,找到最新的建構,并檢視部署狀态。這裡以一個GIF進行示範。
傻二哈測試
可以嘗試修改main.go,将dog.gif,改為cat.gif,重新送出代碼到git倉庫。之後部署成功後,你将看到兩隻可愛的小貓咪。。。
Done。
總結
跟之前在使用的Jenkins相比,gitlab-ci和代碼管理結合的更完美。但是有些地方還是挺坑的,可能是對gitlab-ci 比較陌生。慢慢的熟悉之後再來發表評論。
其實不管是使用jenkins或者gitlab ci,又或者其他工具也好。原理基本上都差不多,就是使用定義方式上有些不同而已。 後面就是根據項目繼續完善優化pipeline。如資料庫遷移job,pr review等。