此文档为 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" <