Chuyển đến nội dung chính

レッスン 22: Kubernetes を使用した Patroni

Patroni オペレーター、StatefulSet、永続ボリューム、Helm チャートを使用して Kubernetes に Patroni をデプロイします。

🔒 DevSecOps — レッスン 22 レッスン 22: Kubernetes を使用した Patroni__HTMLTAG_53___

Patroni と PostgreSQL の高可用性etcd

パート 5: セキュリティと上級_

xdev.asia_

目標

このレッスンの後、次のことを行います:

  • 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/kind

Create 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/kubectl

Verify

kubectl version --client kubectl cluster-info

2.3。ヘルム (オプション)

# Install Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

Verify

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.yaml

Headless 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 -w

Output:

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-ha

NAME 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 -- bash

Inside 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 postgres

Create 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-operator

Install 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-ha

NAME 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.yaml

Update StatefulSet to use new StorageClass

volumeClaimTemplates.spec.storageClassName: postgres-fast

6.2。ボリュームの拡張_

# Enable volume expansion in StorageClass

allowVolumeExpansion: 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 dashboard

Dashboard 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: 0

clients:
  - 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-ha

Scale 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-ha

Or 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 -- bash

Perform switchover

patronictl switchover postgres-cluster --master postgres-0 --candidate postgres-1

9。トラブルシューティング

9.1。ポッドが保留中

kubectl describe pod postgres-0 -n postgres-ha

Common issues:

- Insufficient resources (CPU/memory)

- PVC not bound

- Node affinity rules not satisfied

9.2 のままになっています。レプリケーションが機能しない_

kubectl logs postgres-1 -n postgres-ha

Check 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-ha

Check RBAC permissions

kubectl auth can-i create endpoints --as=system:serviceaccount:postgres-ha:postgres -n postgres-ha

10。ベスト プラクティス

✅ DO

  1. StatefulSet を使用 - 安定したネットワーク ID
  2. リソースを設定する制限 - OOM キルを防止
  3. PV 保持を有効にする - 削除時にデータを失わない
  4. ヘッドレスを使用するサービス_ - StatefulSet 検出の場合
  5. Prometheus による監視 - 健全性の追跡
  6. 演算子の使用 - 簡素化管理_
  7. フェイルオーバーのテスト - HA を定期的に検証
  8. 外部ストレージへのバックアップ - S3、GCS、など_
  9. アンチアフィニティの使用 - ノード間でポッドを分散
  10. 手順を文書化 - 操作用チーム_

❌ 使用しない_

  1. デプロイメントを使用しない_ - 使用するStatefulSets_
  2. リソース制限をスキップしないでください - ノードがクラッシュする可能性があります
  3. PVC を削除しないでください - データについては不確実ですloss_
  4. ポッド アフィニティを無視しないでください - 同じノード上のすべてのポッド = 悪い
  5. emptyDir を使用しないでください - ポッド上のデータが失われます再起動_
  6. バックアップをスキップしないでください - K8s はバックアップ ソリューションではありません_

11。ラボ演習

ラボ 1: StatefulSet を使用した Patroni のデプロイ

タスク:

  1. 名前空間とRBAC_
  2. ConfigMap と Secret のデプロイ
  3. 3 つのレプリカを使用した StatefulSet の作成
  4. サービスのデプロイ_
  5. クラスターの確認ステータス_

ラボ 2: Kubernetes でのフェイルオーバーのテスト

タスク:

  1. リーダー ポッドを削除
  2. 自動フェイルオーバーを観察
  3. 新しいリーダーが選出されたことを確認
  4. アプリケーションの接続を確認_
  5. ドキュメントRTO

ラボ 3: Zalando Postgres Operator の使用

タスク:

  1. インストールオペレーター_
  2. PostgreSQL クラスターの作成 CR
  3. データベースの接続と作成
  4. クラスターのスケールアップ/ダウン
  5. ローリングアップデートのテスト

ラボ4: Prometheus による監視

タスク:

  1. Prometheus Operator のデプロイ
  2. 作成ServiceMonitor
  3. Prometheus でのメトリクスのクエリ
  4. Grafana ダッシュボードの作成
  5. アラート ルールのセットアップ

12。概要_

Kubernetes と従来型

アスペクト_伝統 Kubernetes
DCS_etcd クラスターK8s API_
ストレージ_ローカルディスクPVC
サービスディスカバリー_DNS/HAProxyK8sサービス
スケーリングマニュアル_kubectlスケール_
更新_手動SSH_ローリング更新_
モニタリング個別のセットアップServiceMonitor

重要な概念___HTMLTAG_406__CODEBLOCK_41___

次のステップ___HTMLTAG_408__HTMLTAG_409___レッスン 23 ではPatroni の構成について説明します管理_:

  • 動的な構成変更
  • DCSベースの構成ストレージ
  • patronictl編集構成の使用
  • ゼロダウンタイム更新_
  • 構成の検証