我尝试将Gitlab部署到Kubernetes环境中

首先 / 首先

以前我们在k8s环境中部署了Gitbucket并使用过,现在我们将尝试运行更专业的Gitlab(sameersbn/docker-gitlab)容器。

GitLab的默认状态下,未登录账户无法自由浏览仓库。可以直接通过公开仓库、探索页面或用户页面的URL来浏览,但如果没有线索,就无法从首页跳转。

就这一点而言,我认为Gitbucket在中小规模使用和公开代码方面,即使不做任何修改,已经具备足够简便和功能齐全。

可供参考的资料

    • Gitbucketをkubernetesにデプロイしてみた

 

    https://github.com/sameersbn/docker-gitlab

环境

    • Kubernetes – v1.25.6 (Kubespray v2.21.0)

 

    • Rook/Ceph v1.9.13 (ceph version 16.2.10)

 

    • Ingress (Webアクセス用。Serviceでtype: LoadBalancerはSSHでのみ利用)

 

    OpenLDAP (ユーザー認証用)

Gitlab的CE版本只需连接LDAP即可,但是有点可惜的是不能有效地使用过滤器。

参考文献

    https://github.com/sameersbn/docker-gitlab

政策

sameersbn/docker-gitlabは、docker-compose.yamlファイルだけでなく、kuberentesで稼動させるためのYAMLファイルが含まれています。

$ git clone https://github.com/sameersbn/docker-gitlab.git
$ cd docker-gitlab
$ git checkout refs/tags/15.2.2 -b t_15.2.2

kubernetes/ディレクトリにあるファイルは、PVCの定義が抜けている点と、gitlab-svc.yamlでtype: LoadBalancerが指定されていますが、これは使わずにIngress経由でcontext-rootを変更し、/gitlab/に来たリクエストを振ります。

考慮点

負荷分散させるためのgitalyのクラスター化や、Object Storageの利用などは行なっていません。
そのため、redis/postgresqlはクラスター化させることは比較的容易だと思いますが、全てのインスタンス(Pod)は1つずつ動かしています。

请注意这些可能会成为SPoF(单点故障)的事项。

YAML文件

我将在介绍中包含已更改的部分,以展示整个YAML文件。

PVC文件

我们将准备一个用于使用Rook/Ceph的PVC。
在事先准备好的redis-rc.yml文件中,已经保护了/var/lib/redis目录,但是由于使用的镜像是以/data为持久化目标,因此我们需要更改该路径。

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-data-pvc
  namespace: gitlab
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 10Gi
  storageClassName: rook-ceph-block
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pg-data-pvc
  namespace: gitlab
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 200Gi
  storageClassName: rook-ceph-block
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gitlab-data-pvc
  namespace: gitlab
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 100Gi
  storageClassName: rook-ceph-block

SVC文件

只有在Service的YAML文件中进行了更改,即gitlab-svc.yml。

apiVersion: v1
kind: Service
metadata:
  name: gitlab
  namespace: gitlab
  labels:
    name: gitlab
spec:
  type: LoadBalancer
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: ssh
      port: 10022
      targetPort: 22
  selector:
    name: gitlab

由于无法通过22号端口进行外部访问,所以SSH访问将在10022号端口上等待。
在前端Web服务器上,将10022号端口的访问连接到此LoadBalancer分配的EXTERNAL-IP。

如前文所述,不使用EXTERNAL-IP来访问80端口,而是通过Ingress将请求代理到svc/gitlab服务(gitlab.gitlab.svc.cluster.local)。

RC文件

最近的Kubernetes中已經不再使用Deployment物件,而是使用ReplicationController物件。儘管寫這段文字的內容相對繁瑣,但還是維持原樣不作修改。

主要的变化是将volumeMounts/volumes更改为使用定义的PVC,并将加入DB连接密码、GitLab各种设置(特别是LDAP设置)。

apiVersion: v1
kind: ReplicationController
metadata:
  name: redis
  namespace: gitlab
