Синхронизация пользователя базы данных¶
Пользователи баз данных создаёт пользователя tenax один раз. Для большинства конфигураций
этого достаточно. Эта страница — для GitOps-конфигураций, где пользователь должен соответствовать своему Secret без
чьих-либо ручных действий:
- пароль в Secret ротируется;
- база данных пересоздаётся или восстанавливается из старого дампа, и пользователя больше нет;
- кто-то меняет пользователя вручную (отзывает право, меняет пароль);
- база данных создаётся после манифестов, описывающих её пользователя (новый кластер).
Одноразовый Job не справляется ни с одним из этих случаев: он выполняется один раз, а Kubernetes не перезапустит завершённый Job при изменении Secret. CronJob, который заново применяет тот же идемпотентный SQL, справляется: в пределах одного периода пользователь снова соответствует Secret.
Как это работает¶
Один CronJob на базу данных, в namespace базы данных. Каждый запуск:
- ждёт, пока база данных ответит (в пределах дедлайна Job);
- входит как администратор, используя Secret, который использует сама база данных;
- создаёт пользователя
tenax, если его нет, и задаёт его пароль и права из Secret Tenax.
Каждая инструкция идемпотентна, поэтому запуск, который находит всё на месте, ничего не меняет. Пароли попадают в контейнер как переменные окружения из Secret; они никогда не появляются в командной строке или в журнале.
PostgreSQL¶
Замените четыре значения, помеченные # ←: namespace, 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 без экранирования, кроме строкового литерала, поэтому генерируйте его в hex
(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 их не трогает.