强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 11 章:容器化部署

第 11 章:容器化部署

使用 Docker、Docker Compose 和 Kubernetes 部署和管理 rqlite 集群。


11.1 Docker 部署

11.1.1 镜像说明

rqlite 官方镜像发布在 Docker Hub:

镜像说明
rqlite/rqlite官方镜像,包含 rqlited 和 rqlite CLI
镜像大小~30MB(基于 Alpine Linux)
端口4001(HTTP API)、4002(Raft 通信)
数据目录/rqlite/file
# 拉取最新镜像
docker pull rqlite/rqlite:latest

# 查看镜像信息
docker inspect rqlite/rqlite

11.1.2 单节点运行

# 最简启动
docker run -d --name rqlite \
    -p 4001:4001 \
    rqlite/rqlite

# 带持久化
docker run -d --name rqlite \
    -p 4001:4001 \
    -p 4002:4002 \
    -v rqlite-data:/rqlite/file \
    rqlite/rqlite

# 查看日志
docker logs -f rqlite

# 验证
curl http://localhost:4001/status?pretty

11.1.3 Docker 启动参数

docker run -d --name rqlite \
    -p 4001:4001 \
    -p 4002:4002 \
    -v rqlite-data:/rqlite/file \
    rqlite/rqlite \
    rqlited \
    -node-id=docker-node1 \
    -http-addr=0.0.0.0:4001 \
    -raft-addr=0.0.0.0:4002 \
    -disco-mode=off \
    -fk \
    /rqlite/file
Docker 参数rqlite 参数说明
-p 4001:4001-http-addrHTTP API 端口映射
-p 4002:4002-raft-addrRaft 通信端口映射
-v rqlite-data:/rqlite/file数据目录持久化数据卷
--restart=unless-stopped自动重启策略

11.2 Docker Compose 集群

11.2.1 三节点集群配置

# docker-compose.yml
version: "3.8"

services:
  rqlite1:
    image: rqlite/rqlite:latest
    container_name: rqlite1
    command:
      - rqlited
      - -node-id=node1
      - -http-addr=0.0.0.0:4001
      - -raft-addr=0.0.0.0:4002
      - -disco-mode=off
      - /rqlite/file
    ports:
      - "4001:4001"
      - "4002:4002"
    volumes:
      - rqlite1-data:/rqlite/file
    networks:
      - rqlite-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:4001/status"]
      interval: 10s
      timeout: 5s
      retries: 5

  rqlite2:
    image: rqlite/rqlite:latest
    container_name: rqlite2
    command:
      - rqlited
      - -node-id=node2
      - -http-addr=0.0.0.0:4001
      - -raft-addr=0.0.0.0:4002
      - -disco-mode=off
      - -join=http://rqlite1:4001
      - /rqlite/file
    ports:
      - "4011:4001"
      - "4012:4002"
    volumes:
      - rqlite2-data:/rqlite/file
    networks:
      - rqlite-net
    depends_on:
      rqlite1:
        condition: service_healthy
    restart: unless-stopped

  rqlite3:
    image: rqlite/rqlite:latest
    container_name: rqlite3
    command:
      - rqlited
      - -node-id=node3
      - -http-addr=0.0.0.0:4001
      - -raft-addr=0.0.0.0:4002
      - -disco-mode=off
      - -join=http://rqlite1:4001
      - /rqlite/file
    ports:
      - "4021:4001"
      - "4022:4002"
    volumes:
      - rqlite3-data:/rqlite/file
    networks:
      - rqlite-net
    depends_on:
      rqlite1:
        condition: service_healthy
    restart: unless-stopped

volumes:
  rqlite1-data:
  rqlite2-data:
  rqlite3-data:

networks:
  rqlite-net:
    driver: bridge

11.2.2 启动和验证集群

# 启动集群
docker compose up -d

# 查看所有容器状态
docker compose ps

# 查看日志
docker compose logs -f

# 验证集群
curl -s http://localhost:4001/nodes?pretty | python3 -m json.tool

# 测试数据复制
curl -XPOST 'http://localhost:4001/db/execute' \
    -H 'Content-Type: application/json' \
    -d '[["CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, msg TEXT)"]]'

curl -XPOST 'http://localhost:4001/db/execute' \
    -H 'Content-Type: application/json' \
    -d '[["INSERT INTO test VALUES (1, \"hello docker\")"]]'

# 从 Follower 读取
curl -s -G 'http://localhost:4011/db/query' \
    --data-urlencode 'q=SELECT * FROM test' | python3 -m json.tool

11.2.3 带 TLS 和认证的 Compose 配置

# docker-compose-secure.yml
version: "3.8"

services:
  rqlite1:
    image: rqlite/rqlite:latest
    container_name: rqlite1
    command:
      - rqlited
      - -node-id=node1
      - -http-addr=0.0.0.0:4001
      - -raft-addr=0.0.0.0:4002
      - -http-cert=/certs/server.crt
      - -http-key=/certs/server.key
      - -node-cert=/certs/server.crt
      - -node-key=/certs/server.key
      - -auth=/config/auth.json
      - -disco-mode=off
      - /rqlite/file
    ports:
      - "4001:4001"
      - "4002:4002"
    volumes:
      - rqlite1-data:/rqlite/file
      - ./certs:/certs:ro
      - ./config:/config:ro
    networks:
      - rqlite-net
    restart: unless-stopped

