GitLab CI and Helm CI/CD Kubernetes集群实践


对于想要在GitLab上实施CI/CD到Kubernetes的朋友,可能需要了解一下GitLab、Kubernetes、Helm、nginx-ingress相关技术的概念和知识。

Kubernetes Cluster(已有Kubernetes集群的可以跳过本节)

对于Kubernetes集群的安装部署,本篇文章使用的是GKE,当然你也可以选择其它公有云提供的Kubernetes的产品。例如AWS的EKS,Microsoft的AKS。另外国内的有Alibaba的容器服务Kubernetes版,国内的公有云现在貌似基本都有Kubernetes服务,包括专业做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

验证集群部署

查看刚刚创建的Kubernetes 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:在Kubernetes集群内部暴露服务到外部,我们有多种选择: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上的Kubernetes Node节点上,直接访问该IP。

GitLab CE Setup(1)

GitLab这一节只介绍一些安装配置方法,因为GitLab配置项目涉及的细节有点多。

GitLab安装

见GitLab安装文档:https://about.gitlab.com/install/#centos-7

GitLab常规配置:
  • 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。它将 一个服务部署所需要的Kubernetes资源(Deployment、Secret、ConfigMap、Service、Ingress……)都打包在一个Chart中,另外Chart可单独存储在本地。也可以存储到远程Git仓库中,或搭建个WebServer可供http下载Chart就行。 Helm分为Client端和 server端,Server端(Tiller)部署在Kubernetes集群中,根据Helm客户端提交的Chart解析成相应Kubernetes资源的yaml文件,在Kubernetes上创建对应类型的资源。

安装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 Registry

Docker hub (Optional private docker reg)

这里选择了docker hub,省去了搭建私有仓库的步骤。当然也可以选择GCP的container registry。 需要提供2个变量给后面gitlab-ci使用,docker login用户名和密码。

Demo Service

Demo code:

用Go写了简单的web server。两个二哈,两个猫咪。 代码:见minions

Helm Charts 准备

创建monions chart:
$ mkdir charts && cd charts/ && helm create minions

当前项目根目录charts/minions,简单起见,我们只对文件内容做了一点点修改(包含修改readiness url), 另外我们在Pipeline里面deploy job时会定义。进行动态set。

GitLab CE Setup(1)

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进行演示。
2-min.gif

傻二哈测试

可以尝试修改main.go,将dog.gif,改为cat.gif,重新提交代码到Git仓库。之后部署成功后,你将看到两只可爱的小猫咪。
2.png



Done。

总结

跟之前在使用的Jenkins相比,gitlab-ci和代码管理结合的更完美。但是有些地方还是挺坑的,可能是对gitlab-ci比较陌生。慢慢的熟悉之后再来发表评论。

其实不管是使用Jenkins或者GitLab CI,又或者其他工具也好。原理基本上都差不多,就是使用定义方式上有些不同而已。 后面就是根据项目继续完善优化Pipeline。如数据库迁移Job、PR review等。

原文链接:https://aliasmee.github.io/pos ... tice/

0 个评论

要回复文章请先登录注册