kubernetes 部署禅道
前言
禅道作为常用的项目管理工具,在很多公司都有使用。其官方文档中介绍的部署方式主要是安装LAMP集成环境的方式来部署的,虽然其官方也提供了Docker部署,但是他提供的Docker镜像(easysoft/zentao)是集成了httpd+php+mysql在里面的,不满足容器的最佳实践原则。
在容器时代,禅道所推崇的部署方式显然显得比较的落后了,其官方更没有关于部署到Kubernetes的相关文档。社区论坛是有一些提问的,但是提供的方案大多都是使用 easysoft/zentao 这个仓的镜像。
本来是打算自己制作镜像的,在翻阅资料的时候发现 easysoft/quickon-zentao 这个仓,看了一下是满足我的需求的。
部署架构
所有服务都跑在 Kubernetes 集群里,MySQL 做了主从复制,用 local-path 作为存储提供者。禅道附件则用 nfs 作为共享存储。
部署清单
mysql
mysql用5.7的版本,禅道没做好8.0的适配工作。
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:
- name: mysql
port: 3306
clusterIP: None
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
data:
common.cnf: |
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
lower-case-table-names = 1
ignore-db-dir = lost+found
# SAFETY #
max-allowed-packet = 16M
max-connect-errors = 1000000
skip-name-resolve
# BINARY LOGGING #
binlog-format = ROW
replicate-wild-ignore-table = mysql.%
max-connections = 600
# INNODB #
innodb-flush-method = O_DIRECT
innodb-log-files-in-group = 2
innodb-log-file-size = 256M
innodb-flush-log-at-trx-commit = 1
innodb-file-per-table = 1
innodb-data-file-path = ibdata1:128M:autoextend
innodb-undo-tablespaces = 4
innodb-buffer-pool-size = 2G
master.cnf: |
[mysqld]
log-bin
slave.cnf: |
[mysqld]
super-read-only
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
serviceName: mysql
replicas: 2
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: "local-path"
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:5.7.40
imagePullPolicy: IfNotPresent
command:
- bash
- "-c"
- |
set -xe
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
echo server-id=$((100+$ordinal)) >> /mnt/conf.d/server-id.cnf
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/master.cnf /mnt/config-map/common.cnf /mnt/conf.d/
else
cp /mnt/config-map/slave.cnf /mnt/config-map/common.cnf /mnt/conf.d/
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
- name: clone-mysql
image: haxicc/xtrabackup:2.4.26
imagePullPolicy: IfNotPresent
command:
- bash
- "-c"
- |
set -xe
[[ -d /var/lib/mysql/mysql ]] && exit 0
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
[[ $ordinal -eq 0 ]] && exit 0
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
xtrabackup --prepare --target-dir=/var/lib/mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
containers:
- name: mysql
image: mysql:5.7.40
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
- name: LANG
value: C.UTF-8
- name: TZ
value: Asia/Shanghai
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
- name: create-user
mountPath: /docker-entrypoint-initdb.d/create-user.sql
subPath: create-user.sql
resources:
requests:
cpu: 500m
memory: 2Gi
livenessProbe:
exec:
command: ['mysqladmin', 'ping']
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 10
readinessProbe:
exec:
command: ['mysql', '-h', '127.0.0.1', '-e', 'SELECT 1']
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
- name: xtrabackup
image: haxicc/xtrabackup:2.4.26
imagePullPolicy: IfNotPresent
ports:
- name: xtrabackup
containerPort: 3307
command:
- bash
- "-c"
- |
set -xe
cd /var/lib/mysql
if [[ -f xtrabackup_slave_info ]] && [[ -s xtrabackup_slave_info ]]; then
mv xtrabackup_slave_info change_master_to_sql.in
rm -f xtrabackup_binlog_info
elif [[ -f xtrabackup_binlog_info ]]; then
[[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
rm xtrabackup_binlog_info
echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to_sql.in
fi
if [[ -f change_master_to_sql.in ]]; then
echo "Waiting for mysqld to be ready (accepting connections)"
until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
echo "Initializing replication from clone position"
mv change_master_to_sql.in change_master_to.sql.orig
sed -i 's/;$//' change_master_to.sql.orig
mysql -h 127.0.0.1 << EOF
STOP SLAVE;
RESET SLAVE;
$(<change_master_to.sql.orig),
MASTER_HOST='mysql-0.mysql',
MASTER_USER='repluser',
MASTER_PASSWORD='replpassword',
GET_MASTER_PUBLIC_KEY=1;
START SLAVE;
EOF
fi
exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
"xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 100m
memory: 100Mi
volumes:
- name: conf
emptyDir: {}
- name: config-map
configMap:
name: mysql
- name: create-user
configMap:
name: create-user
---
apiVersion: v1
kind: ConfigMap
metadata:
name: create-user
data:
create-user.sql: |
DROP USER 'root'@'%';
CREATE USER 'root'@'127.0.0.1';
GRANT ALL ON *.* TO 'root'@'127.0.0.1';
CREATE USER 'zentao'@'%' IDENTIFIED BY 'zentao123';
GRANT ALL ON *.* TO 'zentao'@'%';
CREATE USER 'repluser'@'%' IDENTIFIED BY 'replpassword';
GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'%';
redis
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: zentao
spec:
serviceName: redis
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6.2.7
resources:
limits:
cpu: 1
memory: 100Mi
requests:
cpu: 100m
memory: 10Mi
ports:
- name: server
containerPort: 6379
env:
- name: LANG
value: C.UTF-8
- name: TZ
value: Asia/Shanghai
---
kind: Service
apiVersion: v1
metadata:
name: redis
namespace: zentao
labels:
app: redis
spec:
selector:
app: redis
type: ClusterIP
ports:
- port: 6379
name: server
存储卷
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-zentao-data
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 200Gi
nfs:
server: 10.23.201.21
readOnly: false
path: /data/nfs/zentao
volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: zentao-data
namespace: zentao
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Gi
volumeMode: Filesystem
volumeName: pv-zentao-data
storageClassName: ""
禅道
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: zentao
name: zentao
namespace: zentao
spec:
replicas: 3
selector:
matchLabels:
app: zentao
template:
metadata:
labels:
app: zentao
spec:
containers:
- name: zentao
image: easysoft/quickon-zentao:biz7.7.k8s-20221028
volumeMounts:
- name: data
mountPath: /data
command:
- sh
- -c
- |
sed -i 's#/etc/s6/s6-init/run#bash -x /etc/s6/s6-init/run#' /usr/bin/entrypoint.sh
bash -x /usr/bin/entrypoint.sh
env:
- name: LANG
value: C.UTF-8
- name: TZ
value: Asia/Shanghai
- name: MYSQL_HOST
value: mysql-0.mysql
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_USER
value: zentao
- name: MYSQL_PASSWORD
value: "123Z3nt@0"
- name: PHP_SESSION_TYPE
value: redis
- name: PHP_SESSION_PATH
value: tcp://redis:6379
#value: tcp://redis:6379?auth=123456
volumes:
- name: data
persistentVolumeClaim:
claimName: zentao-data
readOnly: false
---
apiVersion: v1
kind: Service
metadata:
labels:
app: zentao
name: zentao
namespace: zentao
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: zentao
sessionAffinity: None
type: ClusterIP
---
mysql8.0跑问题也不大,测试过。