保持数据库用户同步¶
数据库用户一次性创建 tenax 用户,这对大多数环境已经足够。本页面向这样的 GitOps 环境:
用户必须始终与其 Secret 保持一致,而且无需任何人手动执行任何操作:
- Secret 中的密码会被轮换;
- 数据库被重建,或从较旧的转储恢复,导致用户丢失;
- 有人手动修改了用户(撤销某项授权、修改密码);
- 数据库在描述其用户的清单之后才创建(全新集群)。
一次性 Job 处理不了上述任何情况:它只运行一次,而 Secret 变化时 Kubernetes 不会重新运行已完成的 Job。 定期重新执行同一段幂等 SQL 的 CronJob 则可以:在一个周期之内,用户就会重新与 Secret 保持一致。
工作原理¶
每个数据库一个 CronJob,位于数据库所在的命名空间。每次运行:
- 等待数据库可以响应(以 Job 的截止时间为上限);
- 使用数据库自身所用 Secret 中的凭据,以管理员身份登录;
- 如果
tenax用户不存在则创建它,并根据 Tenax Secret 设置其密码和授权。
每条语句都是幂等的,因此当一切都已就绪时,运行不会做任何更改。密码以来自 Secret 的环境变量形式传入容器; 它们从不出现在命令行或日志中。
PostgreSQL¶
替换标有 # ← 的四个值:命名空间、数据库的 Service、数据库,以及保存管理员密码的 Secret 及其键。
apiVersion: batch/v1
kind: CronJob
metadata:
name: tenax-user-postgres
namespace: team-a # ←
spec:
schedule: "*/30 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 300
template:
spec:
restartPolicy: OnFailure
automountServiceAccountToken: false
securityContext: { runAsNonRoot: true, runAsUser: 999, runAsGroup: 999 }
containers:
- name: psql
image: docker.io/library/postgres:16
resources: { requests: { cpu: 10m, memory: 32Mi }, limits: { memory: 128Mi } }
securityContext: { allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }
env:
- { name: PGHOST, value: postgres } # ← the database's Service
- { name: PGDATABASE, value: app } # ← any database on the server
- { name: PGUSER, value: postgres }
- { name: PGCONNECT_TIMEOUT, value: "10" }
- name: PGPASSWORD
valueFrom: { secretKeyRef: { name: postgres-admin, key: password } } # ←
- name: TENAX_PASSWORD
valueFrom: { secretKeyRef: { name: tenax-postgres, key: password } }
command: [sh, -ec]
args:
- |
until pg_isready -q; do echo "waiting for $PGHOST"; sleep 5; done
psql -v ON_ERROR_STOP=1 -X <<'SQL'
\getenv tenax_password TENAX_PASSWORD
SELECT 'CREATE ROLE tenax LOGIN REPLICATION' WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'tenax') \gexec
ALTER ROLE tenax WITH LOGIN REPLICATION NOSUPERUSER NOCREATEDB NOCREATEROLE PASSWORD :'tenax_password';
GRANT pg_read_all_data, pg_monitor TO tenax;
ALTER ROLE tenax SET idle_in_transaction_session_timeout = '10min';
SQL
echo "tenax user in sync on $PGHOST"
\getenv 要求镜像中的 psql 为 15 或更高版本;服务器可以是任意受支持的版本。max_slot_wal_keep_size
请在服务器配置中设置(例如 -c max_slot_wal_keep_size=2GB),而不是在这里:它是服务器设置,不属于用户。
MySQL¶
apiVersion: batch/v1
kind: CronJob
metadata:
name: tenax-user-mysql
namespace: team-a # ←
spec:
schedule: "*/30 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 300
template:
spec:
restartPolicy: OnFailure
automountServiceAccountToken: false
securityContext: { runAsNonRoot: true, runAsUser: 999, runAsGroup: 999 }
containers:
- name: mysql
image: docker.io/library/mysql:8.4
resources: { requests: { cpu: 10m, memory: 64Mi }, limits: { memory: 256Mi } }
securityContext: { allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }
env:
- { name: MYSQL_HOST, value: mysql } # ← the database's Service
- name: ROOT_PASSWORD
valueFrom: { secretKeyRef: { name: mysql-admin, key: root-password } } # ←
- name: TENAX_PASSWORD
valueFrom: { secretKeyRef: { name: tenax-mysql, key: password } }
command: [bash, -ec]
args:
- |
umask 077
printf '[client]\nuser=root\npassword=%s\nhost=%s\nconnect-timeout=10\n' "$ROOT_PASSWORD" "$MYSQL_HOST" > /tmp/root.cnf
until mysqladmin --defaults-extra-file=/tmp/root.cnf ping --silent; do echo "waiting for $MYSQL_HOST"; sleep 5; done
mysql --defaults-extra-file=/tmp/root.cnf <<SQL
CREATE USER IF NOT EXISTS 'tenax'@'%' IDENTIFIED BY '${TENAX_PASSWORD}';
ALTER USER 'tenax'@'%' IDENTIFIED BY '${TENAX_PASSWORD}';
GRANT SELECT, SHOW VIEW, TRIGGER, EVENT, LOCK TABLES, PROCESS, RELOAD, SHOW_ROUTINE,
REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'tenax'@'%';
SQL
rm -f /tmp/root.cnf
echo "tenax user in sync on $MYSQL_HOST"
密码直接嵌入 SQL 的字符串字面量中,除此之外不做任何转义,因此请用十六进制生成密码(openssl rand -hex 24)。
立即运行¶
kubectl -n team-a create job --from=cronjob/tenax-user-postgres tenax-user-now
kubectl -n team-a logs -f job/tenax-user-now
每 30 分钟一次已经足够:Tenax 自身会重试被拒绝的登录并在目标上报告,因此密码轮换最多在一个周期后就会生效。
pg_hba.conf 规则和 binlog 设置仍属于服务器配置(数据库用户第 3 步);此 CronJob 不会改动它们。