Gitlab CI 集成 Kubernetes 集羣部署 Spring Boot 項目

本文首發於個人我的博客,Gitlab CI 集成 Kubernetes 集羣部署 Spring Boot 項目 ,歡迎訪問!html

在上一篇博客中,咱們成功將 Gitlab CI 部署到了 Docker 中去,成功建立了 Gitlab CI Pipline 來執行 CI/CD 任務。那麼這篇文章咱們更進一步,將它集成到 K8s 集羣中去。這個纔是咱們最終的目標。衆所周知,k8s 是目前最火的容器編排項目,不少公司都使用它來構建和管理本身容器集羣,能夠用來作機器學習訓練以及 DevOps 等一系列的事情。java

在這裏,咱們聚焦 CI/CD,針對於 Spring Boot 項目,藉助 Gitlab CI 完成流水線的任務配置,最終部署到 K8s 上去。本文會詳細講解如何一步步操做,完成這樣的一條流水線。node

軟件的核心版本以下:git

  • Kubernetes:v1.16.0-rc.2
  • 部署 Gitlab 的 Docker:19.03.2
  • Gitlab CE:gitlab-ce:latest
  • Gitlab Runner: helm chart gitlab-runner-v0.10.0-rc1
  • Helm:2.14.3

k8s 集羣信息:github

[root@master01 ~]# kubectl get nodes
NAME            STATUS   ROLES    AGE   VERSION
172.17.11.62    Ready    <none>   37d   v1.16.0-rc.2
172.17.13.120   Ready    <none>   91d   v1.16.0-rc.2
172.17.13.121   Ready    <none>   91d   v1.15.1
172.17.13.122   Ready    <none>   91d   v1.16.0-rc.2
172.17.13.123   Ready    <none>   92d   v1.16.0-rc.2

Runner 的安裝和註冊

在上一篇博客《Docker Gitlab CI 部署 Spring Boot 項目》中咱們已經實現了在 Docker 中部署這一套流水線,可是單節點的 Docker 只適合本地調試,若是真正搭建起來用於公司的 CI/CD 工做,仍是會把它放到集羣環境下,所以如今咱們將流水線部署到 k8s 上。web

其實部署到 k8s 上包含兩種,一個是把 Gitlab 部署上去,另外一個是把 CI 這部分部署上去(也就是 Gitlab Runner)。其實 Gitlab 自己就是一個服務,部署在哪裏均可以,能夠選擇 Docker 部署,也能夠找一臺服務器單獨部署,做爲代碼倉庫。最關鍵的實際上是後者,CI/CD 的流程複雜且消耗資源多,部署在集羣上就能自動調度,達到資源利用最大化。那麼下面着重講 Gitlab Runner 的 k8s 部署,想了解前者的能夠看官方文檔,經過 helm 安裝。GitLab cloud native Helm Chartspring

假設 Gitlab 都已經部署成功了,那麼下面開始安裝 Gitlab Runner。具體的就是把 Runner 安裝到某個節點的 pod 的上,在處理具體的 CI 任務時,Runner 會啓動新的 pod 來執行任務,由 k8s 進行節點間的調度。docker

通常來講,咱們會使用 Helm 來進行安裝,Helm 是相似 CentOS 裏的 yum,是一種軟件管理工具,能夠幫咱們快速安裝軟件到 k8s 上。咱們須要在其中一個主節點上安裝好 Helm 的 client 和 server。具體可參考:ubuntu

顯示以下,證實安裝成功。api

[root@master01 ~]# helm version
Client: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"}

添加 gitlab 源並更新。

[root@master01 ~]# helm repo add gitlab https://charts.gitlab.io
[root@master01 ~]# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "gitlab" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete.
[root@master01 ~]# helm search gitlab-runner
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
gitlab/gitlab-runner    0.10.0-rc1      12.4.0-rc1      GitLab Runner

這裏看到有兩個版本,一個是 chart version 一個是 app version。 chart 是 helm 中描述相關的一組 Kubernetes 資源的文件集合,裏面包含了一個 value.yaml 配置文件和一系列模板(deployment.yaml、svc.yaml 等),而具體的 app 是經過須要單獨去 Docker Hub 上拉取的。這兩個字段分別就是描述了這兩個版本號。

安裝前先建立一個 gitlab 的 namespace,併爲其配置相應的權限。

[root@master01 ~]# kubectl create namespace gitlab-runners

建立一個 rbac-runner-config.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab
  namespace: gitlab-runners
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: gitlab-runners
  name: gitlab
