在 Kubernetes 中安装 Gitlab

现在很少写这种安装类的博客了。之前在公司部署了一个 Gitlab 作为日常使用,因为步骤比较繁琐。在此把零零散散的资料汇聚一下,记录一个比较完整的安装过程

环境#

  • Kubernetes 1.13.1 三个高可用节点
  • 每个节点上一个空余磁盘

步骤#

安装 Rook#

Rook 提供了基于 Ceph 的分布式存储,我们利用每个节点上的空余磁盘来支撑 Kubernetes 里的 PV/StorageClass 等

Helm 安装#

首先,初始化磁盘

mkfs.ext4 /dev/vdb
mount /dev/vdb /var/lib/rook
mkdir /var/lib/rook
# TODO: add to /etc/fstab

然后通过 Helm 安装 Rook

helm repo add rook-stable https://charts.rook.io/stable
helm install --namespace rook-ceph-system rook-stable/rook-ceph

部署完成后可以看到rook-ceph-system Namespace 下运行的 Resource:

创建 CephCluster#

#################################################################################
# This example first defines some necessary namespace and RBAC security objects.
# The actual Ceph Cluster CRD example can be found at the bottom of this example.
#################################################################################
apiVersion: v1
kind: Namespace
metadata:
  name: rook-ceph
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: rook-ceph-osd
  namespace: rook-ceph
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: rook-ceph-mgr
  namespace: rook-ceph
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-osd
  namespace: rook-ceph
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: [ "get", "list", "watch", "create", "update", "delete" ]
---
# Aspects of ceph-mgr that require access to the system namespace
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-mgr-system
  namespace: rook-ceph
rules:
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - get
  - list
  - watch
---
# Aspects of ceph-mgr that operate within the cluster's namespace
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-mgr
  namespace: rook-ceph
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - batch
  resources:
  - jobs
  verbs:
  - get
  - list
  - watch
  - create
  - update
  - delete
- apiGroups:
  - ceph.rook.io
  resources:
  - "*"
  verbs:
  - "*"
---
# Allow the operator to create resources in this cluster's namespace
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-cluster-mgmt
  namespace: rook-ceph
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: rook-ceph-cluster-mgmt
subjects:
- kind: ServiceAccount
  name: rook-ceph-system
  namespace: rook-ceph-system
---
# Allow the osd pods in this namespace to work with configmaps
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-osd
  namespace: rook-ceph
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: rook-ceph-osd
subjects:
- kind: ServiceAccount
  name: rook-ceph-osd
  namespace: rook-ceph
---
# Allow the ceph mgr to access the cluster-specific resources necessary for the mgr modules
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-mgr
  namespace: rook-ceph
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: rook-ceph-mgr
subjects:
- kind: ServiceAccount
  name: rook-ceph-mgr
  namespace: rook-ceph
---
# Allow the ceph mgr to access the rook system resources necessary for the mgr modules
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-mgr-system
  namespace: rook-ceph-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: rook-ceph-mgr-system
subjects:
- kind: ServiceAccount
  name: rook-ceph-mgr
  namespace: rook-ceph
---
# Allow the ceph mgr to access cluster-wide resources necessary for the mgr modules
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rook-ceph-mgr-cluster
  namespace: rook-ceph
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: rook-ceph-mgr-cluster
subjects:
- kind: ServiceAccount
  name: rook-ceph-mgr
  namespace: rook-ceph
---
#################################################################################
# The Ceph Cluster CRD example
#################################################################################
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  cephVersion:
    # For the latest ceph images, see https://hub.docker.com/r/ceph/ceph/tags
    image: ceph/ceph:v13.2.2-20181023
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: true
  dashboard:
    enabled: true
  storage:
    useAllNodes: true
    useAllDevices: false
    config:
      databaseSizeMB: "1024"
      journalSizeMB: "1024"

