[應用] Mariadb
===
###### tags: `K8s / app`
###### tags: `Kubernetes`, `k8s`, `app`, `Mariadb`
<br>
[TOC]
<br>
# SQL, maria, mariadb-admin commands
## SQL Commands
### `SELECT 1`
- 不從任何資料表讀取,直接回傳**一列一欄、值為 1**;常用來測試連線/健康檢查。
- **執行範例**
```
+---+
| 1 |
+---+
| 1 |
+---+
```
<br>
### `SELECT 1 FROM DUAL;`
- 做的事與 `SELECT 1` 一樣,但透過虛擬表 **DUAL** 寫法;源自 Oracle 習慣。
- **在 MariaDB/MySQL 可省略 `FROM DUAL`,兩者等價。**
- **執行範例**
```
+---+
| 1 |
+---+
| 1 |
+---+
```
<br>
### `SHOW databases`
- **用途**:列出「目前使用者**可見**」的資料庫清單。是否看得到,取決於你是否對該資料庫有任何權限或有 `SHOW DATABASES` 權限。
- **你會看到**:系統資料庫(`information_schema`, `mysql`, `performance_schema`, `sys`)+ 你自己的業務資料庫(例如 `slurm_acct_db`)。
- **小訣竅**
* 只看特定名稱:`SHOW DATABASES LIKE 'slurm%';`
* 也可用等價語法:`SHOW SCHEMAS;`
* 切換資料庫:`USE slurm_acct_db;`
- **執行範例**
```
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| slurm_acct_db |
| sys |
+--------------------+
```
<br>
### `SELECT user,host,plugin FROM mysql.user`
- **用途**:從系統表 `mysql.user` 查看**帳號清單**與**認證方式**。
- `user`:帳號名稱
- `host`:允許連入的來源(可含萬用字元,如 `%` 代表任意主機、`localhost` 僅本機、`127.0.0.1`/`::1` 分別為 IPv4/IPv6 本機)
- `plugin`:**認證外掛**(如 `mysql_native_password`、`auth_socket`、`ed25519`、`pam` 等)
- **執行範例**
```
+-------------+-----------+-----------------------+
| User | Host | plugin |
+-------------+-----------+-----------------------+
| mariadb.sys | localhost | mysql_native_password |
| root | localhost | mysql_native_password |
| root | % | mysql_native_password |
| healthcheck | 127.0.0.1 | mysql_native_password |
| healthcheck | ::1 | mysql_native_password |
| healthcheck | localhost | mysql_native_password |
| slurm | % | mysql_native_password |
+-------------+-----------+-----------------------+
```
- **匹配規則重點**:
- 連線時會用「`user`@`host`」去**精確匹配**這張表(最具體的 `host` 規則優先)。因此 `root@localhost` 與 `root@%` 是**兩個不同帳號**。
- **常見維運動作**
- 看清楚是哪一個實際帳號被匹配:
```sql
SELECT CONCAT(user, '@', host) AS account, plugin
FROM mysql.user
ORDER BY user, host;
```
- 查某帳號的權限:`SHOW GRANTS FOR 'slurm'@'%';`
- 變更認證外掛(示例):
```sql
ALTER USER 'slurm'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('yourStrongPwd');
-- 或在部分版本可用:
-- ALTER USER 'slurm'@'%' IDENTIFIED BY 'yourStrongPwd';
```
- **安全建議**:避免不必要的 `user@'%'`(開放過大);管理者帳號(例如 `root`)通常限制在 `localhost`。
- **權限提示**:查 `mysql.user` 需要足夠的系統權限;在受限環境可改用 `INFORMATION_SCHEMA`(如 `USER_PRIVILEGES` 等)查詢權限相關資訊。
- **變更plugin**:將 root plugin 方式設置為 使用密碼
```
ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('$ROOT');
ALTER USER 'root'@'127.0.0.1' IDENTIFIED VIA mysql_native_password USING PASSWORD('$ROOT');
ALTER USER 'root'@'::1' IDENTIFIED VIA mysql_native_password USING PASSWORD('$ROOT');
FLUSH PRIVILEGES;
```
<br>
---
<br>
## `mariadb` Commands
> 也可用 `mysql` command
### [重要] :warning::warning::warning: 留意 password 參數用法
- ### Usage
```
-p, --password[=name]
Password to use when connecting to server.
If password is not given it's asked from the tty.
```
- ### 正確使用:
- `-p"2KwBph3kX0(-S8HU"`
- `--password="2KwBph3kX0(-S8HU"`
- ### 錯誤使用:
- `-p="2KwBph3kX0(-S8HU"`
- `--password"2KwBph3kX0(-S8HU"`
- `--password "2KwBph3kX0(-S8HU"`
- 會要求輸入 password
且後面的 `"2KwBph3kX0(-S8HU"` 不是作為 password 參數
而是作為 database 名稱
- `ERROR 1049 (42000): Unknown database '2KwBph3kX0(-S8HU'`
<br>
### 用 root 密碼連上 MariaDB 並跑 `SELECT 1`
```
mariadb -u root -p"${MARIADB_ROOT_PASSWORD}" -e "SELECT 1;"
```
- **說明**
- `mariadb`:MariaDB 的**用戶端程式**(連到伺服器執行 SQL)。
- `-u root`:用 **root** 帳號登入。
- `-p"${MARIADB_ROOT_PASSWORD}"`:用環境變數 **MARIADB_ROOT_PASSWORD** 當密碼(`-p` 後**不留空白**,用雙引號讓變數展開並保留特殊字元)。
- `-e "SELECT 1;"`:**非互動模式**執行一條 SQL 後離開。`SELECT 1;` 是最簡單的健康檢查查詢,成功會印出一個 `1`。
- **小提醒**
- 在 K8s Probe 裡,**Exit Code=0** 代表成功(可連且能跑查詢);非 0 代表失敗(密碼錯、尚未啟動好、連線失敗等)。
- 若 root 啟用了 `unix_socket` 認證,**密碼登入會失敗**,需改用對應帳號或調整 root 認證方式。
<br>
### 用 Unix Socket 連上 MariaDB 並跑 `SELECT 1`
```
mariadb -u root -S /run/mysqld/mysqld.sock -e "SELECT 1;"
```
- **說明**
- `mariadb`:MariaDB 的**用戶端程式**(連到伺服器執行 SQL)。
- `-u root`:以 **root** 帳號登入(搭配 socket 時是否需要密碼,取決於認證外掛)。
- `-S /run/mysqld/mysqld.sock`:指定 **Unix socket 檔**連線(走檔案 I/O,不走 TCP)。
- 常見路徑為
- `/run/mysqld/mysqld.sock`
- `/var/run/mysqld/mysqld.sock`
- `-e "SELECT 1;"`:**非互動模式**執行一條 SQL 後離開。
- **適用情境**
- 跑在**同一台主機 / 同一個 Pod**內,想避開網路堆疊(速度快、少依賴)。
- `root@localhost` 啟用 **`unix_socket` 認證**時,**以本機 OS 使用者登入可免密碼**(多數套件預設如此);此時用 `-S` 最符合預期。
- **小提醒**
- **socket 路徑查法**:
`grep -R "^\s*socket" /etc/mysql /etc/my.cnf* 2>/dev/null || my_print_defaults mysqld | grep socket`
- 執行範例
```
/etc/mysql/my.cnf:socket = /run/mysqld/mysqld.sock
/etc/mysql/mariadb.cnf:socket = /run/mysqld/mysqld.sock
```
- **權限**:執行者需能讀寫該 socket(通常屬主/群組為 `mysql`)。非 root 身分可能需要加入 `mysql` 群組或改用 `sudo -u mysql`。
- **`-h` 差異**:`-h 127.0.0.1` 強制走 TCP;`-h localhost`(未指定 `--protocol=tcp`)通常會走 socket;**指定 `-S` 時會以 socket 為準**。
- **路徑差異**:不同映像/發行版路徑可能不同(`/run/...` 或 `/var/run/...`)。若檔案不存在,通常是 mysqld 尚未啟動好或路徑/權限不正確。
<br>
---
<br>
## `mariadb-admin` Commands
### 能不能回應 ping
```bash
mariadb-admin \
--user=root \
--password="$MARIADB_ROOT_PASSWORD" \
ping --silent
```
- **正常回應**
```
mysqld is alive
```
- **密碼錯誤**
```
error: 'Access denied for user 'root'@'localhost' (using password: YES)'
```
- **無法連線**
```
mariadb-admin: connect to server at 'localhost' failed
```
<br>
---
---
<br>
# kubernetes
## kubectl commands
### 查詢 root 密碼
```bash
NAMESPACE=slurm
ROOT=$(kubectl -n ${NAMESPACE} get secret mariadb-root -o jsonpath='{.data.password}' | base64 -d)
echo $ROOT
# 1T2qHJ~0sRGjiW=3
```
- ### 查看 secret
```yaml=
$ kubectl -n slurm get secret/mariadb-root -o yaml
apiVersion: v1
data:
password: MVQycUhKfjBzUkdqaVc9Mw==
kind: Secret
metadata:
creationTimestamp: "2025-10-03T06:51:19Z"
name: mariadb-root
namespace: slurm
resourceVersion: "166297201"
uid: 0986903e-a2a9-4e1c-99fc-de43ad0822f1
type: Opaque
```
- **驗證 `password` 解碼後的明碼**
```
$ echo -n "MVQycUhKfjBzUkdqaVc9Mw==" | base64 -d
1T2qHJ~0sRGjiW=3
```
- ### 環境變數
- secret 中的密碼,會存放到環境變數 `MARIADB_ROOT_PASSWORD`
- **檢查環境變數**
```bash
env | grep -i password
```
或
```bash
env | grep -i root
```
- ### 使用 root 帳號測試
```
mariadb -u root -p"${MARIADB_ROOT_PASSWORD}" -e "SELECT 1;"
```
<br>
---
<br>
## 在 Kubernetes 上部署 mariadb
> https://github.com/SlinkyProject/slurm-operator/blob/main/docs/installation.md#mariadb-community-edition
### 步驟一:安裝 mariadb operator
```bash=
helm repo add mariadb-operator https://helm.mariadb.com/mariadb-operator
helm repo update
helm install mariadb-operator-crds mariadb-operator/mariadb-operator-crds
helm install mariadb-operator mariadb-operator/mariadb-operator \
--namespace mariadb --create-namespace
```
- ### 缺點
- 移除 mariadb-operator 時,mariadb CRD 不會跟著移除
- ### crds 與 operator 一起安裝
```bash
helm install mariadb-operator mariadb-operator/mariadb-operator \
--namespace mariadb-operator --create-namespace \
--set 'crds.enabled=true'
```
- ### 優點:
- 移除 operator 時,CRD 會跟著移除 (有驗證過)
- 升版 operator 時,CRD 會跟著升版
### 步驟二:部署 MariaDB CR
```yaml=
# mariadb.yaml
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb
namespace: slurm
spec:
rootPasswordSecretKeyRef:
name: mariadb-root
key: password
generate: true
username: slurm
database: slurm_acct_db
passwordSecretKeyRef:
name: mariadb-password
key: password
generate: true
storage:
size: 1Gi
myCnf: |
[mariadb]
bind-address=*
default_storage_engine=InnoDB
binlog_format=row
innodb_autoinc_lock_mode=2
innodb_buffer_pool_size=4096M
innodb_lock_wait_timeout=900
innodb_log_file_size=1024M
max_allowed_packet=256M
```
### probe 設定1
```yaml
startupProbe:
initialDelaySeconds: 20 # 20
periodSeconds: 10 # 10
timeoutSeconds: 5 # 5
failureThreshold: 3 # 3
livenessProbe:
initialDelaySeconds: 180 # 20
periodSeconds: 60 # 10
timeoutSeconds: 60 # 5
failureThreshold: 10 # 3
readinessProbe:
initialDelaySeconds: 180 # 20
periodSeconds: 60 # 10
timeoutSeconds: 60 # 5
failureThreshold: 10 # 3
```
### probe 設定2:使用 tcpSocket
```yaml
startupProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 30
livenessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
```
- **優勢**
- 不需要任何密碼驗證
- 檢查 MariaDB 服務是否在監聽 3306 埠
- 執行速度最快
- 避免了密碼錯誤導致的失敗
### 其他設定
- [Storage](https://mariadb.com/docs/tools/mariadb-enterprise-operator/storage)
<br>
---
---
<br>
## 測試 mariadb 連線與查詢
> 假設 mariadb server 位於 -n slurm
### 在其他 namespace 下連接 mariadb server
- ### 使用 Bitnami MariaDB 映像檔
```bash
# 先把連線參數與密碼變數抓好(密碼從 slurm namespace 的 Secret 取)
DB_HOST=mariadb.slurm.svc.cluster.local
DB_PORT=3306
DB_NAME=slurm_acct_db
DB_USER=slurm
DB_PASS=$(kubectl -n slurm get secret mariadb-password -o jsonpath='{.data.password}' | base64 -d)
# 在 default 起一個臨時 client 容器並直接測試
kubectl run -n default -it --rm mariadb-client \
--image=bitnami/mariadb:latest \
--restart=Never \
--env=MYSQL_PWD="$DB_PASS" \
--command -- sh -lc \
"mariadb -h ${DB_HOST} -P ${DB_PORT} -u ${DB_USER} -D ${DB_NAME} -e 'SELECT 1 AS ok; SHOW DATABASES; SELECT DATABASE(); SHOW TABLES;'"
```
- **說明**
- 在執行 `kubectl run` 前,shell 就會將變數置換成字串
- 用環境變數 `MYSQL_PWD` 避免在命令列明文出現密碼;測試會列出資料庫/目前 DB/資料表。
- 或是進入到 pod 在執行 mariadb 指令
```
mariadb \
-h mariadb.slurm.svc.cluster.local \
-P 3306 \
-u slurm \
-D slurm_acct_db \
-e 'SELECT 1 AS ok; SHOW DATABASES; SELECT DATABASE(); SHOW TABLES;'
```
- **執行結果**
```
WARNING: option --ssl-verify-server-cert is disabled, because of an insecure passwordless login.
+----+
| ok |
+----+
| 1 |
+----+
+--------------------+
| Database |
+--------------------+
| information_schema |
| slurm_acct_db |
+--------------------+
+---------------+
| DATABASE() |
+---------------+
| slurm_acct_db |
+---------------+
+-------------------------------------+
| Tables_in_slurm_acct_db |
+-------------------------------------+
| acct_coord_table |
| acct_table |
| clus_res_table |
| cluster_table |
| convert_version_table |
| federation_table |
| qos_table |
| res_table |
| slurm_slurm_assoc_table |
| slurm_slurm_assoc_usage_day_table |
| slurm_slurm_assoc_usage_hour_table |
| slurm_slurm_assoc_usage_month_table |
| slurm_slurm_event_table |
| slurm_slurm_job_env_table |
| slurm_slurm_job_script_table |
| slurm_slurm_job_table |
| slurm_slurm_last_ran_table |
| slurm_slurm_qos_usage_day_table |
| slurm_slurm_qos_usage_hour_table |
| slurm_slurm_qos_usage_month_table |
| slurm_slurm_resv_table |
| slurm_slurm_step_table |
| slurm_slurm_suspend_table |
| slurm_slurm_usage_day_table |
| slurm_slurm_usage_hour_table |
| slurm_slurm_usage_month_table |
| slurm_slurm_wckey_table |
| slurm_slurm_wckey_usage_day_table |
| slurm_slurm_wckey_usage_hour_table |
| slurm_slurm_wckey_usage_month_table |
| table_defs_table |
| tres_table |
| txn_table |
| user_table |
+-------------------------------------+
pod "mariadb-client" deleted
```
<br>
- ### 若想用官方 `mariadb` 映像檔
```bash
DB_PASS=$(kubectl -n slurm get secret mariadb-password -o jsonpath='{.data.password}' | base64 -d)
kubectl run -n default -it --rm mariadb-client \
--image=mariadb:11 \
--restart=Never \
--env=MYSQL_PWD="$DB_PASS" \
--command -- bash -lc \
"mariadb -h mariadb.slurm.svc.cluster.local -P 3306 -u slurm slurm_acct_db -e 'SELECT VERSION(); SELECT 1;'"
```
- **執行結果**
```
WARNING: option --ssl-verify-server-cert is disabled, because of an insecure passwordless login.
+------------------------+
| VERSION() |
+------------------------+
| 11.8.2-MariaDB-ubu2404 |
+------------------------+
+---+
| 1 |
+---+
| 1 |
+---+
pod "mariadb-client" deleted
```
<br>
### 連線不通時快速檢查
```bash
# 確認 DNS 能解
kubectl run -n default -it --rm dnsutils --image=busybox --restart=Never \
--command -- sh -lc "nslookup mariadb.slurm.svc.cluster.local"
# 確認 TCP 可達
kubectl run -n default -it --rm netshoot --image=nicolaka/netshoot --restart=Never \
--command -- sh -lc "nc -vz mariadb.slurm.svc.cluster.local 3306"
```
> 若這裡失敗,通常是 NetworkPolicy、Service 名稱或 Port 設定問題;能通但認證失敗再回頭檢查帳密與 DB 名稱是否一致。
<br>
---
---
<br>
## 討論
### MARIADB_AUTO_UPGRADE 參數用途為何?
- ### Question
```yaml
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb
spec:
...
env:
- name: MARIADB_AUTO_UPGRADE
value: "1"
...
```
MARIADB_AUTO_UPGRADE 參數用途為何?
- ### gpt-5 (2025/10/09)
簡單說:`MARIADB_AUTO_UPGRADE=1` 會讓 **MariaDB Docker 官方映像**在容器啟動時,自動執行 `mariadb-upgrade`(若判斷有需要),以**升級系統表**並標記/快速檢查資料表,使資料目錄與新版本相容。這可減少你在**更換映像標籤或版本升級**(甚至 MySQL→MariaDB 的遷移)時的手動步驟。([Docker Hub][1])
### 要點
* **觸發時機**:只要設為非空值(如 `"1"`),入口腳本會在需要時執行 `mariadb-upgrade`;若版本未變更或不需升級則不做事。([Docker Hub][1])
* **做了什麼**:更新 `mysql` 系統資料表,並快速檢查/標記資料表與新版本相容(必要時修復)。([man7.org][2])
* **常見用途**:
* 容器升級版本時自動處理系統表升級。([Docker Hub][1])
* 使用舊版 MySQL/MariaDB 的資料目錄啟動新版 MariaDB(官方示例就要求設 `MARIADB_AUTO_UPGRADE=1`)。([MariaDB.org][3])
### 注意事項
* 這個變數**主要涵蓋系統表的升級**;若你的應用需要特定的**使用者資料表結構變更**,仍可能需要你自行執行對應的 DDL 或維護腳本。([GitHub][4])
* 在少數環境設定(例如特定 SSL 設定)下,曾有自動升級失敗的回報;若遇到啟動失敗,請先檢查容器日誌,必要時暫時關閉自動升級(設為 `"0"` 或移除變數)並改採手動流程。([GitHub][5])
### 與你這份 CR 的關聯
你在 `spec.env` 放入:
```yaml
- name: MARIADB_AUTO_UPGRADE
value: "1"
```
代表 MariaDB Pod 每次以新映像啟動時,會自動執行必要的 `mariadb-upgrade`,協助完成升級所需的系統表更新,減少人工介入。([Docker Hub][1])
> 延伸閱讀:MariaDB 官方對 Docker 自動升級/遷移的說明與範例。([MariaDB.org][3])
[1]: https://hub.docker.com/_/mariadb?utm_source=chatgpt.com "mariadb - Official Image"
[2]: https://man7.org/linux/man-pages/man1/mariadb-upgrade.1.html?utm_source=chatgpt.com "mariadb-upgrade(1) - Linux manual page"
[3]: https://mariadb.org/migration-with-docker-official-images/?utm_source=chatgpt.com "Migration with Docker Official Images"
[4]: https://github.com/MariaDB/mariadb-docker/issues/562?utm_source=chatgpt.com "Add option to auto upgrade user tables · Issue #562"
[5]: https://github.com/MariaDB/mariadb-docker/issues/592?utm_source=chatgpt.com "MARIADB_AUTO_UPGRADE=1/mariadb-upgrade not ..."
<br>
---
### startupProbe 時間軸
- ### Question
```yaml
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb
spec:
...
startupProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 30
```
給出 startupProbe 時間軸,並標注做了什麼?
- ### gpt-5 (2025/10/09)
下面用你的設定值畫出「**startupProbe 時間軸**」+每一步 kubelet 做了什麼。
### 你的參數
* `tcpSocket.port`: **3306**
* `initialDelaySeconds`: **20s**
* `periodSeconds`: **10s**
* `failureThreshold`: **30**
* `timeoutSeconds`: 未設定 ⇒ **預設 1s**
* `successThreshold`: 固定為 **1**(startupProbe 不可調)
---
### 時間軸(第一次容器啟動)
```
t=0s Container 進入 Running;啟動 startupProbe 計時。(若有 liveness/readiness,這時都「暫停」,直到 startup 成功)
t=20s 第 1 次探針:對 3306 建立 TCP 連線(成功 ⇒ startup 完成;失敗 ⇒ 累計失敗 1/30)
t=30s 第 2 次探針(成功 ⇒ 完成;失敗 ⇒ 2/30)
t=40s 第 3 次探針(…)
...
t=300s 第 29 次探針(失敗 ⇒ 29/30)
t=310s 第 30 次探針(失敗 ⇒ 30/30 達到門檻)⇒ **kubelet 立即重啟容器**(加上單次 probe 的 timeout,最晚約 311s)
```
* **最長允許啟動時間(直到第一次被重啟)**
≈ `initialDelaySeconds + (failureThreshold - 1) * periodSeconds + timeoutSeconds`
= `20 + (30 - 1) * 10 + 1` ≈ **311 秒**
(說法白話點:第 1 次在 20s,之後每 10s 一次,連續 30 次都失敗就重啟;每次最多等 1s 超時)
---
### 成功與後續行為
* **任何一次探針成功**(TCP 連上 3306)⇒ `startupProbe` **完成**,之後**不再執行**。
* 這時(如果你**有**定義)才會開始套用 **livenessProbe / readinessProbe**。
* 你目前 **未定義 readiness/liveness**:
* 沒有 readinessProbe 時,容器一旦 Running 通常就被視為 Ready(可能過早納入 Service 後端)。
* 建議再補一個 `readinessProbe`(例如 `mariadb -S /run/mysqld/mysqld.sock -e "SELECT 1"`)來**真正等資料庫可用**才對外服務;另行加上 `livenessProbe` 做長期健康檢查。
---
### 速查表(每一步做了什麼)
* **等待期**(0–20s):不做檢查,等初始延遲。
* **週期檢查**(每 10s 一次):嘗試 **TCP 連線到 3306**;連上即成功;連不上或逾時(>1s)算失敗。
* **失敗累計**:連續失敗 **30 次** ⇒ **重啟容器**,並進入 Kubernetes 的重啟退避(back-off)機制。
* **成功一次就收工**:`startupProbe` 結束,之後交由 readiness/liveness(若有)接手。
<br>
### startupProbe 時間軸
- ### Question
```yaml
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 18m default-scheduler Successfully assigned slurm/mariadb-0 to ocis-esc4000a-e11
Normal Pulled 18m kubelet Container image "docker-registry1.mariadb.com/library/mariadb:11.8.2" already present on machine
Normal Created 18m kubelet Created container: mariadb
Normal Started 18m kubelet Started container mariadb
Warning Unhealthy 15m kubelet Startup probe failed: ERROR 2002 (HY000): Can't connect to local server through socket '/run/mysqld/mysqld.sock' (2)
```
為何是不健康?
- ### gpt-5 (2025/10/09)
<br>
{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}