spec:
  replicas: 1
  selector:
    name: redis
  template:
    metadata:
      name: redis
      labels:
        name: redis
    spec:
      containers:
      - name: redis
        image: redis:6.2.7
        ports:
        - name: redis
          containerPort: 6379
        volumeMounts:
        - mountPath: /data
          name: redis-data
        livenessProbe:
          exec:
            command:
            - redis-cli
            - ping
          initialDelaySeconds: 30
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command:
            - redis-cli
            - ping
          initialDelaySeconds: 5
          timeoutSeconds: 1
      volumes:
      - name: redis-data
        persistentVolumeClaim:
          claimName: redis-data-pvc
apiVersion: v1
kind: ReplicationController
metadata:                    
  name: postgresql   
  namespace: gitlab 
spec:
  replicas: 1
  selector:
    name: postgresql
  template:
    metadata:
      name: postgresql
      labels:
        name: postgresql
    spec:
      containers:
      - name: postgresql
        image: sameersbn/postgresql:12-20200524
        env:
        - name: DB_USER
          value: gitlab
        - name: DB_PASS
          value: e6cb3ac870c56287
        - name: DB_NAME
          value: gitlab_production
        - name: DB_EXTENSION
          value: pg_trgm,btree_gist
        ports:
        - name: postgres
          containerPort: 5432
        volumeMounts:
        - mountPath: /var/lib/postgresql
          name: data
        livenessProbe:
          exec:
            command:
            - pg_isready
            - -h
            - localhost
            - -U
            - postgres
          initialDelaySeconds: 30
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command:
            - pg_isready
            - -h
            - localhost
            - -U
            - postgres
          initialDelaySeconds: 5
          timeoutSeconds: 1
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: pg-data-pvc

postgresql-rc.yml文件的修改包括在PVC之外,还添加了DB_EXTENSION的btree_gist插件,并设置了用于DB连接的密码。

---                          
apiVersion: v1                           
kind: ReplicationController             
metadata:                            
  name: gitlab                    
  namespace: gitlab                               
