はじめに
Gitbucketはdockerで稼動させる事についてはドキュメントが充実していて、Kubernetesでの稼動についてはissuesが上がっていたり、簡単に動かすことができるかよく分かりませんでした。
付属のH2 databaseを利用する分には問題なさそうでしたが、外部のdatabaseと連携させようとすると、設定ファイルを外部から与えるための工夫が必要になりました。
初期設定は極力自動化する方針で環境を構築したので、その際のメモを残しておきます。
参考文献
- qiita.com::GitBucketをMySQL8で立ち上げるdocker-composeファイル
- (MySQL8.0 認証方式を変更する(Laravel5)
- hub.docker.com::docker-gitbucket
- www.xmisao.com::nginxのproxy_passの注意点
環境
- Kubernetes v1.16.9 (kubespray v2.12.6)
- Nginx (CentOS7 nginx-1.16.1-1.el7) - ReverseProxy Server
- Gitbucket - docker.io/gitbucket/gitbucket:4.29.0
- MySQL - docker.io/mysql:5.6
+----------+ +-------+ +--------------+ +------+
| Intranet |-- https --| nginx |-- http --| LoadBalancer |----| Pods |
+----------+ +-------+ +--------------+ +------+
https://xxxx/gitbucket/ http://external-ip/
直接K8sのLoadBalancerに接続せず、外部からは正式な証明書によるTLS接続を行ない、内部はnon-TLS接続でURL(context-root)も変更しています。
Setup
あらかじめ次のようなserviceを定義しておきます。
---
apiVersion: v1
kind: Service
metadata:
name: bucket
spec:
type: LoadBalancer
loadBalancerIP: "192.168.100.196"
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
- port: 29418
protocol: TCP
name: ssh
selector:
app: bucket
tier: webapp
---
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: bucket
spec:
ports:
- port: 3306
selector:
app: bucket
tier: mysql
clusterIP: None
Mysql
あらかじめUser: rootのパスワードや、接続に利用するパスワードを設定しておきます。
コマンドはMakefileに設定しています。
NS = gitbucket
.PHONY: setup-secret gen-secret
setup-secret:
echo -n `openssl rand -hex 16` > mysql-password
echo -n `openssl rand -hex 16` > bucket-password
gen-secret:
kubectl -n $(NS) create secret generic mysql-pass --from-file=mysql-password
kubectl -n $(NS) create secret generic bucket-pass --from-file=bucket-password
show-secret:
kubectl -n $(NS) get secret mysql-pass -o yaml| grep mysql-password | cut -d: -f2 | sed -e "s/ //g" | base64 -d
@echo ""
kubectl -n $(NS) get secret bucket-pass -o yaml| grep bucket-password | cut -d: -f2 | sed -e "s/ //g" | base64 -d
@echo ""
手動で$ echo -n $(openssl rand -hex 16) > mysql-password
のように実行してもよいのですが、Makefileにタスクとして含めました。
$ make setup-secret
$ make gen-secret
$ rm mysql-password bucket-password
ファイルを消した後で設定したパスワードを確認するには、稼動しているPodに入って環境変数を(env,setコマンドなどで)確認する方法もありますが、secretの内容をデコードするタスクをMakefileに設定しています。
MySQLの設定はConfigMapとDeploymentのYAMLファイルで行ないます。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: gitbucket-database-conf
data:
database.conf: |
db {
url = "GITBUCKET_DB_URL"
user = "GITBUCKET_DB_USER"
password = "GITBUCKET_DB_PASSWORD"
}
mysql.cnf: |
[mysqld]
default-authentication-plugin = mysql_native_password
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: gitbucket
labels:
app: bucket
spec:
selector:
matchLabels:
app: bucket
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: bucket
tier: mysql
spec:
containers:
- image: mysql:8.0.26
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: mysql-password
- name: MYSQL_DATABASE
value: gitbucket
- name: MYSQL_USER
value: bucketuser
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: bucket-pass
key: bucket-password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
- name: database-auth-conf
mountPath: /etc/mysql/conf.d/local.cnf
subPath: mysql.cnf
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pvc
- name: database-auth-conf
configMap:
name: gitbucket-database-conf
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: gitbucket
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: rook-ceph-block
resources:
requests:
storage: 20Gi
この結果、User: bucketuserに自動的に設定される権限設定は次のようになっています。
mysql> show grants;
+-----------------------------------------------------------+
| Grants for bucketuser@% |
+-----------------------------------------------------------+
| GRANT USAGE ON *.* TO `bucketuser`@`%` |
| GRANT ALL PRIVILEGES ON `gitbucket`.* TO `bucketuser`@`%` |
+-----------------------------------------------------------+
2 rows in set (0.00 sec)
SSHアクセスを許可するためのGatewayへの設定
Kubernetesのサーバーは、クライアントから直接接続できない場所に配置しています。
そのため、NginxのReverse Proxyサーバーが稼動しているホストに、SSH用ポート (29418) でのアクセスを許可するための設定を加えておきます。
- name: firewalld enable services for external
firewalld:
zone: internal
masquerade: yes
permanent: true
state: enabled
- name: firewalld external config for the harbor port forwarding
firewalld:
zone: external
rich_rule: rule family=ipv4 forward-port port=29418 protocol=tcp to-port=29418 to-addr=192.168.100.78
permanent: yes
immediate: yes
state: enabled
フロントエンドのNginxはCentOS上で稼動しているので、同様の操作をしようとすると、firewall-cmd
を通して設定を加えることになります。確認していませんが、次のような設定になると思われます。
$ sudo firewall-cmd --zone=external --add-masquerade --permanent
$ sudo firewall-cmd --zone=external --add-rich-rule="rule family=ipv4 forward-port port=29418 protocol=tcp to-port=29418 to-addr=192.168.1.20" --permanent
Gitbucket
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: gitbucket
name: bucket
labels:
app: bucket
spec:
replicas: 1
selector:
matchLabels:
app: bucket
tier: webapp
strategy:
type: Recreate
template:
metadata:
labels:
app: bucket
tier: webapp
spec:
initContainers:
- name: init-database-conf
image: busybox:1.31.1
command: ['sh', '-c', 'sed -e s:GITBUCKET_DB_USER:$GITBUCKET_DB_USER: -e s:GITBUCKET_DB_PASSWORD:$GITBUCKET_DB_PASSWORD: -e s,GITBUCKET_DB_URL,$GITBUCKET_DB_URL, /config/database.conf > /gitbucket/database.conf']
volumeMounts:
- name: git-data
mountPath: /gitbucket
- name: database-conf
mountPath: /config
env:
- name: GITBUCKET_DB_URL
value: "jdbc:mysql://mysql/gitbucket?useUnicode=true\\&characterEncoding=utf8"
- name: GITBUCKET_DB_USER
value: bucketuser
- name: GITBUCKET_DB_PASSWORD
valueFrom:
secretKeyRef:
name: bucket-pass
key: bucket-password
- name: wait-database
image: busybox:1.31.1
command: ['sh', '-c', 'until (echo |telnet mysql 3306) ; do echo "waiting mysql..."; sleep 4; done;']
containers:
- name: gitbucket
image: gitbucket/gitbucket:4.38.4
startupProbe:
exec:
command:
- curl
- http://localhost:8080/gitbucket/
failureThreshold: 300
periodSeconds: 10
startupProbe:
httpGet:
path: /
port: 8080
failureThreshold: 60
periodSeconds: 5
livenessProbe:
httpGet:
path: /
port: 8080
failureThreshold: 1
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 7
periodSeconds: 7
ports:
- containerPort: 8080
name: bucket
volumeMounts:
- name: git-data
mountPath: /gitbucket
- name: database-conf
mountPath: /config
env:
- name: GITBUCKET_HOME
value: /gitbucket
- name: GITBUCKET_BASE_URL
value: https://example.com/gitbucket
volumes:
- name: git-data
persistentVolumeClaim:
claimName: git-data-pvc
- name: database-conf
configMap:
name: gitbucket-database-conf
items:
- key: database.conf
path: database.conf
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: git-data-pvc
namespace: gitbucket
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: rook-ceph-block
resources:
requests:
storage: 20Gi
遭遇したエラー
Gitbucketで環境変数に設定したデータベースにアクセスしない
環境変数でdatabase.confの設定を上書きできるとあったので外部のMySQLに接続するよう構成を変更したが、うまく動作しないため、起動時に/gitbucket/database.confを上書きするよう変更しました。
13:58:23.798 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
13:58:24.844 [main] ERROR com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Exception during pool initialization.
java.sql.SQLNonTransientConnectionException: Could not connect to address=(host=mysql)(port=3306)(type=master) : Connection refused (Connection refused)
このためinitContainersでbusyboxのsedを利用しています。
MySQLが起動してもネットワークアクセスを受け付けない
Service定義が間違っているのかと見直しましたが、Kubernetes公式チュートリアルを参考にlivenessProbe(mysqladmin ping)を追加したところ定期的に失敗する様子が観察されました。
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 3m5s default-scheduler persistentvolumeclaim "mysql-pvc" not found
Warning FailedScheduling 3m3s (x3 over 3m4s) default-scheduler pod has unbound immediate PersistentVolumeClaims (repeated 4 times)
Normal Scheduled 3m default-scheduler Successfully assigned gitbucket/mysql-59d8c88f55-2b6xp to u109ls04
Warning FailedMount 2m59s kubelet, u109ls04 Unable to attach or mount volumes: unmounted volumes=[mysql-persistent-storage], unattached volumes=[mysql-persistent-storage database-auth-conf default-token-txprc]: error processing PVC gitbucket/mysql-pvc: PVC is not bound
Warning Unhealthy 105s (x3 over 2m4s) kubelet, u109ls04 Liveness probe failed: mysqladmin: connect to server at 'localhost' failed
error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)'
Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!
Normal Killing 105s kubelet, u109ls04 Container mysql failed liveness probe, will be restarted
Normal Created 29s (x4 over 2m36s) kubelet, u109ls04 Created container mysql
Normal Started 28s (x4 over 2m35s) kubelet, u109ls04 Started container mysql
初期化中にlivenessProbeが起動してしまい、初期化途中でプロセスが再起動したため、その後に正常に稼動を続けることができなくなったことが原因でした。livenessProbeを外すと、私の環境では初期化に4分以上かかっていました。
対応としては、当初、livenessProbeのinitialDelaySeconds値を300に設定しましたが、Gitbucketでも同様の問題が発生したので、v1.18以降ではstartupProbeを設定することを予定しています。
現状での副作用として、readinessProbeも同様に動き出す時間が遅くなっているため、initialDelaySecondsに設定した時間が経過した後でないと、GitbucketのinitContainersで設定している3306番ポートチェックが通らないため、Gitbucketの起動処理も遅くなります。最終的にはGitbucketが正常に稼動した後も、1800秒が経過するまでreadinessProbeが実行されないため、80番ポートはGitbucketのPodに全体の起動時間が大きく遅くなっています。
Timestamp関連の警告と初期化の失敗
2020-05-30 01:56:29+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.30-1debian10 started.
2020-05-30 01:56:29+00:00 [Note] [Entrypoint]: Initializing database files
2020-05-30T01:56:29.420558Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2020-05-30T01:56:29.422063Z 0 [ERROR] --initialize specified but the data directory has files in it. Aborting.
2020-05-30T01:56:29.422084Z 0 [ERROR] Aborting
類似の問題はStackoverflowの投稿で報告されています。
これは一時的にmysql:5.7を利用したために発生した問題だったので、mysql:8.0.20に戻して再発しないようにしました。(”--ignore-db-dir=lost+found"を指定するためのオプションはmysql8では削除されています)
Gitbucketがサービスを開始しない
最初の起動時にGitbucketはデータベース上にテーブルを作成するなどの初期化を行ないますが、手元の環境ではサービスを開始するまでに、少し時間がかかる状況がありました。
07:57:40.070 [main] INFO g.core.servlet.InitializeListener - Check version
07:57:40.070 [main] INFO g.core.servlet.InitializeListener - Start schema update
...
08:09:47.584 [main] INFO g.core.servlet.InitializeListener - Extract bundled plugins...
08:09:47.592 [main] INFO g.core.servlet.InitializeListener - Extract to /gitbucket/plugins/gitbucket-notifications-plugin-1.8.0.jar
...
2020-05-31 08:12:04.421:INFO:oejs.Server:main: Started @867127ms
起動してからWebサーバーが起動するまで、およそ15分が経過しています。この間にGitbucketのPodを再起動した場合、不整合が発生し、Deployment定義を削除して、手動で全テーブルを削除した後、再度Deploymentを読み込み初期化作業を行なう以外には対応できなくなってしまいました。
こういった現象への対応は公式ガイドに記述があります。
残念ながらv.1.18以降でないと利用できないので、現状ではinitialDelaySeconds値を相当に長い時間(1800秒)に設定しています。
v1.18以降ではstartupProbeを設定したいと思います。