现在很少写这种安装类的博客了。之前在公司部署了一个 Gitlab 作为日常使用,因为步骤比较繁琐。在此把零零散散的资料汇聚一下,记录一个比较完整的安装过程
环境
- Kubernetes 1.13.1 三个高可用节点
- 每个节点上一个空余磁盘
步骤
安装 Rook
Rook 提供了基于 Ceph 的分布式存储,我们利用每个节点上的空余磁盘来支撑 Kubernetes 里的 PV/StorageClass 等
Helm 安装
首先,初始化磁盘
1
2
3
4
|
mkfs.ext4 /dev/vdb
mount /dev/vdb /var/lib/rook
mkdir /var/lib/rook
# TODO: add to /etc/fstab
|
然后通过 Helm 安装 Rook
1
2
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
#################################################################################
# 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:
- ns/rook-ceph
- sa/rook-ceph/rook-ceph-mgr
- role/rook-ceph/rook-ceph-osd
- role/rook-ceph/rook-ceph-mgr-system
- role/rook-ceph/rook-ceph-mgr
- rolebinding/rook-ceph/rook-ceph-cluster-mgmt
- rolebinding/rook-ceph/rook-ceph-osd
- rolebinding/rook-ceph/rook-ceph-mgr-system
- rolebinding/rook-ceph/rook-ceph-mgr-cluster
- cephecluster/rook-ceph/rook-ceph
Helm Rook 里已经定义好了相关的 CRD,我们再这里就是要创建一个 CephCluster 的资源,可以看到它的 YAML 里指定了之前我们初始化好的/var/lib/rook
目录。
1
|
kubectl create -f cluster.yaml
|
创建 StorageClass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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 安装
1
2
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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 自定义的部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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
|
说明:
- baseDomain/baseIP/legoEmail: 这几个需要根据具体情况替换
- *Size: 这些变量的值就是我们安装的 Gitlab 所需要使用的空间,当然都是越大越好
Helm 安装:
1
|
helm install --name gitlab -f values.yaml gitlab/gitlab-omnibus --namespace=gitlab
|
修改 Ingress
之前已经安装了 Cert Manger,我们可以通过给 Gitlab 的 Ingress 添加如下的 Annotation 来支持 HTTPS
1
2
|
# 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: 监控信息
权限相关
1
2
3
4
5
6
7
8
9
10
11
12
|
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
1
2
3
4
5
6
7
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
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:
- {}
|
包含如下资源:
- 两个 PVC 分别用于存放 SonarQube 自身的数据以及插件的数据
- Service/Ingress: 这部分类似于 Gitlab 自身的配置
需要注意的是,因为 SonarQube 自身需要 DB,而我们在 Gitlab 安装的时候已经部署了 DB,所以可以继续复用
以上资源最好也都单独放在一个 NS 下面,比如
1
|
kubectl apply -f sq.yaml -n sq
|
结果如图所示:

DB 配置
在 Gitlab 的 DB 中创建 sonar DB
1
|
CREATE DATABASE sonar WITH OWNER gitlab;
|
数据库的名字需要与上面 SonarQube 的 Deployment YAML 中的 JDBC 地址一致。
安装插件
需要安装一下几个插件
- bitbucket oauth: 用于支持 Bitbucket 登录
- git: git 支持
- go: Golang 语言的分析支持
- gitlab: 与 Gitlab 集成
我们可以通过 SonarQube 的页面安装,也可以直接用 wget 下载到相应的目录中。如下的两个 Gitlab 插件就只能通过下载安装:
1
2
3
|
# 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 配置好以下参数:
1
|
sonar.core.serverBaseURL
|
集成
Bitbucket
通过 OAuth 的方式与 Bitbucket 集成,首先需要在 Bitbucket 的页面配置相关的 id/secret 等,然后更新 gitlab-gitlab 的 Deployment:
1
|
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。
项目配置
在实际的项目中,我们需要有一个配置文件,主要用来指明一些具体的分析规则,如下是一个示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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
|
需要注意的地方:
- project_name: 这个需要在 SonarQube 的页面预先创建好对应的项目
- username/token: 配置的用于访问 SonarQube 的用户和 Token
CI 配置
在 CI 中,我们可以专门增加一个 Stage 用于代码分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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 即可。示例配置如下:
1
2
3
4
5
6
7
8
9
10
11
|
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 后面增加参数:
1
|
git push origin <branch> -o merge_request.create
|
其他的参数可参考官方文档使用。
升级
因为 Gitlab 的功能更新比较频繁,我们可以经常性地更新 Gitlab 的版本,也是通过直接 edit deployment,更新镜像版本字段即可。当然因为是单实例的服务,会有几分钟的服务中断,影响也不大。需要注意的是 runner 的版本也需要同步更新。

Links
- GitLab CI/CD Pipeline Configuration Reference
- Python Jira Library
- Gitlab LGTM Bot