rules:
- apiGroups: [""] #"" indicates the core API group
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["*"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: gitlab
  namespace: gitlab-runners
subjects:
- kind: ServiceAccount
  name: gitlab # Name is case sensitive
  apiGroup: ""
roleRef:
  kind: Role #this must be Role or ClusterRole
  name: gitlab # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io

而後執行如下。

[root@master01 ~]# kubectl create -f rbac-runner-config.yaml

接下來配置 runner 的註冊信息。上一篇博客中,咱們是先安裝 gitlab runner 而後進入容器執行 gitlab-runner register 來進行註冊的。在 k8s 可支持這麼操做,可是同時 helm 也提供了一個配置文件能夠在安裝 runner 的時候爲其註冊一個默認的 runner。咱們能夠去 gitlab-runner 的項目源碼 中獲取到 values.yaml 這個配置文件。配置文件比較長,能夠根據須要本身去配置,下面就貼下本文中須要配置的地方。

## 拉取策略, 先取本地鏡像
imagePullPolicy: IfNotPresent

## 配置 gitlab 的 url 和註冊令牌
## 能夠在 gitlab 項目中設置 --CI/CD--Runner-- 手動設置 specific Runner 中查詢
gitlabUrl: http://172.17.193.109:7780/
runnerRegistrationToken: "qzxposxDst_Nnq5MMnPf"

## 和以前配置的 rbac name 對應
rbac:
  serviceAccountName: gitlab

## 指定關聯 runner 的標籤
tags: "maven,docker,k8s"

privileged: true

serviceAccountName: gitlab

而後經過 helm install 安裝 runner 就能夠了。token 和 url 不知道如何獲取的,見我上一篇博客。

[root@master01 ~]# helm install --name gitlab-runner gitlab/gitlab-runner -f values.yaml --namespace gitlab-runners
NAME:   gitlab-runner
LAST DEPLOYED: Fri Oct 25 10:08:40 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME              DATA  AGE
gitlab-runner-gitlab-runner  5     <invalid>

==> v1/Deployment
NAME              READY  UP-TO-DATE  AVAILABLE  AGE
gitlab-runner-gitlab-runner  0/1    0           0          <invalid>

==> v1/Secret
NAME              TYPE    DATA  AGE
gitlab-runner-gitlab-runner  Opaque  2     <invalid>
NOTES:
Your GitLab Runner should now be registered against the GitLab instance reachable at: "http://172.17.193.109:7780/"

等待一段時間後就能夠在 k8s 的 dashboard 上看到啓動成功的 runner 的 pod 了。

這個時候能夠進入 pod 看一下 runner 的配置文件(/home/gitlab-runner/.gitlab-runner/config.toml)了。這個文件就是根據以前配置的 values.yaml 自動生成的。

listen_address = "[::]:9252"
concurrent = 10
check_interval = 30
log_level = "info"

[session_server]
  session_timeout = 1800

[[runners]]
  name = "gitlab-runner-gitlab-runner-6767fdcb6-pjvbz"
  request_concurrency = 1
  url = "http://172.17.193.109:7780/"
  token = "Soxf6HEQVj4wr25zsGUS"
  executor = "kubernetes"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
  [runners.kubernetes]
    host = ""
    bearer_token_overwrite_allowed = false
    image = "ubuntu:16.04"
    namespace = "gitlab-runners"
    namespace_overwrite_allowed = ""
    privileged = true
    service_account = "gitlab"
    service_account_overwrite_allowed = ""
    pod_annotations_overwrite_allowed = ""
    [runners.kubernetes.pod_security_context]
    [runners.kubernetes.volumes]

註冊成功後就能夠在 gitlab 的 web UI 上看到對應 token 的 runner 了。

項目的建立與 Dockerfile

這邊沒啥好說的,直接新建一個 spring boot 的 hello world 項目,並添加一個 dockerfile。

FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY  /target/demo-0.0.1-SNAPSHOT.jar app.jar
ENV PORT 5000
EXPOSE $PORT
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Dserver.port=${PORT}","-jar","/app.jar"]

.gitlab-ci.yml 改造

因爲使用的 executor 不一樣,因此. gitlab-ci.yml 和以前也有些不一樣。好比 k8s 貌似不支持緩存,volume 掛載方式的不一樣,以及權限問題等。先看完整的配置文件。

image: docker:latest
variables:
  DOCKER_DRIVER: overlay2
  # k8s 掛載本地卷做爲 maven 的緩存
  MAVEN_OPTS: "-Dmaven.repo.local=/home/cache/maven"
  REGISTRY: "registry.cn-hangzhou.aliyuncs.com"
  TAG: "tmp-images/hello-spring"
  TEST_POD: "hello-spring"
  TEST_POD_CONTAINER: "spring-boot"
stages:
  - package
  - build
  - deploy
  - release
maven-package:
  image: maven:3.5-jdk-8-alpine
  tags:
    - maven
  stage: package
  script:
    - mvn clean package -Dmaven.test.skip=true
  artifacts:
    paths:
      - target/*.jar
docker-build:
  tags:
    - docker
  stage: build
  script:
    - echo "Building Dockerfile-based application..."
    - docker login --username=maoqyhz@outlook.com registry.cn-hangzhou.aliyuncs.com -p [your pwd]
    - docker build -t $REGISTRY/$TAG:$CI_COMMIT_SHORT_SHA .
    - docker push $REGISTRY/$TAG
  only:
    - master
k8s-deploy:
  image: bitnami/kubectl:latest
  tags:
    - k8s
  stage: deploy
  script:
    - echo "deploy to k8s cluster..."
    - kubectl version
    - kubectl set image deployment/$TEST_POD $TEST_POD_CONTAINER=$REGISTRY/$TAG:$CI_COMMIT_SHORT_SHA --namespace gitlab-runners
  only:
    - master
#   when: manual
release:
  stage: release
  script:
    - echo "release to prod env..."
  when: manual

須要特別指出的地方有 3 點:

  • 綁定本地 Docker 守護進程。

  • maven 倉庫的緩存。
  • k8s 測試鏡像的部署。

綁定本地 Docker 守護進程

Docker 環境下咱們使用了一個 docker:dind 的服務用於執行 docker build 到本地鏡像倉庫。在 k8s 中,發現用了這個東西會出現 runner 連不到 Gitlab 服務器上,報了一個 host xxx is unreachable 的錯誤。因此最終採用 volume 綁定的形式把本地 docker.sock 經過 host_path 的方式掛載到 runner 中去。

# /home/gitlab-runner/.gitlab-runner/config.toml
[[runners.kubernetes.volumes.host_path]]
  name = "docker"
  mount_path = "/var/run/docker.sock"

Maven 倉庫的緩存

在以前的 Docker 環境下,volume 的掛載是一件很容易的事情,咱們直接能夠把本地的 maven 倉庫掛載。或者經過 cache 節點進行緩存的配置。可是在 k8s 環境下,我折騰了好久也沒找到 cache 節點的配置方法。生產環境下應該能夠配置 aws 或者 minio 做爲緩存。目前最終測試出來本地可行的方案是經過掛載 nfs pvc。

首先咱們須要建立一個 PersistentVolume 和 PersistentVolumeClaim。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: gitlab-runner-maven-repo
spec:
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 5Gi
  mountOptions:
  - nolock
  nfs:
    path: /opt/maven-cache/
    server: 172.17.13.120
  persistentVolumeReclaimPolicy: Recycle
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gitlab-runner-maven-repo-claim
  namespace: gitlab-runners
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  volumeName: gitlab-runner-maven-repo
status:
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 5Gi

在 runner 的配置中綁定。

# /home/gitlab-runner/.gitlab-runner/config.toml
[[runners.kubernetes.volumes.pvc]]
  name = "gitlab-runner-maven-repo-claim"
  mount_path = "/home/cache/maven"

而後配置 MAVEN_OPTS 到掛載的路徑,就能夠實現 maven 倉庫的緩存了。

k8s 測試鏡像的部署

在 Docker 中,build 以後的鏡像直接能夠經過 docker run 啓動。在 k8s 下相對比較複雜,須要經過配置 deployment.yaml 來進行啓動,若是須要外部訪問,還須要配置 services 等組件。這裏僅僅是爲了演示流水線的執行過程,就預先啓動一個 pod 做爲測試。每次觸發新的構建任務時,直接經過命令 kubectl set image 替換掉舊鏡像就能夠了。

執行流水線

git push & commit 代碼後,流水線會自動建立執行。完成後可在以前配置的 services 端口看到部署的結果。

Troubleshooting

第一次實操過程當中,坑仍是不少的,在這裏記錄一下。

apiVersion 發生變化產生的問題

k8s 迭代的速度較快,使用最新版可是周邊生態沒有跟上腳步同步更新的話很容易產生問題。在 1.16 中 API versions 就有了比較大的變化,因此致使 helm 的模板沒及時更新出錯了。

Error: validation failed: unable to recognize "": no matches for kind"Deployment"in version"extensions/v1beta1"

1. Helm 服務端安裝失敗

以往 helm 的服務端安裝直接調用 helm init 就能夠了。因爲 apiVersion 的問題須要經過如下命令才能裝上。完整的安裝的命令以下:

# 添加權限控制帳戶
[root@master01 ~]# kubectl create serviceaccount --namespace kube-system tiller
[root@master01 ~]# kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller

# 經過此命令修改 apiVersion 由 extensions/v1beta1 變爲 apps/v1,並使用國內鏡像。
[root@master01 ~]# helm init --upgrade -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.14.3 --service-account tiller --override spec.selector.matchLabels.'name'='tiller',spec.selector.matchLabels.'app'='helm' --output yaml | sed 's@apiVersion: extensions/v1beta1@apiVersion: apps/v1@' | kubectl apply -f -

[root@master01 ~]# [root@master01 ~]# helm repo remove stable
[root@master01 ~]# helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

2. Gitlab 沒法直接託管 k8s 集羣安裝 Runner

一樣的,上文咱們是經過手動安裝的 helm server 和 gitlab runner。而實際上 Gitlab CE 支持免費綁定 1 個 k8s 集羣,而且提供一鍵式的安裝。可是因爲版本號的問題,我這裏安裝不上。你們能夠用低版本的 k8s 測試一下。具體在 Gitlab WebUI 中 root 帳戶登陸,管理中心 -- Kubernetes 或者項目管理界面下,運維 --Kubernetes 進行集羣的綁定。

綁定後會託管到 Gitlab,建立一個 namespace 來進行程序的安裝。

Runner 中 config.toml 配置消失

以前在配置 runner 的 volume 的時候是直接進入 pod 內部修改 config.toml 這個配置文件的。這樣會存在一個問題,若是集羣重啓了,修改過的數據就全沒了,由於重啓後會從新起一個 pod。以往在 Docker 中,咱們會 commit 成一個新的鏡像保存,但這樣保存的鏡像顯然沒有廣泛性,而且每次還須要修改 deployment.yaml 中的鏡像,所以最好的方法是修改 helm 的 chart 中的模板,把全部的配置信息都寫入 yaml 文件中,而非直接修改 pod 自己中的配置。

所以爲了可以高度的配置咱們本身的 runner,就不直接從 helm 官方的源鏡像安裝了。

[root@master01 ~]# helm fetch gitlab/gitlab-runner

helm fetch 能夠將 chart 下載到本地,能夠看到是. tgz 格式的,解壓後的文件夾內包含的就是描述資源的一些模板文件。

gitlab-runner-v0.10.0-rc1
├── CHANGELOG.md
├── Chart.yaml
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── scripts
│   ├── changelog2releasepost
│   └── prepare-changelog-entries.rb
├── templates
│   ├── _cache.tpl
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── _env_vars.tpl
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── NOTES.txt
│   ├── role-binding.yaml
│   ├── role.yaml
│   ├── secrets.yaml
│   └── service-account.yaml
└── values.yaml

以前提取的 values.yaml 和這裏是同樣的,用於設置一些可配置的變量。具體的模板在 templates 目錄下。這裏咱們能夠仔細看下 values.yaml、deployment.yaml 和 configmap.yaml,會發現 runner 的 config.toml 文件中的大部分信息都是根據這三個配置文件中的信息自動生成的。那麼咱們只須要額外配置一下須要的 volume 就能夠了。

這裏咱們須要修改 vaules.yaml 和 configmap.yaml。

## configmap.yaml
if ! sh /scripts/register-the-runner; then
  exit 1
fi

# add volume config
cat >>/home/gitlab-runner/.gitlab-runner/config.toml <<EOF
  [[runners.kubernetes.volumes.pvc]]
    name = "{{.Values.maven.cache.pvcName}}"
    mount_path = "{{.Values.maven.cache.mountPath}}"
  [[runners.kubernetes.volumes.host_path]]
    name = "docker"
    mount_path = "/var/run/docker.sock"
EOF

# Start the runner
exec /entrypoint run --user=gitlab-runner \
  --working-directory=/home/gitlab-runner

在 register 和 start 之間添加配置 volume 的信息,並在 values.yaml 配置相應的變量。

## my config
#  maven cache
maven:
  cache:
    pvcName: gitlab-runner-maven-repo-claim
    mountPath: /home/cache/maven

配置完成後,執行 helm upgrade gitlab-runner gitlab-runner-v0.10.0-rc1/

以後不管重啓 pod 仍是集羣,runner 的配置信息都能被正確加載了。

鏡像下載不到的問題

gcr.io 沒法訪問,Docker Hub 訪問速度慢,這個你們都懂的。

  • 阿里雲鏡像加速器。
  • 尋找能用的鏡像 pull 下來本地打 tag。七牛雲鏡像中心
  • 尋找本地的鏡像服務器

阿里雲通用鏡像服務器: https://registry.cn-hangzhou.aliyuncs.com

helm chart 鏡像

  • 阿里:https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
  • Azure:http://mirror.azure.cn/kubernetes/charts

總結

本文主要描述了在 k8s 上部署流水線的整個過程,因爲對 k8s 不太熟悉,遇到了很多的坑,國內相關的博客也較少,因此就記錄一下整個配置的過程。對於整個流程不熟悉的讀者能夠先閱讀上一篇博客《Docker Gitlab CI 部署 Spring Boot 項目》。下一步的工做就是要將流水線對接到實際的項目中去了。

未完待續...

源碼

參考資料

相關文章
相關標籤/搜索