目標
このレッスンの後、次のことを行います:
- Kubernetes に Patroni クラスターをデプロイ
- StatefulSet と Persistent Volumes を構成する
- Patroni Kubernetes を使用するオペレーター
- ストレージ クラスとボリューム管理を実装
- K8s 環境で Patroni を監視およびスケーリング
1。 Patroni
1.1 の Kubernetes アーキテクチャ。コンポーネント
Kubernetes Cluster: ├─ StatefulSet: postgres-cluster │ ├─ Pod: postgres-0 (Leader) │ ├─ Pod: postgres-1 (Replica) │ └─ Pod: postgres-2 (Replica) ├─ Service: postgres-master (ClusterIP) ├─ Service: postgres-replica (ClusterIP) ├─ Service: postgres-config (Headless) ├─ ConfigMap: postgres-config ├─ Secret: postgres-credentials └─ PersistentVolumeClaims: ├─ pgdata-postgres-0 ├─ pgdata-postgres-1 └─ pgdata-postgres-2
DCS: Kubernetes API (replaces etcd!)
1.2。 K8s の利点
- 個別の etcd は必要ありません - DCS に Kubernetes API を使用
- 組み込みのスケジューリング_ - K8s はポッドを処理します配置_
- ストレージ管理 - PVC の自動プロビジョニング_
- サービス検出 - K8s サービスエンドポイント
- ローリングアップデート - ネイティブ K8 機能
- リソース制限 - CPU/メモリ保証_
2。前提条件_
2.1。 Kubernetes クラスター
# Using kind (Kubernetes in Docker) for local testing curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 chmod +x ./kind sudo mv ./kind /usr/local/bin/kindCreate cluster
kind create cluster --name postgres-ha
Or use existing K8s cluster (GKE, EKS, AKS)
2.2。 kubectl セットアップ
# Install kubectl curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectlVerify
kubectl version --client kubectl cluster-info
2.3。ヘルム (オプション)
# Install Helm curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bashVerify
helm version
3。 StatefulSet を使用した手動展開
3.1。名前空間_
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: postgres-ha
kubectl apply -f namespace.yaml
3.2 を作成します。 ConfigMap_
# configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: postgres-config namespace: postgres-ha data: patroni.yml: | scope: postgres-cluster namespace: /service/kubernetes: labels: application: patroni cluster-name: postgres-cluster scope_label: cluster-name role_label: role use_endpoints: true pod_ip: $(POD_IP) ports: - name: postgresql port: 5432 bootstrap: dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 maximum_lag_on_failover: 1048576 postgresql: use_pg_rewind: true parameters: max_connections: 100 shared_buffers: 256MB effective_cache_size: 1GB maintenance_work_mem: 64MB checkpoint_completion_target: 0.9 wal_buffers: 16MB default_statistics_target: 100 random_page_cost: 1.1 effective_io_concurrency: 200 work_mem: 2621kB min_wal_size: 1GB max_wal_size: 4GB max_worker_processes: 4 max_parallel_workers_per_gather: 2 max_parallel_workers: 4 max_parallel_maintenance_workers: 2 initdb: - encoding: UTF8 - data-checksums pg_hba: - host replication replicator 0.0.0.0/0 scram-sha-256 - host all all 0.0.0.0/0 scram-sha-256 postgresql: listen: 0.0.0.0:5432 connect_address: $(POD_IP):5432 data_dir: /var/lib/postgresql/data/pgdata bin_dir: /usr/lib/postgresql/18/bin authentication: replication: username: replicator password: rep_password superuser: username: postgres password: postgres_password parameters: unix_socket_directories: '/var/run/postgresql' restapi: listen: 0.0.0.0:8008 connect_address: $(POD_IP):8008
kubectl apply -f configmap.yaml
3.3.秘密
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
namespace: postgres-ha
type: Opaque
stringData:
postgres-password: postgres_password
replicator-password: rep_password
kubectl apply -f secret.yaml
3.4。 StatefulSet
# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: postgres-ha
labels:
application: patroni
cluster-name: postgres-cluster
spec:
serviceName: postgres-config
replicas: 3
selector:
matchLabels:
application: patroni
cluster-name: postgres-cluster
template:
metadata:
labels:
application: patroni
cluster-name: postgres-cluster
spec:
serviceAccountName: postgres
containers:
- name: postgres
image: postgres:18-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5432
name: postgresql
protocol: TCP
- containerPort: 8008
name: patroni
protocol: TCP
env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: PATRONI_KUBERNETES_POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: PATRONI_KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: PATRONI_KUBERNETES_LABELS
value: "{application: patroni, cluster-name: postgres-cluster}"
- name: PATRONI_SCOPE
value: postgres-cluster
- name: PATRONI_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: PATRONI_POSTGRESQL_DATA_DIR
value: /var/lib/postgresql/data/pgdata
- name: PATRONI_REPLICATION_USERNAME
value: replicator
- name: PATRONI_REPLICATION_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: replicator-password
- name: PATRONI_SUPERUSER_USERNAME
value: postgres
- name: PATRONI_SUPERUSER_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: postgres-password
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
- name: config
mountPath: /etc/patroni
livenessProbe:
httpGet:
path: /liveness
port: 8008
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /readiness
port: 8008
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
volumes:
- name: config
configMap:
name: postgres-config
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard # Adjust for your K8s cluster
resources:
requests:
storage: 10Gi
kubectl apply -f statefulset.yaml
3.5。サービス
# services.yamlHeadless service for StatefulSet
apiVersion: v1 kind: Service metadata: name: postgres-config namespace: postgres-ha labels: application: patroni cluster-name: postgres-cluster spec: clusterIP: None ports: - port: 5432 targetPort: 5432 name: postgresql - port: 8008 targetPort: 8008 name: patroni selector: application: patroni cluster-name: postgres-cluster
Service for master (read-write)
apiVersion: v1 kind: Service metadata: name: postgres-master namespace: postgres-ha labels: application: patroni cluster-name: postgres-cluster spec: type: ClusterIP ports: - port: 5432 targetPort: 5432 name: postgresql selector: application: patroni cluster-name: postgres-cluster role: master
Service for replicas (read-only)
apiVersion: v1 kind: Service metadata: name: postgres-replica namespace: postgres-ha labels: application: patroni cluster-name: postgres-cluster spec: type: ClusterIP ports: - port: 5432 targetPort: 5432 name: postgresql selector: application: patroni cluster-name: postgres-cluster role: replica
kubectl apply -f services.yaml
3.6。 RBAC (サービス アカウント)
# rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: postgres
namespace: postgres-ha
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: postgres
namespace: postgres-ha
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- get
- list
- patch
- update
- watch
- delete
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
- patch
- update
- create
- list
- watch
- delete
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- patch
- update
- watch
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: postgres
namespace: postgres-ha
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: postgres
subjects:
kind: ServiceAccount name: postgres
kubectl apply -f rbac.yaml
4。導入を確認
4.1。ポッド_
kubectl get pods -n postgres-ha -wOutput:
NAME READY STATUS RESTARTS AGE
postgres-0 1/1 Running 0 2m
postgres-1 1/1 Running 0 1m
postgres-2 1/1 Running 0 30s
4.2 を確認します。 StatefulSet
kubectl get statefulset -n postgres-ha
kubectl describe statefulset postgres -n postgres-ha
4.3 を確認します。サービス_
kubectl get svc -n postgres-haNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
postgres-config ClusterIP None <none> 5432/TCP,8008/TCP 3m
postgres-master ClusterIP 10.96.100.1 <none> 5432/TCP 3m
postgres-replica ClusterIP 10.96.100.2 <none> 5432/TCP 3m
4.4 を確認します。 Patroni クラスター
# Exec into pod kubectl exec -it postgres-0 -n postgres-ha -- bashInside pod
patronictl list
+ Cluster: postgres-cluster -------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+------------+-------------+--------+-----------+----+-----------+
| postgres-0 | 10.244.0.5 | Leader | running | 1 | |
| postgres-1 | 10.244.0.6 | Replica| streaming | 1 | 0 |
| postgres-2 | 10.244.0.7 | Replica| streaming | 1 | 0 |
+------------+-------------+--------+-----------+----+-----------+
4.5 を確認してください。接続をテストします
# From within cluster kubectl run -it --rm psql-client --image=postgres:18 --restart=Never -n postgres-ha --
psql -h postgres-master -U postgresCreate test table
CREATE TABLE k8s_test (id serial primary key, data text); INSERT INTO k8s_test (data) VALUES ('Hello from Kubernetes!'); SELECT * FROM k8s_test;
5。 Zalando Postgres オペレーター
5.1 の使用。オペレーター
# Clone operator repo git clone https://github.com/zalando/postgres-operator.git cd postgres-operatorInstall via kubectl
kubectl apply -k kustomize/operator/
Or via Helm
helm repo add postgres-operator-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator helm install postgres-operator postgres-operator-charts/postgres-operator
5.2 をインストールします。 PostgreSQL クラスターを作成します_
# postgres-cluster.yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-postgres-cluster
namespace: postgres-ha
spec:
teamId: "myteam"
volume:
size: 10Gi
storageClass: standard
numberOfInstances: 3
users:
myapp:
- superuser
- createdb
databases:
myapp: myapp
postgresql:
version: "18"
parameters:
shared_buffers: "256MB"
max_connections: "100"
log_statement: "all"
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
patroni:
initdb:
encoding: "UTF8"
locale: "en_US.UTF-8"
data-checksums: "true"
pg_hba:
- hostssl all all 0.0.0.0/0 scram-sha-256
- host all all 0.0.0.0/0 scram-sha-256
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 33554432
backup:
schedule: "0 2 * * *"
retentionPolicy: "7d"
kubectl apply -f postgres-cluster.yaml
5.3。クラスターのステータス
kubectl get postgresql -n postgres-haNAME TEAM VERSION PODS VOLUME CPU-REQUEST MEMORY-REQUEST AGE STATUS
acid-postgres-cluster myteam 18 3 10Gi 500m 512Mi 2m Running
kubectl get pods -l cluster-name=acid-postgres-cluster -n postgres-ha
5.4 を確認します。クラスター
# Get password export PGPASSWORD=$(kubectl get secret myapp.acid-postgres-cluster.credentials.postgresql.acid.zalan.do
-n postgres-ha -o jsonpath='{.data.password}' | base64 -d)Port-forward
kubectl port-forward svc/acid-postgres-cluster 5432:5432 -n postgres-ha &
Connect
psql -h localhost -U myapp -d myapp
6 に接続します。ストレージ管理
6.1。パフォーマンスのための StorageClass
# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: postgres-fast
provisioner: kubernetes.io/aws-ebs # Or GCE, Azure, etc.
parameters:
type: gp3 # AWS EBS GP3 (faster than GP2)
iops: "3000"
throughput: "125"
fsType: ext4
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: Retain # Don't delete PV on PVC deletion
kubectl apply -f storageclass.yamlUpdate StatefulSet to use new StorageClass
volumeClaimTemplates.spec.storageClassName: postgres-fast
6.2。ボリュームの拡張_
# Enable volume expansion in StorageClassallowVolumeExpansion: true
Edit PVC
kubectl edit pvc pgdata-postgres-0 -n postgres-ha
Change: storage: 10Gi → storage: 20Gi
K8s will automatically expand the volume
kubectl get pvc -n postgres-ha -w
6.3。バックアップ ボリューム_
# Using VolumeSnapshot (if supported by storage provider)snapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshot metadata: name: postgres-0-snapshot namespace: postgres-ha spec: volumeSnapshotClassName: csi-snapclass source: persistentVolumeClaimName: pgdata-postgres-0
kubectl apply -f snapshot.yaml
kubectl get volumesnapshot -n postgres-ha
7。 Kubernetes でのモニタリング
7.1。 Prometheus ServiceMonitor
# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: postgres
namespace: postgres-ha
labels:
prometheus: kube-prometheus
spec:
selector:
matchLabels:
application: patroni
cluster-name: postgres-cluster
endpoints:
- port: patroni
path: /metrics
interval: 30s
kubectl apply -f servicemonitor.yaml
7.2。 Grafana ダッシュボード
# Import Patroni dashboardDashboard ID: 9628 (from grafana.com)
Or create custom dashboard
kubectl port-forward svc/grafana 3000:3000 -n monitoring
7.3。 Loki
# promtail-config.yaml apiVersion: v1 kind: ConfigMap metadata: name: promtail-config namespace: postgres-ha data: promtail.yaml: | server: http_listen_port: 9080 grpc_listen_port: 0clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: postgres kubernetes_sd_configs: - role: pod namespaces: names: - postgres-ha relabel_configs: - source_labels: [__meta_kubernetes_pod_label_application] action: keep regex: patroni
8 のログ。スケーリングとアップデート_
8.1。スケール クラスター_
# Scale up kubectl scale statefulset postgres --replicas=5 -n postgres-haScale down (careful!)
kubectl scale statefulset postgres --replicas=3 -n postgres-ha
8.2。ローリング アップデート
# Update PostgreSQL version kubectl set image statefulset/postgres postgres=postgres:18.1-alpine -n postgres-haOr edit StatefulSet
kubectl edit statefulset postgres -n postgres-ha
K8s will update pods one by one
kubectl rollout status statefulset/postgres -n postgres-ha
8.3。手動フェイルオーバー
# Exec into any pod kubectl exec -it postgres-0 -n postgres-ha -- bashPerform switchover
patronictl switchover postgres-cluster --master postgres-0 --candidate postgres-1
9。トラブルシューティング
9.1。ポッドが保留中
kubectl describe pod postgres-0 -n postgres-haCommon issues:
- Insufficient resources (CPU/memory)
- PVC not bound
- Node affinity rules not satisfied
9.2 のままになっています。レプリケーションが機能しない_
kubectl logs postgres-1 -n postgres-haCheck Patroni status
kubectl exec -it postgres-1 -n postgres-ha -- patronictl list
Check PostgreSQL logs
kubectl exec -it postgres-1 -n postgres-ha -- tail -f /var/lib/postgresql/data/pgdata/log/postgresql-*.log
9.3。リーダー選挙の問題
# Check Kubernetes Endpoints kubectl get endpoints -n postgres-haCheck RBAC permissions
kubectl auth can-i create endpoints --as=system:serviceaccount:postgres-ha:postgres -n postgres-ha
10。ベスト プラクティス
✅ DO
- StatefulSet を使用 - 安定したネットワーク ID
- リソースを設定する制限 - OOM キルを防止
- PV 保持を有効にする - 削除時にデータを失わない
- ヘッドレスを使用するサービス_ - StatefulSet 検出の場合
- Prometheus による監視 - 健全性の追跡
- 演算子の使用 - 簡素化管理_
- フェイルオーバーのテスト - HA を定期的に検証
- 外部ストレージへのバックアップ - S3、GCS、など_
- アンチアフィニティの使用 - ノード間でポッドを分散
- 手順を文書化 - 操作用チーム_
❌ 使用しない_
- デプロイメントを使用しない_ - 使用するStatefulSets_
- リソース制限をスキップしないでください - ノードがクラッシュする可能性があります
- PVC を削除しないでください - データについては不確実ですloss_
- ポッド アフィニティを無視しないでください - 同じノード上のすべてのポッド = 悪い
- emptyDir を使用しないでください - ポッド上のデータが失われます再起動_
- バックアップをスキップしないでください - K8s はバックアップ ソリューションではありません_
11。ラボ演習
ラボ 1: StatefulSet を使用した Patroni のデプロイ
タスク:
- 名前空間とRBAC_
- ConfigMap と Secret のデプロイ
- 3 つのレプリカを使用した StatefulSet の作成
- サービスのデプロイ_
- クラスターの確認ステータス_
ラボ 2: Kubernetes でのフェイルオーバーのテスト
タスク:
- リーダー ポッドを削除
- 自動フェイルオーバーを観察
- 新しいリーダーが選出されたことを確認
- アプリケーションの接続を確認_
- ドキュメントRTO
ラボ 3: Zalando Postgres Operator の使用
タスク:
- インストールオペレーター_
- PostgreSQL クラスターの作成 CR
- データベースの接続と作成
- クラスターのスケールアップ/ダウン
- ローリングアップデートのテスト
ラボ4: Prometheus による監視
タスク:
- Prometheus Operator のデプロイ
- 作成ServiceMonitor
- Prometheus でのメトリクスのクエリ
- Grafana ダッシュボードの作成
- アラート ルールのセットアップ
12。概要_
Kubernetes と従来型
| アスペクト | _伝統 | Kubernetes |
|---|---|---|
| DCS_ | etcd クラスター | K8s API_ |
| ストレージ_ | ローカルディスク | PVC |
| サービスディスカバリー_ | DNS/HAProxy | K8sサービス |
| スケーリング | マニュアル_ | kubectlスケール_ |
| 更新_ | 手動SSH_ | ローリング更新_ |
| モニタリング | 個別のセットアップ | ServiceMonitor |
重要な概念___HTMLTAG_406__CODEBLOCK_41___
次のステップ___HTMLTAG_408__HTMLTAG_409___レッスン 23 ではPatroni の構成について説明します管理_:
- 動的な構成変更
- DCSベースの構成ストレージ
- patronictl編集構成の使用
- ゼロダウンタイム更新_
- 構成の検証