Manter o usuário do banco sincronizado¶
Usuários do banco cria o usuário tenax uma vez. Isso basta para a maioria dos ambientes. Esta
página é para ambientes GitOps em que o usuário precisa continuar batendo com o seu Secret sem que ninguém execute
nada:
- a senha do Secret é rotacionada;
- o banco é recriado ou restaurado de um dump mais antigo, e o usuário some;
- alguém altera o usuário à mão (remove um grant, muda a senha);
- o banco é criado depois dos manifestos que descrevem o usuário dele (um cluster novo).
Um Job avulso não resolve nenhum desses casos: ele roda uma vez, e o Kubernetes não executa de novo um Job concluído quando o Secret muda. Um CronJob que reaplica o mesmo SQL idempotente resolve: dentro de um período, o usuário volta a bater com o Secret.
Como funciona¶
Um CronJob por banco, no namespace do banco. Cada execução:
- espera até o banco responder (limitado pelo deadline do Job);
- faz login como administrador, usando o Secret que o próprio banco usa;
- cria o usuário
tenaxse ele não existir e define a senha e os grants dele a partir do Secret do Tenax.
Todas as instruções são idempotentes, então uma execução que encontra tudo no lugar não altera nada. As senhas chegam ao contêiner como variáveis de ambiente vindas de Secrets; nunca aparecem em uma linha de comando nem em um log.
PostgreSQL¶
Substitua os quatro valores marcados com # ←: o namespace, o Service do banco, o banco e o Secret e a chave que
guardam a senha do administrador.
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 exige psql 15 ou posterior na imagem; o servidor pode ser qualquer versão suportada. Defina
max_slot_wal_keep_size na configuração do servidor (por exemplo -c max_slot_wal_keep_size=2GB), não aqui: é uma
configuração do servidor, não faz parte do usuário.
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"
A senha entra no SQL sem escape além do literal de string, então gere-a em hexadecimal (openssl rand -hex 24).
Executar agora¶
kubectl -n team-a create job --from=cronjob/tenax-user-postgres tenax-user-now
kubectl -n team-a logs -f job/tenax-user-now
A cada 30 minutos é mais que suficiente: o próprio Tenax tenta de novo um login recusado e o reporta no alvo, então
uma rotação é aplicada no máximo um período depois. A regra do pg_hba.conf e as configurações do binlog continuam
sendo configuração do servidor (Usuários do banco, passo 3); este CronJob não mexe nelas.