[分享] 极狐Gitlab集成k8s的方法

gitlab集成k8s的所有方法

1.相关文档

官方关于集成k8s的简介:Integrate Kubernetes to your DevOps Lifecycle

总体上可以整理出三种集成k8s的方案:

在这里我们分别使用三种方案集成一个k8s cluster,并分别对应test,stage,production三个环境,然后分别基于这三种集成方式发布一个测试应用,架构如下图:

2.使用certificate-based集成k8s

gitlab基于证书集成k8s可以在三个level配置:

  • 实例级别:Menu > Admin > Kubernetes
  • group级别:进入group选择 Kubernetes
  • project级别:进入project依次选择Infrastructure > Kubernetes clusters

2.1获取集成集群所需要的所有参数

Kubernetes cluster name:自定义的一个集群名称

Environment scope :这个集群所关联的gitlab environment

API URL:连接这个k8s集群的api-server Endpoint

可以通过如下命令获取:

kubectl cluster-info | grep -E 'Kubernetes master|Kubernetes control plane' | awk '/http/ {print $NF}'

如果是从公网连接还需要把地址修改成公网ip

CA certificate :

可以通过如下命令获取:

kubectl get secrets $(kubectl get secret|awk '/default/{print$1}') -o jsonpath="{['data']['ca\.crt']}" | base64 --decode

Token

这里使用serviceaccount的token来连接k8s集群,所以这个token拥有哪些权限gitlab就对这个集群具有对应的权限。这里使用cluster-admin的role来创建token:

创建serviceaccount:

kubectl apply -f - << eof
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: gitlab-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: gitlab
    namespace: kube-system
eof

获取这个serviceaccount的token:

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep gitlab | awk '{print $1}')|awk '/token:/{print$NF}'

将上面获取到的参数在添加集群时填入对应的位置:

最后在添加集群之后,能够看到集群的信息了,就表示添加成功:

2.2理解这种模式的原理

这种基于证书连接k8s集群的方式是如何工作的呢?

首先每个k8s集群一定要指定一个Environment scope,即使在创建时没有指定,之后关联gitlab ci中的deploy job时也必须指定一个,因为deploy job就是通过environment的name和k8s集群关联上的,名字一样的job会和对应的k8s集群关联上。

一旦job和k8s集群关联上就会自动注入下面的环境变量,job和k8s集群的交互就是通过这些变量实现的,所以如果不配置基于证书的集成,手动配置这些变量也是可以让某个job关联上对应的k8s集群的。

KUBE_URL Equal to the API URL.
KUBE_TOKEN The Kubernetes token of the environment service account. Prior to GitLab 11.5, KUBE_TOKEN was the Kubernetes token of the main service account of the cluster integration.
KUBE_NAMESPACE The namespace associated with the project’s deployment service account. In the format <project_name>-<project_id>-<environment>. For GitLab-managed clusters, a matching namespace is automatically created by GitLab in the cluster. If your cluster was created before GitLab 12.2, the default KUBE_NAMESPACE is set to <project_name>-<project_id>.
KUBE_CA_PEM_FILE Path to a file containing PEM data. Only present if a custom CA bundle was specified.
KUBE_CA_PEM (deprecated) Raw PEM data. Only if a custom CA bundle was specified.
KUBECONFIG Path to a file containing kubeconfig for this deployment. CA bundle would be embedded if specified. This configuration also embeds the same token defined in KUBE_TOKEN so you likely need only this variable. This variable name is also automatically picked up by kubectl so you don’t need to reference it explicitly if using kubectl.
KUBE_INGRESS_BASE_DOMAIN From GitLab 11.8, this variable can be used to set a domain per cluster. See cluster domains for more information.

比如现在有这样一个已经集成好的k8s集群:

某个job想要关联这个集群.gitlab-ci.yml则应该写成:

deploy_to_public:
  stage: deploy
  when: manual
  environment:
    name: test-public
    url: http://<url>

  image:
    name: bitnami/kubectl:latest
    entrypoint: ['']
  script:
    - echo $KUBE_URL
  tags:
    - docker

这里还有一个特别需要注意的点:在environment字段下面还可以指定k8s的namespace,类似:

  environment:
    name: production
    url: https://example.com
    kubernetes:
      namespace: test

这里需要注意在添加k8s集群时是否勾选了GitLab-managed cluster,如果勾选了则会由gitlab去k8s中创建对应的namespace,如果没有勾选则需要自己手动去k8s集群中创建对应的namespace。这里建议不要勾选由自己手动去创建namespace,因为如果让gitlab去管理这个配置,一旦配置出错就会导致ci/cd的job出错,报错类似下图:

2.3部署测试应用

在k8s中创建pull镜像的secret:

kubectl create secret docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_DEPLOY_USER" --docker-password="$CI_DEPLOY_PASSWORD" -o yaml --dry-run=client | kubectl apply -f -

在项目中创建一个用于部署deployment的测试文件:
这就是一个很简单deployment,利用NodePort暴露服务,可根据实际情况进行修改。

kube-deployment.yaml:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: test
  name: test
spec:
  ports:
  - name: tcp
    port: 5000
    protocol: TCP
    targetPort: 5000
    nodePort: 30618
  selector:
    app: test
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: test
  name: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      imagePullSecrets:
      - name: gitlab-registry
      containers:
      - image: <your_image>
        name: test
        imagePullPolicy: Always

定义pipeline中的deploy job:

deploy_to_test:
  stage: deploy
  when: manual
  environment:
    name: test-public
    url: http://<url>
    kubernetes:
      namespace: test

  image:
    name: bitnami/kubectl:latest
    entrypoint: ['']
  script:
    - sed  "s/CI_ENVIRONMENT_SLUG/$CI_ENVIRONMENT_SLUG/g" kube-deployment.yaml|kubectl apply -f -
  tags:
    - docker

这里只需要环境名称和k8s集群的环境名称相同即可,runner的话可以任意选择,只有能运行kubectl的镜像即可,这样就能根据gitlab默认生成的kubeconfig调用api-server了。

3.使用kas集成k8s

注意:在14.5以前kas是属于Premium的功能,14.5以后免费开放。

在kas中有两种workflow:

  • GitOps workflow:也被称为pull-based workflow,它是由agent实时去监控manifest所在的git repo,一旦其中的manifest发生变化,就主动的将配置pull并应用到k8s集群中。所以gitops workflow中不需要ci/cd,也能发布和更新k8s中的应用。

  • CI/CD workflow:也被称为push-based workflow,它需要借助ci/cd,每次的配置变更由ci/cd主动的将配置推送到k8s集群中。

3.1在gitlab中启用kas

可以使用gitlab-ctl status 查看是否启用了gitlab-kas这个组件,如果没有启用,可以按照下面的步骤启用。

vim /etc/gitlab/gitlab.rb

gitlab_kas['enable'] = true	

gitlab-ctl reconfigure

3.2在k8s中安装agent

3.2.1前提条件

  • 安装helm
  • Gitlab server需要是以https的方式暴露

3.2.2配置GitOps workflow

在需要向k8s部署应用的仓库相同的组下面创建一个名为agentk的项目仓库。

在这个仓库中创建agent的配置文件,在其中定义GitOps workflow,配置文件的路径始终满足以下规则:.gitlab/agents/<agent-name>/config.yaml

gitops:
  manifest_projects:
  - id: "<group>/<project>"
    default_namespace: <k8s namespace>
    paths:
    - glob: '/**/*.{yml,yaml,json}'

ci_access:
  projects:
  - id: <group>/<project>
  groups:
  - id: <project>

理解这个配置文件

gitops: 定义了gitops workflow中需要监控哪些仓库下的哪些文件,并将这些文件的manifest同步到k8s中。

  1. manifest_projects:定义用于存放k8s manifest的仓库
  2. id: 仓库的路径
  3. default_namespace:当manifest里面没有指定namespace时,默认部署的namespace
  4. paths:指明manifest文件在仓库中具体的路径

ci_access: 定义了ci/cd workflow中哪些project和group能够访问并使用当前这个agent(前提是这些project和group要和agent在同一个group下面)

  1. projects: 项目的path
  2. groups:组的path

agentk仓库注册agent:

在项目中依次选择:Infrastructure > Kubernetes clusters > Connect a cluster (agent)

输入刚才已经创建配置文件的agent名称:,然后点击register,此时会弹出在k8s中安装agent的命令,直接复制运行即可:

需要注意的是,按照这个命令运行直接安装的是最新版本的gitlab-agent,但是我们最好安装和gitlab server相同的版本,所以可以先使用如下命令:helm search repo -l gitlab/gitlab-agent查找需要的版本,再使用指定版本进行安装。

helm upgrade --install gitlab-agent gitlab/gitlab-agent --version 0.6.1 \
    --namespace gitlab-agent \
    --create-namespace \
    --set config.token=y3QhfBmhezBsXBsjLWVpLuzYqBbJhypWA9y-gC8-kPTKsni5-w \
    --set config.kasAddress=wss://<gitlab_url>/-/kubernetes-agent/

最后可以看到agent的状态

注意