这个 yaml 列表包含了如下的 Resource:

  1. ns/rook-ceph
  2. sa/rook-ceph/rook-ceph-mgr
  3. role/rook-ceph/rook-ceph-osd
  4. role/rook-ceph/rook-ceph-mgr-system
  5. role/rook-ceph/rook-ceph-mgr
  6. rolebinding/rook-ceph/rook-ceph-cluster-mgmt
  7. rolebinding/rook-ceph/rook-ceph-osd
  8. rolebinding/rook-ceph/rook-ceph-mgr-system
  9. rolebinding/rook-ceph/rook-ceph-mgr-cluster
  10. cephecluster/rook-ceph/rook-ceph

Helm Rook 里已经定义好了相关的 CRD,我们再这里就是要创建一个 CephCluster 的资源,可以看到它的 YAML 里指定了之前我们初始化好的/var/lib/rook目录。

kubectl create -f cluster.yaml

创建 StorageClass#

apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  name: replicapool
  namespace: rook-ceph
spec:
  failureDomain: host
  replicated:
    size: 3
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: rook-ceph-block
provisioner: ceph.rook.io/block
parameters:
  blockPool: replicapool
  # The value of "clusterNamespace" MUST be the same as the one in which your rook cluster exist
  clusterNamespace: rook-ceph
  # Specify the filesystem type of the volume. If not specified, it will use `ext4`.
  fstype: xfs
# Optional, default reclaimPolicy is "Delete". Other options are: "Retain", "Recycle" as documented in https://kubernetes.io/docs/concepts/storage/storage-classes/
reclaimPolicy: Retain

页面#

Rook 自带了一个 UI,我们可以通过 Ingress 配置的地址来访问和管理 Ceph 集群:

安装 Cert Manager#

我们后面需要通过 HTTPS 访问 Gitlab/Rook 等,Cert Manager 与 Ingress 一起提供这个功能。

Helm 安装#

wget https://github.com/helm/charts/blob/master/stable/cert-manager/cert-manager-v0.6.0-dev.5.tgz
helm install --name=cert-manager --namespace=kube-system ./cert-manager-v0.6.0-dev.5.tgz

创建 ClusterIssuer#

ClusterIssuer 是 Cert Manager 提供的 CRD

apiVersion: v1
items:
- apiVersion: certmanager.k8s.io/v1alpha1
  kind: ClusterIssuer
  metadata:
    creationTimestamp: 2018-12-24T07:30:04Z
    generation: 1
    name: letsencrypt-prod
    namespace: ""
    resourceVersion: "2346374"
    selfLink: /apis/certmanager.k8s.io/v1alpha1/clusterissuers/letsencrypt-prod
    uid: bb17c08d-074d-11e9-9e39-525400f36999
  spec:
    acme:
      email: <your-email>
      http01: {}
      privateKeySecretRef:
        key: ""
        name: letsencrypt-prod
      server: https://acme-v02.api.letsencrypt.org/directory
  status:
    acme:
      uri: https://acme-v02.api.letsencrypt.org/acme/acct/48274855
    conditions:
    - lastTransitionTime: 2018-12-24T07:30:09Z
      message: The ACME account was registered with the ACME server
      reason: ACMEAccountRegistered
      status: "True"
      type: Ready
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

中间的<you-email>需要替换成具体的 Email。

安装 Gitlab#

Helm 安装#

Gitlab 也是需要通过 Helm 安装,但是需要自定义的变量比较多。

以下是 values 自定义的部分

redisImage: redis:3.2.10
redisDedicatedStorage: true
redisStorageSize: 5Gi
postgresImage: postgres:9.6.3
postgresStorageSize: 30Gi
gitlabRailsStorageSize: 70Gi
gitlabRegistryStorageSize: 70Gi
gitlabConfigStorageSize: 1Gi
baseDomain: <domain>
baseIP: <ip>
legoEmail: <your-email>
gitlabConfigStorageClass: rook-ceph-block
gitlabDataStorageClass: rook-ceph-block
gitlabRegistryStorageClass: rook-ceph-block
postgresStorageClass: rook-ceph-block
redisStorageClass: rook-ceph-block
gitlab: CE
gitlabCEImage: gitlab/gitlab-ce:11.6.5-ce.0
gitlabRunnerImage: gitlab/gitlab-runner:alpine-v11.6.1
runners:
  privileged: true
