此文档为 bitnami/mysql 配置文件解读,详细 values 等信息见[官方文档](https://artifacthub.io/packages/helm/bitnami/mysql)。
bitnami MySQL 集群中的容器全部均采用 https://hub.docker.com/r/bitnami/mysql,容器中包含初始化脚本,后文会提及,
镜像源码(包含所有脚本)可见 https://github.com/bitnami/containers/tree/main/bitnami/mysql/,
chart 源码可见 https://github.com/bitnami/charts/tree/main/bitnami/mysql。
# 简单部署
```bash
helm repo add bitnami https://charts.bitnami.com/bitnami
```
```bash
helm install my-mysql ./mysql \
--set global.strorageClass=$YOUR_STORAGECLASS \
--set architecture=replication \
--set secondary.replicaCount=$REPILICA_NUM
```
- `global.strorageClass`:设置存储类,如果不设置默认采用 default StorageClass,如果没有默认 SC 创建的 PVC 将处于 `Pending` 状态。
- `architecture`:设置 MySQL 架构,有 `standalone` 单主机和 `replication` 集群两个选项,默认为单主机。
- `secondary.replicaCount`:设置集群从节点个数,即 Slave 个数。
也可以通过 `values.yaml` 进行高级配置。
> 如遇网络问题可通过 pull 拉取,再上传到所需主机。
>
> ```bash
> helm pull oci://registry-1.docker.io/bitnamicharts/mysql --version 12.3.4
>
> tar -zxvf mysql mysql-12.3.4.tgz && cd mysql/
>
> helm install my-mysql mysql/
> ```
# 获取资源清单
由上文设置的 values 获取 helm chart 配置清单。
```bash
$ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-mysql default 1 2025-04-25 19:51:56.787669028 +0800 CST deployed mysql-12.3.4 8.4.5
```
```bash
helm get manifest my-mysql > my-mysql.yaml
```
# 资源清单解释
资源清单中包含 NetWorkPolicy、PodDisruptionBudget、ServiceAccount、Secret、ConfigMap(primary+secondary)、Service(primary+secondary cluster+headless)、StatefulSet(primary+secondary)。
## StatefulSet
primary 和 secondary 除在标签命名上的区别,在[环境变量声明]()上还有区别。
>[!TIP]
>
>若不在 values.yaml 中设置,部署时 `persistentVolumeClaimRetentionPolicy` 字段中的两个子字段 `whenDeleted`、`whenScaled` 均会默认被设置成 `Retain`,删除 release 时需要手动删除 PVC 和 PV。
```yaml
# Source: mysql/templates/primary/statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-mysql-primary
namespace: "default"
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
app.kubernetes.io/component: primary
spec:
replicas: 1
# pod 创建、删除和扩展顺序,默认 OrderedReady
podManagementPolicy: ""
selector:
matchLabels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/name: mysql
app.kubernetes.io/part-of: mysql
app.kubernetes.io/component: primary
serviceName: my-mysql-primary-headless
updateStrategy:
type: RollingUpdate
template:
metadata:
annotations:
checksum/configuration: a581348f7af561e486fad8d76b185ef64f865137e8229ff5d0fa6cdf95694ea1
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
app.kubernetes.io/component: primary
spec:
serviceAccountName: my-mysql
automountServiceAccountToken: false
affinity:
podAffinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/name: mysql
topologyKey: kubernetes.io/hostname
weight: 1
nodeAffinity:
securityContext:
fsGroup: 1001
fsGroupChangePolicy: Always
supplementalGroups: []
sysctls: []
initContainers:
- name: preserve-logs-symlinks
image: docker.io/bitnami/mysql:8.4.5-debian-12-r0
imagePullPolicy: "IfNotPresent"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsGroup: 1001
runAsNonRoot: true
runAsUser: 1001
seLinuxOptions: {}
seccompProfile:
type: RuntimeDefault
resources:
limits:
cpu: 750m
ephemeral-storage: 2Gi
memory: 768Mi
requests:
cpu: 500m
ephemeral-storage: 50Mi
memory: 512Mi
command:
- /bin/bash
args:
- -ec
- |
#!/bin/bash
. /opt/bitnami/scripts/libfs.sh
# We copy the logs folder because it has symlinks to stdout and stderr
if ! is_dir_empty /opt/bitnami/mysql/logs; then
cp -r /opt/bitnami/mysql/logs /emptydir/app-logs-dir
fi
volumeMounts:
- name: empty-dir
mountPath: /emptydir
containers:
- name: mysql
image: docker.io/bitnami/mysql:8.4.5-debian-12-r0
imagePullPolicy: "IfNotPresent"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsGroup: 1001
runAsNonRoot: true
runAsUser: 1001
seLinuxOptions: {}
seccompProfile:
type: RuntimeDefault
env:
- name: BITNAMI_DEBUG
value: "false"
- name: MYSQL_ROOT_PASSWORD_FILE
value: /opt/bitnami/mysql/secrets/mysql-root-password
- name: MYSQL_ENABLE_SSL
value: "no"
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_DATABASE
value: "my_database"
- name: MYSQL_REPLICATION_MODE
value: "master"
- name: MYSQL_REPLICATION_USER
value: "replicator"
- name: MYSQL_REPLICATION_PASSWORD_FILE
value: /opt/bitnami/mysql/secrets/mysql-replication-password
envFrom:
ports:
- name: mysql
containerPort: 3306
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
exec:
command:
- /bin/bash
- -ec
- |
password_aux="${MYSQL_ROOT_PASSWORD:-}"
if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
fi
mysqladmin status -uroot -p"${password_aux}"
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
exec:
command:
- /bin/bash
- -ec
- |
password_aux="${MYSQL_ROOT_PASSWORD:-}"
if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
fi
mysqladmin ping -uroot -p"${password_aux}" | grep "mysqld is alive"
startupProbe:
failureThreshold: 10
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
exec:
command:
- /bin/bash
- -ec
- |
password_aux="${MYSQL_ROOT_PASSWORD:-}"
if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
fi
mysqladmin ping -uroot -p"${password_aux}" | grep "mysqld is alive"
resources:
limits:
cpu: 750m
ephemeral-storage: 2Gi
memory: 768Mi
requests:
cpu: 500m
ephemeral-storage: 50Mi
memory: 512Mi
volumeMounts:
- name: data
mountPath: /bitnami/mysql
- name: empty-dir
mountPath: /tmp
subPath: tmp-dir
- name: empty-dir
mountPath: /opt/bitnami/mysql/conf
subPath: app-conf-dir
- name: empty-dir
mountPath: /opt/bitnami/mysql/tmp
subPath: app-tmp-dir
- name: empty-dir
mountPath: /opt/bitnami/mysql/logs
subPath: app-logs-dir
- name: config
mountPath: /opt/bitnami/mysql/conf/my.cnf
subPath: my.cnf
- name: mysql-credentials
mountPath: /opt/bitnami/mysql/secrets/
volumes:
- name: config
configMap:
name: my-mysql-primary
- name: mysql-credentials
secret:
secretName: my-mysql
items:
- key: mysql-root-password
path: mysql-root-password
- key: mysql-password
path: mysql-password
- key: mysql-replication-password
path: mysql-replication-password
- name: empty-dir
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: data
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/name: mysql
app.kubernetes.io/component: primary
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: "8Gi"
```
### initContainers
通过 `/opt/bitnami/scripts/libfs.sh` 标准化文件系统管理,内容可见 [libfs.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/prebuildfs/opt/bitnami/scripts/libfs.sh)。
检查 MySQL 日志目录是否为空,如果目录不为空,将整个日志目录(包含符号链接)复制到临时目录,确保日志文件能被挂载到主容器的 `/opt/bitnami/mysql/logs/app-logs-dir` 目录下,便于使用 Kubernetes 的日志收集和管理功能。
### Containers
#### 环境变量
设置环境变量,为之后初始化脚本做准备
**primary**:
```yaml
env:
- name: BITNAMI_DEBUG
value: "false"
- name: MYSQL_ENABLE_SSL
value: "no"
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_DATABASE
value: "my_database"
- name: MYSQL_REPLICATION_MODE
value: "master"
- name: MYSQL_REPLICATION_USER
value: "replicator"
- name: MYSQL_ROOT_PASSWORD_FILE
value: /opt/bitnami/mysql/secrets/mysql-root-password
- name: MYSQL_REPLICATION_PASSWORD_FILE
value: /opt/bitnami/mysql/secrets/mysql-replication-password
```
环境变量解释
```ini
# 调试日志开关:禁用
BITNAMI_DEBUG=false
# 控制SSL加密连接:禁用
MYSQL_ENABLE_SSL=no
# MySQL监听端口
MYSQL_PORT=3306
# 初始化时创建的默认数据库
MYSQL_DATABASE=my_database
# 定义节点角色:主库
MYSQL_REPLICATION_MODE=master
# 复制专用用户名,主库自动创建此用户并授权 REPLICATION SLAVE 权限
MYSQL_REPLICATION_USER=replicator
# 指定root密码路径
MYSQL_ROOT_PASSWOR_FILE=/opt/bitnami/mysql/secrets/mysql-root-password
# 复制用户密码文件路径
MYSQL_REPLICATION_PASSWORD_FILE=/opt/bitnami/mysql/secrets/mysql-replication-password
```
[Secret](#secret) 中指定的 3 个密码,通过 Volumes 分别挂载至 `/opt/bitnami/mysql/secrets/` 目录下,再由 `MYSQL_ROOT_PASSWOR_FILE`、`MYSQL_REPLICATION_PASSWORD_FILE` 环境变量指定密码文件位置,通过 shell 脚本实现密码获取。
**secondary**:
```yaml
env:
- name: BITNAMI_DEBUG
value: "false"
- name: MYSQL_ENABLE_SSL
value: "no"
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_MASTER_PORT_NUMBER
value: "3306"
- name: MYSQL_REPLICATION_MODE
value: "slave"
- name: MYSQL_MASTER_HOST
value: my-mysql-primary
- name: MYSQL_MASTER_ROOT_USER
value: "root"
- name: MYSQL_REPLICATION_USER
value: "replicator"
- name: MYSQL_MASTER_ROOT_PASSWORD_FILE
value: /opt/bitnami/mysql/secrets/mysql-root-password
- name: MYSQL_REPLICATION_PASSWORD_FILE
value: /opt/bitnami/mysql/secrets/mysql-replication-password
```
环境变量解释,上面提到过的略
```ini
# 主库端口,需与主库 MYSQL_PORT 一致
MYSQL_MASTER_PORT_NUMBER=3306
# 定义节点角色:从库,自动配置 server-id 和复制参数
MYSQL_REPLICATION_MODE=slave
# 主库主机名/IP,从库通过此值连接主库
MYSQL_MASTER_HOST=my-mysql-primary
# 主库root用户,用于从库初始化时连接主库(需与主库root用户一致)
MYSQL_MASTER_ROOT_USER=root
# 复制专用用户名,主库需提前创建此用户并授权REPLICATION SLAVE权限
MYSQL_REPLICATION_USER=replicator
```
## Service
Primary 和 Secondary 配置文件中仅有名称的区别,故不赘述。
```yaml
# Source: mysql/templates/primary/svc-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: my-mysql-primary-headless
namespace: "default"
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
app.kubernetes.io/component: primary
spec:
type: ClusterIP
clusterIP: None
publishNotReadyAddresses: true
ports:
- name: mysql
port: 3306
targetPort: mysql
selector:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/name: mysql
app.kubernetes.io/component: primary
---
# Source: mysql/templates/primary/svc.yaml
apiVersion: v1
kind: Service
metadata:
name: my-mysql-primary
namespace: "default"
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
app.kubernetes.io/component: primary
spec:
type: ClusterIP
sessionAffinity: None
ports:
- name: mysql
port: 3306
protocol: TCP
targetPort: mysql
nodePort: null
selector:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/name: mysql
app.kubernetes.io/part-of: mysql
app.kubernetes.io/component: primary
```
## ConfigMap
Primary 和 Secondary 配置文件中仅有名称的区别,故不赘述。
```yaml
# Source: mysql/templates/primary/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-mysql-primary
namespace: "default"
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
app.kubernetes.io/component: primary
data:
my.cnf: |-
[mysqld]
# 控制多因素认证策略,* ,, 表示允许所有认证方式(如密码、插件等)
authentication_policy='* ,,'
# 禁用DNS反向解析,仅通过IP授权访问
skip-name-resolve
# 禁用TIMESTAMP列的自动更新特性,需显式指定默认值
explicit_defaults_for_timestamp
# MySQL安装目录
basedir=/opt/bitnami/mysql
# 插件存放路径
plugin_dir=/opt/bitnami/mysql/lib/plugin
# 监听端口
port=3306
# 禁用 MySQL X Protocol(默认 33060 端口)
mysqlx=0
mysqlx_port=33060
socket=/opt/bitnami/mysql/tmp/mysql.sock
# 数据文件存放目录
datadir=/bitnami/mysql/data
# 临时文件目录
tmpdir=/opt/bitnami/mysql/tmp
# 单此传输最大数据包大小
max_allowed_packet=16M
# 监听所有IP地址
bind-address=*
pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
# 错误日志路径
log-error=/opt/bitnami/mysql/logs/mysqld.log
# 默认字符集
character-set-server=UTF8
# 关闭慢查询日志
slow_query_log=0
# 若启用慢查询,记录超过10秒的查询
long_query_time=10.0
[client]
port=3306
# 本地通信的Unix套接字路径
socket=/opt/bitnami/mysql/tmp/mysql.sock
default-character-set=UTF8
plugin_dir=/opt/bitnami/mysql/lib/plugin
[manager]
port=3306
socket=/opt/bitnami/mysql/tmp/mysql.sock
pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
```
## Secret
```yaml
# Source: mysql/templates/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: my-mysql
namespace: "default"
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
type: Opaque
data:
mysql-root-password: "N0ZveE5ZdXRobg=="
mysql-password: "TVlvWXZYOFZiZQ=="
mysql-replication-password: "NzNkc0NtbmtJMQ=="
```
```bash
kubectl get secret --namespace default my-mysql -o jsonpath="{.data.mysql-root-password}" | base64 -d
```
该命令获取的密码即为 `mysql-root-password` base64 decode 值。
## NetWorkPolicy
```yaml
# Source: mysql/templates/networkpolicy.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: my-mysql
namespace: "default"
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
policyTypes:
- Ingress
- Egress
egress:
- {}
ingress:
# Allow connection from other cluster pods
- ports:
- port: 3306
```
## ServiceAccount
```yaml
# Source: mysql/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-mysql
namespace: "default"
labels:
app.kubernetes.io/instance: my-mysql
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.4.5
helm.sh/chart: mysql-12.3.4
app.kubernetes.io/part-of: mysql
automountServiceAccountToken: false
secrets:
- name: my-mysql
```
# 手动安装示例
常规手动二进制安装 mysql,下文脚本执行的顺序可以参照手动部署 mysql 的顺序。
## 安装 MySQL
```bash
# 准备工作
yum install -y libaio numactl wget
```
```bash
# 下载并解压
wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz
tar -xvf mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz
mv mysql-8.0.28-linux-glibc2.12-x86_64 /usr/local/mysql
```
```bash
# 创建 mysql 用户
groupadd mysql
useradd -r -g mysql -s /bin/false mysql
mkdir -p /usr/local/mysql/data /var/log/mysql
chown -R mysql:mysql /usr/local/mysql /var/log/mysql
```
```bash
# 初始化数据库
/usr/local/mysql/bin/mysqld \
--initialize \
--user=mysql \
--basedir=/usr/local/mysql \
--datadir=/usr/local/mysql/data
```
```bash
# 配置 mysql
[mysqld]
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
```
```bash
# 设置环境变量
echo 'export PATH=/usr/local/mysql/bin:$PATH' >> /etc/profile
source /etc/profile
```
```bash
# Systemd 服务配置
mkdir /etc/systemd/system/mysqld.service
```
```ini
[Unit]
Description=MySQL Server
After=network.target
[Service]
User=mysql
Group=mysql
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
LimitNOFILE=5000
[Install]
WantedBy=multi-user.target
```
```bash
# 启动服务
systemctl daemon-reload
systemctl start mysqld
systemctl enable mysqld
```
## Master 配置
修改 `/etc/my.cnf`:
```ini
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
binlog-do-db = test_db # 需同步的数据库
```
重启MySQL并授权复制用户:
```sql
CREATE USER 'repl'@'%' IDENTIFIED BY 'SecurePass123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
```
记录二进制日志位置:
```sql
SHOW MASTER STATUS;
```
## Slave配置
修改 `/etc/my.cnf`:
```ini
[mysqld]
server-id = 2 # 必须与主服务器不同
relay-log = relay-log
replicate-do-db = test_db
```
配置主从连接:
```sql
CHANGE MASTER TO
MASTER_HOST='192.168.1.100', # 主服务器IP
MASTER_USER='repl',
MASTER_PASSWORD='SecurePass123!',
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=785;
START SLAVE;
```
验证复制状态:
```sql
SHOW SLAVE STATUS\G
```
# 镜像
bitnami MySQL 镜像可见 [Dockerfile](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/Dockerfile)。MySQL 二进制包安装目录为 `/opt/bitnami`。
bitnami 提供的 MySQL 镜像可以直接部署集群无需 kubernetes,部署详情可见 [README.md](https://github.com/bitnami/containers/blob/main/bitnami/mysql/README.md)。
bitnami MySQL 集群初始化依赖镜像中的脚本 [scripts](https://github.com/bitnami/containers/tree/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts)、依赖 [lib](https://github.com/bitnami/containers/tree/main/bitnami/mysql/8.4/debian-12/prebuildfs/opt/bitnami/scripts),容器内位置为 `/opt/bitnami/scripts/mysql`。
镜像内脚本执行顺序为 [entrypoint.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts/mysql/entrypoint.sh),[setup.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts/mysql/entrypoint.sh),[run.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts/mysql/run.sh) 。
**以下脚本解读顺序为镜像内 MySQL 初始化顺序。**
> [!WARNING]
>
> [setup.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts/mysql/entrypoint.sh) 脚本仅为 MySQL 初始化,不负责启动 MySQL,MySQL 集群只能通过 [run.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts/mysql/run.sh) 脚本启动。
## mysql_validate()
[libmysql.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts/libmysql.sh)
检查环境变量,`()` 内为 [Statefulset](#statefulset) 中的环境变量名:
- `DB_REPLICATION_MODE="master"` 检查 DB_REPLICATION_USER(MYSQL_REPLICATION_USER)、DB_ROOT_PASSWORD(MYSQL_ROOT_PASSWORD_FILE)
- `DB_REPLICATION_MODE="slave"` 检查 DB_MASTER_HOST(MYSQL_MASTER_HOST)
- DB_ROOT_PASSWORD、DB_PASSWORD、DB_REPLICATION_PASSWORD 检查
## mysql_initialize()
[libmysql.sh](https://github.com/bitnami/containers/blob/main/bitnami/mysql/8.4/debian-12/rootfs/opt/bitnami/scripts/libmysql.sh)
MySQL 初始化脚本,相当于上文手动安装流程从 **创建 mysql 用户** 至 **配置 mysql** 以及所有 **主从配置操作**。
```bash
debug "Ensuring expected directories/files exist"
for dir in "$DB_DATA_DIR" "$DB_TMP_DIR" "$DB_LOGS_DIR"; do
ensure_dir_exists "$dir"
am_i_root && chown "$DB_DAEMON_USER":"$DB_DAEMON_GROUP" "$dir"
done
```
相当于:
```bash
mkdir -p /bitnami/mysql/data /opt/bitnami/mysql/tmp /opt/bitnami/mysql/logs
chown mysql:mysql /bitnami/mysql/data
chown mysql:mysql /opt/bitnami/mysql/tmp
chown mysql:mysql /opt/bitnami/mysql/logs
```
***
```bash
if is_file_writable "$DB_CONF_FILE"; then
info "Updating 'my.cnf' with custom configuration"
mysql_update_custom_config
else
warn "The ${DB_FLAVOR} configuration file '${DB_CONF_FILE}' is not writable. Configurations based on environment variables will not be applied for this file."
fi
```
检查 `/opt/bitnami/mysql/conf/my.cnf` 是否存在并可写,这里如果是用之前的 helm 部署 `my.cnf` 会随 [configmap](#configmap) 挂载至此路径,覆盖容器初始化 `mysql_create_default_config()` 函数根据环境变量创建的 `my.cnf`
```bash
mysql 04:48:45.58 WARN ==> The mysql configuration file '/opt/bitnami/mysql/conf/my.cnf' is not writable. Configurations based on environment variables will not be applied for this file.
```
由于 ConfigMap 默认以只读形式挂载,[statefulset](#statefulset) 中并没有设置 `defaultMode` ,这里的 `my.cnf` 文件并不会被 `mysql_update_custom_config()` 函数修改成 `my_custom.cnf`。
***
`my_custom.cnf()` 部分在 kubernetes 中并不存在,故不做讨论。
***
```bash
if [[ -e "$DB_DATA_DIR/mysql" ]]; then
info "Using persisted data"
# mysql_upgrade requires the server to be running
[[ -n "$(get_master_env_var_value ROOT_PASSWORD)" ]] && export ROOT_AUTH_ENABLED="yes"
# https://dev.mysql.com/doc/refman/8.0/en/replication-upgrade.html
mysql_upgrade
else
debug "Cleaning data directory to ensure successfully initialization"
rm -rf "${DB_DATA_DIR:?}"/*
info "Installing database"
mysql_install_db
mysql_start_bg
wait_for_mysql_access
# we delete existing users and create new ones with stricter access
# commands can still be executed until we restart or run 'flush privileges'
info "Configuring authentication"
mysql_execute "mysql" < [!WARNING]
>
> 此时 MySQL 还在初始化阶段,不允许外部访问,使用 `--skip-replica-start` 避免在初始化阶段未配置好复制用户凭据时就开始复制,等待所有初始化完成(包括用户创建、权限设置等)后会在在 `run.sh` 中执行 `START REPLICA` 命令启动复制。
```bash
mysql_start_bg() {
local -a flags=("--defaults-file=${DB_CONF_FILE}" "--basedir=${DB_BASE_DIR}" "--datadir=${DB_DATA_DIR}" "--socket=${DB_SOCKET_FILE}")
# Only allow local connections until MySQL is fully initialized, to avoid apps trying to connect to MySQL before it is fully initialized
flags+=("--bind-address=127.0.0.1")
# Add flags specified via the 'DB_EXTRA_FLAGS' environment variable
read -r -a db_extra_flags <<<"$(mysql_extra_flags)"
[[ "${#db_extra_flags[@]}" -gt 0 ]] && flags+=("${db_extra_flags[@]}")
# Do not start as root, to avoid permission issues
am_i_root && flags+=("--user=${DB_DAEMON_USER}")
# The replica should only start in 'run.sh', elseways user credentials would be needed for any connection
flags+=("--skip-replica-start")
flags+=("$@")
is_mysql_running && return
info "Starting $DB_FLAVOR in background"
debug_execute "${DB_SBIN_DIR}/mysqld" "${flags[@]}" &
# we cannot use wait_for_mysql_access here as mysql_upgrade for MySQL >=8 depends on this command
# users are not configured on slave nodes during initialization due to --skip-slave-start
wait_for_mysql
# Special configuration flag for system with slow disks that could take more time
# in initializing
if [[ -n "${DB_INIT_SLEEP_TIME}" ]]; then
debug "Sleeping ${DB_INIT_SLEEP_TIME} seconds before continuing with initialization"
sleep "${DB_INIT_SLEEP_TIME}"
fi
}
```
在命令行相当于:
Master:
```bash
/opt/bitnami/mysql/sbin/mysqld \
--defaults-file=/opt/bitnami/mysql/conf/my.cnf \
--basedir=/opt/bitnami/mysql \
--datadir=/bitnami/mysql/data \
--socket=/opt/bitnami/mysql/tmp/mysql.sock \
--bind-address=127.0.0.1 \
--server-id=123 \ # 随机生成的3位数
--log-bin=mysql-bin \ # 开启二进制日志
--sync-binlog=1 \ # 同步写入二进制日志
--innodb_flush_log_at_trx_commit=1 \ # Master节点特有配置
--user=mysql \ # 如果以root运行则切换用户
--skip-replica-start \ # 禁止复制自动启动
& # 将进程放入后台,不需要用 systemctl 启动
```
Slave:
```bash
/opt/bitnami/mysql/sbin/mysqld \
--defaults-file=/opt/bitnami/mysql/conf/my.cnf \
--basedir=/opt/bitnami/mysql \
--datadir=/bitnami/mysql/data \
--socket=/opt/bitnami/mysql/tmp/mysql.sock \
--bind-address=127.0.0.1 \
--server-id=456 \ # 随机生成的3位数
--log-bin=mysql-bin \ # 开启二进制日志
--sync-binlog=1 \ # 同步写入二进制日志
--relay-log=mysql-relay-bin \ # Slave节点特有配置
--log-replica-updates=1 \ # Slave节点特有配置
--read-only=1 \ # Slave节点特有配置
--user=mysql \ # 如果以root运行则切换用户
--skip-replica-start \ # 禁止复制自动启动
& # 将进程放入后台
```
### wait_for_mysql_access()
根据 `ROOT_AUTH_ENABLED` 情况使用 `mysql -u root` 或 `mysql mysql -u root -p"${MYSQL_ROOT_PASSWORD}"` 连接来判断 MySQL 是否准备好接受数据。
```bash
wait_for_mysql_access() {
# wait until the server is up and answering queries.
local -r user="${1:-root}"
local -a args=("mysql" "$user")
is_boolean_yes "${ROOT_AUTH_ENABLED:-false}" && args+=("$(get_master_env_var_value ROOT_PASSWORD)")
local -r retries=300
local -r sleep_time=2
is_mysql_accessible() {
echo "select 1" | mysql_execute "${args[@]}"
}
if ! retry_while is_mysql_accessible "$retries" "$sleep_time"; then
error "Timed out waiting for MySQL to be accessible"
return 1
fi
}
```
转换为:
```bash
# 循环尝试连接直到成功或超时
# ROOT_AUTH_ENABLED=false
for i in {1..300}; do
echo "select 1" | mysql mysql -u root && break
sleep 2
[ $i -eq 300 ] && echo "Timed out waiting for MySQL" && exit 1
done
# ROOT_AUTH_ENABLED=true
for i in {1..300}; do
echo "select 1" | mysql mysql -u root -p"${MYSQL_ROOT_PASSWORD}" && break
sleep 2
[ $i -eq 300 ] && echo "Timed out waiting for MySQL" && exit 1
done
```
> `wait_for_mysql()` 与 `wait_for_mysql_access()` 区别在于前者只检查 PID,后者通过 mysql 命令连接。
### mysql_configure_replication()
主从配置实现,master 创建复制用户并赋予复制权限,slave 等待主库根据 DB_REPLICATION_SLAVE_DUMP 环境变量选择复制方式,全量数据同步或者仅配置复制连接。
```bash
mysql_configure_replication() {
if [[ "$DB_REPLICATION_MODE" = "slave" ]]; then
info "Configuring replication in slave node"
debug "Checking if replication master is ready to accept connection"
while ! echo "select 1" | mysql_remote_execute "$DB_MASTER_HOST" "$DB_MASTER_PORT_NUMBER" "mysql" "$DB_MASTER_ROOT_USER" "$DB_MASTER_ROOT_PASSWORD"; do
sleep 1
done
if [[ "$DB_REPLICATION_SLAVE_DUMP" = "true" ]]; then
mysql_exec_initial_dump
else
debug "Replication master ready!"
debug "Setting the master configuration"
mysql_execute "mysql" <