spec:                       
  replicas: 1                                
  selector:                                   
    name: gitlab           
  template:                                                
    metadata:                                    
      name: gitlab                            
      labels:                                    
        name: gitlab                       
    spec:                                        
      containers:                      
      - name: gitlab                                     
        image: sameersbn/gitlab:15.2.2
        env:
        - name: DEBUG
          value: "false"

        - name: TZ
          value: "Asia/Tokyo"
        - name: GITLAB_TIMEZONE
          value: "Tokyo"

        - name: GITLAB_UNICORN_MEMORY_MAX
          value: "629145600"
        - name: GITLAB_HTTPS
          value: "false"
        - name: SSL_SELF_SIGNED
          value: "false"
        - name: OAUTH_ENABLED
          value: "false"

        - name: GITLAB_SECRETS_DB_KEY_BASE                 
          value: 1c10d46a57abd38c2c7957d87980fecb
        - name: GITLAB_SECRETS_SECRET_KEY_BASE
          value: e927734a7fdb0363e078ede99e4d7180
        - name: GITLAB_SECRETS_OTP_KEY_BASE
          value: 4f69440296aa7db3be56c5ea2d8f3876
                                       
        - name: GITLAB_ROOT_PASSWORD     
          value: ""       
        - name: GITLAB_ROOT_EMAIL
          value: "gitlab-admin@example.com"
        - name: GITLAB_EMAIL_REPLY_TO 
          value: "gitlab-admin@example.com"
        - name: GITLAB_EMAIL_SUBJECT_SUFFIX
          value: "[Gitlab]"
                                   
        - name: GITLAB_RELATIVE_URL_ROOT
          value: "/gitlab"   
        - name: GITLAB_HOST    
          value: "external.example.com"
        - name: GITLAB_PORT          
          value: "80"             
        - name: GITLAB_SSH_PORT                   
          value: "10022"    
                                             
        - name: GITLAB_NOTIFY_ON_BROKEN_BUILDS
          value: "false"   
        - name: GITLAB_NOTIFY_PUSHER                       
          value: "false"                
                            
        - name: GITLAB_BACKUP_SCHEDULE       
          value: daily         
        - name: GITLAB_BACKUP_TIME          
          value: 01:00                 
                                                         
        - name: DB_ADAPTER
          value: "postgresql"
        - name: DB_TYPE
          value: postgres
        - name: DB_HOST
          value: postgresql
        - name: DB_PORT
          value: "5432"
        - name: DB_USER
          value: gitlab
        - name: DB_PASS
          value: e6cb3ac870c56287
        - name: DB_NAME
          value: gitlab_production

        - name: REDIS_HOST
          value: redis                       
        - name: REDIS_PORT         
          value: "6379"  
                                                           
        - name: SMTP_ENABLED            
          value: "true" 
        - name: SMTP_DOMAIN                  
          value: "example.com"
        - name: SMTP_HOST                   
          value: "smtp.example.com"
        - name: SMTP_PORT                
          value: "25"     
        - name: SMTP_USER   
          value: ""      
        - name: SMTP_PASS             
          value: ""        
        - name: SMTP_STARTTLS
          value: "false"   
        - name: SMTP_AUTHENTICATION
          value: "plain"           
                             
        - name: IMAP_ENABLED   
          value: "false"

        - name: LDAP_ENABLED
          value: "true"                  
        - name: LDAP_LABEL
          value: "AINS LDAP"
        - name: LDAP_HOST
          value: "ldap.example.com"
        - name: LDAP_PORT  
          value: "636"
        - name: LDAP_UID   
          value: "uid"
        - name: LDAP_METHOD        
          value: "simple_tls"
        - name: LDAP_VERIFY_SSL
          value: "true"
        - name: LDAP_ACTIVE_DIRECTORY
          value: "false"
        - name: LDAP_ALLOW_USERNAME_OR_EMAIL_LOGIN
          value: "true"     
        - name: LDAP_BLOCK_AUTO_CREATED_USERS
          value: "true"
        - name: LDAP_BASE  
          value: "ou=People,ou=Proxy,dc=example,dc=com"
        - name: LDAP_USER_ATTRIBUTE_NAME
          value: "gecos"    
        - name: LDAP_USER_ATTRIBUTE_FIRSTNAME
          value: "gecos"
        - name: LDAP_USER_ATTRIBUTE_LASTNAME
          value: "uid"              
        - name: LDAP_PREVENT_LDAP_SIGN_IN                
          value: "false"

        ports:
        - name: http
          containerPort: 80
        - name: ssh
          containerPort: 22
        volumeMounts:
        - mountPath: /home/git/data
          name: data
        #livenessProbe:
        #  httpGet:
        #    path: /gitlab/
        #    port: 80
        #  initialDelaySeconds: 180
        #  timeoutSeconds: 5
        #readinessProbe:
        #  httpGet:
        #    path: /gitlab/
        #    port: 80
        #  initialDelaySeconds: 5
        #  timeoutSeconds: 1
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: gitlab-data-pvc

关于Ingress等其他设置

通过设置环境变量(GITLAB_RELATIVE_URL_ROOT、GITLAB_HOST),使外部访问时的顶级地址为”https://external.example.com/gitlab/”。

我們將使用透過這個主機的10022端口通過ssh進行clone/push等操作。