gitlab-runner:
  image: gitlab/gitlab-runner:alpine-v11.6.1

说明:

  1. baseDomain/baseIP/legoEmail: 这几个需要根据具体情况替换
  2. *Size: 这些变量的值就是我们安装的 Gitlab 所需要使用的空间,当然都是越大越好

Helm 安装:

helm install --name gitlab -f values.yaml gitlab/gitlab-omnibus --namespace=gitlab

修改 Ingress#

之前已经安装了 Cert Manger,我们可以通过给 Gitlab 的 Ingress 添加如下的 Annotation 来支持 HTTPS

# kubectl edit ingress gitlab-gitlab -n gitlab
'certmanager.k8s.io/cluster-issuer: letsencrypt-prod'

我们可以通过 Ingress 看到,有以下四个域名可用了:

  • gitlab.aks.myalauda.cn: 主页面
  • registry.aks.myalauda.cn: 镜像仓库
  • mattermost.aks.myalauda.cn: 一个团队协作工具
  • prometheus.aks.myalauda.cn: 监控信息

权限相关#

kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
 metadata:
   name: gitlab-cluster-admin
 subjects:
 - kind: ServiceAccount
   name: gitlab
   namespace: gitlab
 roleRef:
   kind: ClusterRole
   name: cluster-admin
   apiGroup: rbac.authorization.k8s.io

增加 Gitlab 的权限,不然可能会有未知的访问问题

Gitlab Runner 配置#

让 Gitlab Runner 支持 docker-in-doker

cat >> /etc/gitlab-runner/config.toml << EOF
          [[runners.kubernetes.volumes.host_path]]
            name = "docker"
            path = "/var/run/docker.sock"
            mount_path = "/var/run/docker.sock"
            read_only = false
    EOF

这个是通过修改 configmaps/gitlab/gitlab-gitlab-runner 实现:

安装 SonarQube#

SonarQube 可以提供对多种语言的代码静态分析,它自身是一个 Server,带页面,也支持各种插件。与 Gitlab 的集成是通过 Gitlab Account 以及 Token 等方式实现。

YAML 安装#

以下是相关 YAML

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sonar-data
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: rook-ceph-block
  resources:
    requests:
      storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sonar-extensions
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: rook-ceph-block
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Secret
metadata:
  name: postgres
type: Opaque
data:
  password: NDl1ZjNtenMxcWR6NXZnbw==
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: sonarqube
  name: sonarqube
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      containers:
        - name: sonarqube
          image: sonarqube:7.1
          resources:
            requests:
              cpu: 500m
              memory: 1024Mi
            limits:
              cpu: 2000m
              memory: 2048Mi
          volumeMounts:
          - mountPath: "/opt/sonarqube/data/"
            name: sonar-data
          - mountPath: "/opt/sonarqube/extensions/"
            name: sonar-extensions
          env:
          - name: "SONARQUBE_JDBC_USERNAME"
            value: "gitlab"
          - name: "SONARQUBE_JDBC_URL"
            value: "jdbc:postgresql://gitlab-gitlab-postgresql.gitlab/sonar"
          - name: "SONARQUBE_JDBC_PASSWORD"
            valueFrom:
              secretKeyRef:
                name: postgres
                key: password
          ports:
          - containerPort: 9000
            protocol: TCP
      volumes:
      - name: sonar-data
        persistentVolumeClaim:
          claimName: sonar-data
      - name: sonar-extensions
        persistentVolumeClaim:
          claimName: sonar-extensions
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: sonarqube
  name: sonarqube
spec:
  ports:
    - name: sonar
      port: 80
      protocol: TCP
      targetPort: 9000
  selector:
    app: sonarqube
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    certmanager.k8s.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
  name: sq
spec:
  rules:
  - host: <sonarqube-host>
    http:
      paths:
      - backend:
          serviceName: sonarqube
          servicePort: 80
  tls:
  - hosts:
    -  <sonarqube-host>
    secretName: sonar