​ 一个agent只能被和它在相同group下的project和同group下的subgroup所引用。也就是说其实每个group下面我们只需要配置一个agent就可以了,然后其它project就能自由在pipeline中调用。

3.3使用GitOps workflow部署测试应用(仅专业版以上可用)

创建一个public的group(k8s-public),在里面创建一个public的project(manifest)用于存放manifest

kube-deployment

apiVersion: v1
kind: Service
metadata:
  labels:
    app: test
  name: test
spec:
  ports:
  - name: tcp
    port: 8080
    protocol: TCP
    targetPort: 8080
    nodePort: 30618
  selector:
    app: test
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: test
  name: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      imagePullSecrets:
      - name: gitlab-registry
      containers:
      - image: <your_image>
        name: test
        imagePullPolicy: Always
        env:
        - name: CI_ENVIRONMENT
          value: GitOps

然后修改agentk repo的配置文件,将这个仓库加入到gitops中:

gitops:
  manifest_projects:
  - id: "<group>/<project>"
    default_namespace: <k8s namespace>
    paths:
    - glob: '/**/*.{yml,yaml,json}'
  - id: "k8s-public/manifest"
    default_namespace: <k8s namespace>
    paths:
    - glob: '/**/*.{yml,yaml,json}'
    reconcile_timeout: 10s

ci_access:
  projects:
  - id: <group>/<project>
  groups:
  - id: <project>

保存并提交配置之后,去查看k8s中agent的日志就可以看到,agent中新增了一个worker,监视的是k8s-public/manifest这个仓库,并且会在k8s中部署manifest中的资源。然后可以尝试修改k8s-public/manifest中的配置,看看同步的效果!

3.4部署测试应用(使用ci/cd workflow)

在和agent相同的group下面创建一个测试的项目。项目的pipeline脚本中通过指定context即可和对应的agent所在的k8s集群进行交互。

示例:

deploy_to_stage:
  stage: deploy
  when: manual
  environment:
    name: stage
    url: http://<url>
  image:
    name: bitnami/kubectl:latest
    entrypoint: ['']
  script:
    - kubectl config get-contexts
    - kubectl config use-context /path/to/project:agent-name
    - sed  "s/CI_ENVIRONMENT_SLUG/$CI_ENVIRONMENT_SLUG/g" kube-deployment.yaml|kubectl -n ${CI_ENVIRONMENT_SLUG} apply -f -
  tags:
    - docker

context的命名由agent所在的project path加agent的name组成。

这里依然使用前面所创建的测试manifest部署应用。

4.使用gitlab runner集成k8s

4.1在k8s集群中安装gitlab runner

helm repo add gitlab-jh https://charts.gitlab.cn
helm fetch gitlab-jh/gitlab-runner --version 0.40.0                                                                                  
tar -xf gitlab-runner-0.40.0.tgz                                                                                                     
cd gitlab-runner/      

vim values.yaml 配置runner

gitlabUrl:
runnerRegistrationToken:
  config: |
    [[runners]]
      name = "k8s"
      executor = "kubernetes"
      [runners.kubernetes]
        image = "bitnami/kubectl:latest"
        image_pull_secrets = ["gitlab-registry"]
        privileged = true
        pull_policy = ["if-not-present"]
        memory_limit = "1Gi"
        service_cpu_limit = "1"
        service_memory_limit = "1Gi"
        helper_cpu_limit = "500m"
        helper_memory_limit = "100Mi"
        poll_interval = 5
        poll_timeout = 3600
        service_account = "gitlab-runner"
      [[runners.kubernetes.volumes.empty_dir]]
        name = "docker-certs"
        mount_path = "/certs/client"
        medium = "Memory"   
rbac:
  create: false
  serviceAccountName: gitlab-runner

创建所需的k8s资源,并部署runner:

kubectl create ns gitlab-runner
kubectl -n gitlab-runner create sa gitlab-runner
kubectl create clusterrolebinding gitlab-runner-cluster-admin --clusterrole=cluster-admin --serviceaccount=gitlab-runner:gitlab-runner
helm -n gitlab-runner install gitlab-runner . --set runners.tags="k8s\,prod"  

部署测试应用(依然使用上面的manifest):

pipeline的job:

deploy_to_pruduction:
  stage: deploy
  when: manual
  environment:
    name: pruduction
    url: http://<url>
  image:
    name: bitnami/kubectl:latest
    entrypoint: ['']
  script:
    - sed  "s/CI_ENVIRONMENT_SLUG/$CI_ENVIRONMENT_SLUG/g" kube-deployment.yaml|kubectl -n ${CI_ENVIRONMENT_SLUG} apply -f -
  tags:
    - k8s