# auth.json 内容
# [
#   {"username": "admin", "password": "secure_password", "perm": "all"},
#   {"username": "reader", "password": "read_pass", "perm": "ro"}
# ]

11.3 Kubernetes 部署

11.3.1 使用 StatefulSet

# k8s/rqlite-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
  name: rqlite
  labels:
    app: rqlite
spec:
  clusterIP: None
  ports:
    - name: http
      port: 4001
      targetPort: 4001
    - name: raft
      port: 4002
      targetPort: 4002
  selector:
    app: rqlite
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: rqlite
  labels:
    app: rqlite
spec:
  serviceName: rqlite
  replicas: 3
  selector:
    matchLabels:
      app: rqlite
  template:
    metadata:
      labels:
        app: rqlite
    spec:
      containers:
        - name: rqlite
          image: rqlite/rqlite:latest
          command:
            - rqlited
            - -node-id=$(NODE_ID)
            - -http-addr=0.0.0.0:4001
            - -raft-addr=0.0.0.0:4002
            - -disco-mode=off
            - -join=http://rqlite-0.rqlite.default.svc.cluster.local:4001
            - /rqlite/file
          env:
            - name: NODE_ID
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          ports:
            - name: http
              containerPort: 4001
            - name: raft
              containerPort: 4002
          volumeMounts:
            - name: data
              mountPath: /rqlite/file
          readinessProbe:
            httpGet:
              path: /status/ready
              port: http
            initialDelaySeconds: 10
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /status
              port: http
            initialDelaySeconds: 15
            periodSeconds: 20
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

11.3.2 K8s 环境下的节点加入

由于 Kubernetes StatefulSet 的 Pod 启动顺序不确定,需要处理首次启动的特殊情况:

# 使用 Init Container 处理首次启动
initContainers:
  - name: wait-for-leader
    image: busybox:latest
    command:
      - sh
      - -c
      - |
        # 如果是第一个 Pod (rqlite-0),直接启动,不等待
        if [ "$(hostname)" = "rqlite-0" ]; then
          echo "This is the first node, starting directly"
          exit 0
        fi
        
        # 等待 rqlite-0 启动
        echo "Waiting for rqlite-0 to be ready..."
        until wget -qO- http://rqlite-0.rqlite.default.svc.cluster.local:4001/status/ready; do
          sleep 2
        done
        echo "rqlite-0 is ready"

11.3.3 Headless Service 配置

Headless Service 允许 Pod 之间通过 DNS 名称互相发现:

rqlite-0.rqlite.default.svc.cluster.local  → Node 1 (Leader)
rqlite-1.rqlite.default.svc.cluster.local  → Node 2
rqlite-2.rqlite.default.svc.cluster.local  → Node 3

11.3.4 Ingress 配置

# k8s/rqlite-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rqlite
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  rules:
    - host: rqlite.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: rqlite
                port:
                  number: 4001
  tls:
    - hosts:
        - rqlite.example.com
      secretName: rqlite-tls

11.4 容器化运维技巧

11.4.1 容器内备份

# 从容器内备份
docker exec rqlite1 sh -c "wget -qO- 'http://localhost:4001/db/backup'" > backup.sql

# 使用 Docker volume 备份
docker run --rm -v rqlite1-data:/data -v $(pwd):/backup alpine \
    tar czf /backup/rqlite-data-$(date +%Y%m%d).tar.gz -C /data .

11.4.2 容器日志管理

# docker-compose.yml 中添加日志限制
services:
  rqlite1:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

11.4.3 资源限制

# Docker Compose 资源限制
services:
  rqlite1:
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 128M

11.5 业务场景:微服务配置中心

┌─────────────────────────────────────────┐
│            Kubernetes 集群              │
│                                         │
│  ┌──────────┐  ┌──────────┐            │
│  │ Web App  │  │ API Svc  │            │
│  └────┬─────┘  └────┬─────┘            │
│       │              │                  │
│       └──────┬───────┘                  │
│              │                           │
│       ┌──────▼──────┐                   │
│       │  rqlite 集群 │                   │
│       │  (3 Pod)    │                   │
│       └─────────────┘                   │
│                                         │
│  存储内容:                               │
│  - 服务配置 (feature flags)             │
│  - 白名单/黑名单                         │
│  - 系统参数 (限流阈值等)                  │
└─────────────────────────────────────────┘

配置表设计:

CREATE TABLE configs (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    type TEXT CHECK(type IN ('string', 'number', 'boolean', 'json')),
    description TEXT,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE config_history (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    key TEXT NOT NULL,
    old_value TEXT,
    new_value TEXT,
    changed_by TEXT,
    changed_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

11.6 容器化检查清单

检查项说明状态
持久化卷使用 Docker Volume 或 PV
健康检查配置 readiness/liveness probe
资源限制设置 CPU 和内存限制
日志管理限制日志大小和文件数
网络策略限制 Pod 间通信
安全上下文使用非 root 用户运行
镜像版本固定版本号,避免 latest

11.7 本章小结

要点内容
Docker 单节点docker run rqlite/rqlite 快速启动
Docker Compose推荐用于开发和小规模集群
Kubernetes使用 StatefulSet + Headless Service
持久化必须使用 Volume 持久化数据
高可用至少 3 个 Pod,使用 readiness probe
备份容器内执行或从 Volume 备份

上一章:第 10 章:客户端开发 下一章:第 12 章:监控与可观测性