status:
  loadBalancer:
    ingress:
    - {}

包含如下资源:

  1. 两个 PVC 分别用于存放 SonarQube 自身的数据以及插件的数据
  2. Service/Ingress: 这部分类似于 Gitlab 自身的配置

需要注意的是,因为 SonarQube 自身需要 DB,而我们在 Gitlab 安装的时候已经部署了 DB,所以可以继续复用

以上资源最好也都单独放在一个 NS 下面,比如

kubectl apply -f sq.yaml -n sq

结果如图所示:

DB 配置#

在 Gitlab 的 DB 中创建 sonar DB

CREATE DATABASE sonar WITH OWNER gitlab;

数据库的名字需要与上面 SonarQube 的 Deployment YAML 中的 JDBC 地址一致。

安装插件#

需要安装一下几个插件

  • bitbucket oauth: 用于支持 Bitbucket 登录
  • git: git 支持
  • go: Golang 语言的分析支持
  • gitlab: 与 Gitlab 集成

我们可以通过 SonarQube 的页面安装,也可以直接用 wget 下载到相应的目录中。如下的两个 Gitlab 插件就只能通过下载安装:

# kubectl exec to sonarqube pod
wget https://github.com/gabrie-allaigre/sonar-auth-gitlab-plugin/releases/download/1.0.0/sonar-auth-gitlab-plugin-1.0.0.jar
wget https://github.com/gabrie-allaigre/sonar-gitlab-plugin/releases/download/4.0.0/sonar-gitlab-plugin-4.0.0.jar

设置 Bitbucket 登录#

之前已经配置过 Bitbucket 的 OAuth 信息,这里可以复用。需要在 SonarQube 配置好以下参数:

sonar.core.serverBaseURL 

集成#

Bitbucket#

通过 OAuth 的方式与 Bitbucket 集成,首先需要在 Bitbucket 的页面配置相关的 id/secret 等,然后更新 gitlab-gitlab 的 Deployment:

gitlab_rails['omniauth_providers'] = [{"name" => "bitbucket","app_id" => "<id>", "app_secret" => "<token>","url" => "https://bitbucket.org/"}]

如图所示,Gitlab 将大部分配置存储于其 Deployment 的一个 ENV 中,各种自定义配置都可以通过修改这个环境变量实现。

SonarQube#

用户配置#

我们需要在 Gitlab 添加一个 SonarQube 的用户,并赋予相应的权限,这样他可以在 MR 上添加分析的结果。
同时也要在 SonarQube 上添加一个 Gitlab 用户,用于上报分析结果给 SonarQube。

项目配置#

在实际的项目中,我们需要有一个配置文件,主要用来指明一些具体的分析规则,如下是一个示例