为了这个原因,我们会进行Ingress和边界服务器(external.example.com/)的端口转发设置。不涉及在边界内部配置的Web服务器(https://external.example.com/)的设置方法等。

来自external.example.com的连接通过Ingress连接

在 ingress-nginx 命名空间中定义了以下的 Service(svc) 对象。
这个 externalName 是指向在 gitlab 命名空间中通过 gitlab-svc.yml 创建的 svc/gitlab 对象。

---
apiVersion: v1
kind: Service
metadata:
  name: gitlab-svc
  labels:
    group: ingress-nginx
  namespace: ingress-nginx
spec:
  type: ExternalName
  externalName: gitlab.gitlab.svc.cluster.local

在Ingress自身的设置中,添加以下定义。请注意不要覆盖已经使用的其他定义。

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: default
  labels:
    group: ingress-nginx
  namespace: ingress-nginx
spec:
  rules:
  - http:
      paths:
      - backend:
          service:
            name: gitlab-svc
            port:
              number: 80
        path: /gitlab
        pathType: Prefix
      ...  ## 他の backend: 定義が続く

在 ingress.yaml 中定义的 .spec.rules.http.paths.backend.service.name 指的是 ingress-svc-gitlab.yaml 中定义的 namespace: ingress-nginx 的 svc/gitlab-svc 对象。

将10022端口进行端口转发。

这台服务器的特点在于它是基于CentOS 8 Stream操作系统,并且使用了Ansible工具。

- name: firewalld enablse the masquerade service for external
  firewalld:
    zone: external
    masquerade: yes
    permanent: true
    state: enabled

- name: firewalld external config for the gitlab ssh port
  firewalld:
    zone: external
    rich_rule: rule family=ipv4 forward-port port=10022 protocol=tcp to-port=10022 to-addr=192.168.1.22
    permanent: yes
    immediate: yes
    state: enabled

如果使用Ubuntu

我在使用Ubuntu时,如果要使用ufw会变得有点复杂。
但是我使用socat而不是ufw,因为它更加稳定,所以我选择使用socat。

#!/bin/bash

TARGET="10022"

ps auxww|grep "${TARGET}" | grep -v grep > /dev/null
rc="$?"

if test "${rc}" != "0" ; then
    nohup socat tcp4-listen:10022,reuseaddr,fork TCP:192.168.1.22:10022 &
fi

为了在重新启动或异常关闭时自动启动,我们使用cron进行调度。

#Ansible: check-gitlab
2 * * * * /usr/local/sbin/check-gitlab

关于升级

目前我们正在使用15.3.1和15.3.3版本。为了更新到15系列的最新版本(15.11.13),首先需要升级到15.4.6,包括15.2.2在内。

在GitLab中,提供了升级路径,所以必须查看官方网站的信息确认。

目前推荐更新版本依次为 15.0.5 > 15.1.6(适用于具备多个网络节点的GitLab实例)> 15.4.6 > 15.11.13。从版本15.11.13起,可以按照16.1.5→16.3.6→16.6.1的顺序进行最新版的更新。

我害怕更新到最新版本会遇到bug,所以我想在15.11.13之前先观察一段时间。

建议将备份文件复制到本地,以免遗失,备份文件位于Pod上的/home/git/data/backups/目录下。

由于存在测试环境,所以我将尝试将gitlab-rc.yaml中的gitlab版本更改为15.4.6并应用。

diff --git a/gitlab/yaml/gitlab-rc.yml b/gitlab/yaml/gitlab-rc.yml
index f489a43..7b42ad1 100644
--- a/gitlab/yaml/gitlab-rc.yml
+++ b/gitlab/yaml/gitlab-rc.yml
@@ -16,7 +16,7 @@ spec:
     spec:
       containers:
       - name: gitlab
-        image: sameersbn/gitlab:15.3.1
+        image: sameersbn/gitlab:15.4.6
         env:
         - name: DEBUG
           value: "false"

由于应用这个设置后,Pod不会自动重启,因此需要手动删除Pod。

在新版本中,gitlab的Pod启动后,当检查日志时,会在重新编译资源(使用相对URL)这部分暂停一段时间。

success Packages successfully patched.
Done in 41.60s.
yarn add v1.22.19
[1/5] Validating package.json...
[2/5] Resolving packages...
[3/5] Fetching packages...
[4/5] Linking dependencies...
warning "@gitlab/eslint-plugin > eslint-plugin-jest > @typescript-eslint/experimental-utils > @typescript-eslint/typescript-estree > tsutils@3.17.1" has unmet peer dependency "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta".
warning "@graphql-eslint/eslint-plugin > graphql-config > @endemolshinegroup/cosmiconfig-typescript-loader > ts-node@9.1.1" has unmet peer dependency "typescript@>=2.7".
warning Workspaces can only be enabled in private projects.
[5/5] Building fresh packages...
warning "ajv" is already in "devDependencies". Please remove existing entry first before adding it to "dependencies".
warning Workspaces can only be enabled in private projects.
success Saved 0 new dependencies.
$ node ./scripts/frontend/postinstall.js
success Dependency postinstall check passed.
success Packages successfully patched.
Done in 3.24s.
Recompiling assets (relative_url in use), this could take a while...

虽然需要花费相当长的时间,但是耐心等待,处理将会进行并且最终可以通过WebUI登录。

由于后台可能仍在进行更新处理,因此请不要急于进行下一个版本的更新。请通过管理界面确认更新处理已完成。

以 root 身份登入後,導航至管理員 → 監控 → 背景遷移,並確認所有處理達到「完成」狀態。

确认到此再继续进入下一次升级。

以上