sonar.projectKey=<project_name>
sonar.projectName=<project_name>
sonar.projectVersion=0.1
sonar.login=<username>
sonar.password=<token>
sonar.scm.provider=git
# GoLint report path, default value is report.xml
# TODO: add back
# sonar.golint.reportPath=report.xml
# Cobertura like coverage report path, default value is coverage.xml
sonar.typescript.lcov.reportPaths=lcov.info
# if you want disabled the DTD verification for a proxy problem for example, true by default
sonar.coverage.dtdVerification=false
# JUnit like test report, default value is test.xml
sonar.test.reportPath=test-reports/junit.xml
sonar.sources=.
sonar.test=.
sonar.test.inclusions=./**/*_test.go
sonar.test.exclusions=./vendor/**
sonar.go.coverage.reportPaths=coverage.out
# Ignore duplicating string literal issue
sonar.issue.ignore.multicriteria=e1
sonar.issue.ignore.multicriteria.e1.ruleKey=go:S1192

需要注意的地方:

  1. project_name: 这个需要在 SonarQube 的页面预先创建好对应的项目
  2. username/token: 配置的用于访问 SonarQube 的用户和 Token

CI 配置#

在 CI 中,我们可以专门增加一个 Stage 用于代码分析:

sonarqube:
  only:
    - merge_requests
  stage: analysis
  image: ciricihq/gitlab-sonar-scanner
  dependencies:
    - unit-test
  variables:
    SONAR_URL: <url>
    SONAR_ANALYSIS_MODE: preview
    SONAR_GITLAB_PROJECT_ID: <project_id>
  script:
    - gitlab-sonar-scanner

sonarqube-reports:
  only:
    - master
  stage: analysis
  image: ciricihq/gitlab-sonar-scanner
  variables:
    SONAR_URL:  <url>
    SONAR_ANALYSIS_MODE: publish
  script:
    - gitlab-sonar-scanner

其中url指 SonarQube 的访问地址, project_id指 Gitlab 项目的 ID,一般用 <group>/<project>即可。
上面的两个 Job 作用不同,sonarqube用于分析并且添加 Comment, sonarqube-reports用于分析并且上报结果到 SonarQube。

最终效果如下:

如果有错误,Comment 中也会详细列出来并可以跳转到具体的 SonarQube 页面查看。

邮箱#

Gitlab 发送邮箱的部分也是需要自己配置,和上面对 Bitbucket 的 OAuth 配置一样,直接 edit 其 Deployment 即可。示例配置如下:

gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.office365.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "<email>"
gitlab_rails['smtp_password'] = "<password>"
gitlab_rails['smtp_domain'] = "<domain>"
gitlab_rails['gitlab_email_from'] = '<email>'
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_openssl_verify_mode'] = 'peer'

JIRA#

Gitlab 目前与 Jira 的关联还比较弱,我们可以在 Gitlab 的 Service Templates 里配置好具体的 JIRA 地址,然后在 Commit Message 里关联上相关的 JIRA ID。这里也需要在 JIRA 上创建相应的 Gitlab 用户,这样我们提交的 MR 与 JIRA 的关联就可以在 JIRA 的页面自动展示出来。

当然另一方面,我们也可以利用 Webhook + JIRA SDK,来实现一个 WebHook,通过在 Commit Message 里增加相应的动作来驱动 JIRA 状态的变更,这个可以根据具体的业务情况用 Python 脚本很容地实现。

Danger#

请见本站关于 Danger 与 Gitlab 的集成相关的文章

TODO: 增加 Link

使用#

LGTM#

Gitlab CE 因为砍掉了很多 EE 版本的功能,没有像 GITHUB 那样的通过 /lgtm或者LGTM这样的关键词来自动合并 MR。这个也可以通过部署一个简单的 Server 来作为 Webhook 实现。再通过增加一个MergeBot用户来执行合并的动作,能够极大地简化我们的代码合并流程。

CI/CD#

敏感信息#

CI/CD 中要使用很多 password/token 等敏感信息,直接放在 CI 文件中并且保存在 Git 仓库中是非常不安全的,Gitlab 的 CI 提供了 Pipeline 的环境配置信息,将敏感信息存储在 Gitlab,可以在 ci 文件中直接引用:

Badges#

Badges 的配置是在项目上的,不用在项目的 Readme 等文件中插入什么信息。这点是比较好的。目前 CE 版本支持 Build 状态以及单元测试覆盖率等,一般情况下是够用的。也可以增加第三方的 Badge 地址.

CLI 直接创建 MR#

Gitlab CE 的比较新的版本里已经支持从命令行 Push 的时候直接创建 Merge Request 了,这也是一个非常好用的功能。最简单的方式就是在 Push 后面增加参数:

git push origin <branch> -o merge_request.create

其他的参数可参考官方文档使用。

升级#

因为 Gitlab 的功能更新比较频繁,我们可以经常性地更新 Gitlab 的版本,也是通过直接 edit deployment,更新镜像版本字段即可。当然因为是单实例的服务,会有几分钟的服务中断,影响也不大。需要注意的是 runner 的版本也需要同步更新。

Links#

  1. GitLab CI/CD Pipeline Configuration Reference
  2. Python Jira Library
  3. Gitlab LGTM Bot

  转载请注明: Hang 在 Kubernetes 中安装 Gitlab

  目录