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

Rekor 透明日志完整教程 / 09 - Docker 部署

第 9 章:Docker 部署

本章介绍如何使用 Docker、Docker Compose 和 Kubernetes 部署 Rekor 透明日志服务,涵盖配置管理、数据持久化和生产级部署实践。


9.1 Docker 部署概览

9.1.1 镜像列表

组件镜像用途
rekor-servergcr.io/projectsigstore/rekor-serverRekor API 服务
rekor-cligcr.io/projectsigstore/rekor-cli命令行客户端
trillian-log-servergcr.io/trillian-opensource-ci/log_serverTrillian 日志服务
trillian-log-signergcr.io/trillian-opensource-ci/log_signerTrillian 签名服务
mysqlmysql:8.0数据库后端
redisredis:7-alpine缓存

9.1.2 版本选择

# 查看 Rekor 最新版本
# https://github.com/sigstore/releases/releases

# 推荐使用的版本组合
REKOR_VERSION="v1.3.6"
TRILLIAN_VERSION="v1.6.1"
MYSQL_VERSION="8.0"
REDIS_VERSION="7-alpine"

9.2 Docker Compose 快速部署

9.2.1 目录结构

rekor-deploy/
├── docker-compose.yml
├── config/
│   ├── rekor/
│   │   └── rekor-config.yaml
│   └── mysql/
│       └── init.sql
├── data/
│   ├── mysql/
│   └── redis/
├── scripts/
│   ├── init-tree.sh
│   └── healthcheck.sh
└── certs/
    ├── server.crt
    └── server.key

9.2.2 Docker Compose 配置

# docker-compose.yml
version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: rekor-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root_password}
      MYSQL_DATABASE: trillian
      MYSQL_USER: trillian
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-trillian_password}
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./config/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --innodb-buffer-pool-size=1G
      - --innodb-log-file-size=256M
      - --max-connections=200
      - --slow-query-log=ON
      - --long-query-time=1
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-root_password}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - rekor-net

  redis:
    image: redis:7-alpine
    container_name: rekor-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - rekor-net

  trillian-log-server:
    image: gcr.io/trillian-opensource-ci/log_server:${TRILLIAN_VERSION:-v1.6.1}
    container_name: trillian-log-server
    restart: always
    depends_on:
      mysql:
        condition: service_healthy
    ports:
      - "8090:8090"
      - "8091:8091"
    command:
      - --mysql_uri=trillian:trillian_password@tcp(mysql:3306)/trillian
      - --rpc_endpoint=0.0.0.0:8090
      - --http_endpoint=0.0.0.0:8091
      - --logtostderr
    networks:
      - rekor-net

  trillian-log-signer:
    image: gcr.io/trillian-opensource-ci/log_signer:${TRILLIAN_VERSION:-v1.6.1}
    container_name: trillian-log-signer
    restart: always
    depends_on:
      mysql:
        condition: service_healthy
      trillian-log-server:
        condition: service_started
    ports:
      - "8092:8092"
      - "8093:8093"
    command:
      - --mysql_uri=trillian:trillian_password@tcp(mysql:3306)/trillian
      - --rpc_endpoint=0.0.0.0:8092
      - --http_endpoint=0.0.0.0:8093
      - --logtostderr
      - --force_master
    networks:
      - rekor-net

  rekor-server:
    image: gcr.io/projectsigstore/rekor-server:${REKOR_VERSION:-v1.3.6}
    container_name: rekor-server
    restart: always
    depends_on:
      trillian-log-server:
        condition: service_started
      redis:
        condition: service_healthy
    ports:
      - "3000:3000"
    command:
      - serve
      - --trillian_log_server.address=trillian-log-server
      - --trillian_log_server.port=8090
      - --rekor_server.address=0.0.0.0
      - --rekor_server.port=3000
      - --redis_server.address=redis
      - --redis_server.port=6379
      - --log_type=log
      - --log_level=info
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/v1/log"]
      interval: 15s
      timeout: 10s
      retries: 3
    networks:
      - rekor-net

  init-tree:
    image: gcr.io/trillian-opensource-ci/log_server:${TRILLIAN_VERSION:-v1.6.1}
    container_name: init-tree
    depends_on:
      trillian-log-server:
        condition: service_started
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        echo "Waiting for Trillian to be ready..."
        sleep 10
        echo "Creating tree..."
        # createtree 需要在 log_server 容器中执行
        echo "Tree creation should be done manually after first start"
        echo "Use: docker exec trillian-log-server createtree --admin_server=trillian-log-server:8090 --tree_type=LOG"
    networks:
      - rekor-net

volumes:
  mysql-data:
    driver: local
  redis-data:
    driver: local

networks:
  rekor-net:
    driver: bridge

9.2.3 MySQL 初始化脚本

-- config/mysql/init.sql
-- Trillian 数据库初始化

CREATE DATABASE IF NOT EXISTS trillian;
USE trillian;

-- Trillian 存储表结构
-- 注意:Trillian 会自动创建表,此脚本仅用于预配置

-- 创建 Trillian 用户
CREATE USER IF NOT EXISTS 'trillian'@'%' IDENTIFIED BY 'trillian_password';
GRANT ALL PRIVILEGES ON trillian.* TO 'trillian'@'%';
FLUSH PRIVILEGES;

-- 优化配置
SET GLOBAL innodb_buffer_pool_size = 1073741824;  -- 1G
SET GLOBAL max_connections = 200;

9.2.4 环境变量文件

# .env
REKOR_VERSION=v1.3.6
TRILLIAN_VERSION=v1.6.1
MYSQL_ROOT_PASSWORD=your_secure_root_password
MYSQL_PASSWORD=your_secure_trillian_password

9.2.5 启动与初始化

# 克隆部署文件
mkdir rekor-deploy && cd rekor-deploy

# 创建所需目录
mkdir -p config/mysql data/mysql data/redis scripts certs

# 启动所有服务
docker compose up -d

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f rekor-server
docker compose logs -f trillian-log-server
docker compose logs -f mysql

# 初始化 Trillian Tree
sleep 30  # 等待 MySQL 和 Trillian 完全启动
docker exec trillian-log-server createtree \
  --admin_server=trillian-log-server:8090 \
  --tree_type=LOG

# 记录返回的 Tree ID,更新 rekor-server 配置

# 验证 Rekor 服务
curl -s http://localhost:3000/api/v1/log | jq '.'

9.3 完整部署脚本

9.3.1 自动化部署脚本

#!/bin/bash
# scripts/deploy-rekor.sh
# Rekor Docker Compose 自动化部署脚本

set -euo pipefail

DEPLOY_DIR="${1:-./rekor-deploy}"
REKOR_VERSION="${REKOR_VERSION:-v1.3.6}"
TRILLIAN_VERSION="${TRILLIAN_VERSION:-v1.6.1}"
MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-$(openssl rand -base64 32)}"
MYSQL_PASSWORD="${MYSQL_PASSWORD:-$(openssl rand -base64 32)}"

echo "=== Rekor Docker 部署 ==="
echo "部署目录: $DEPLOY_DIR"
echo "Rekor 版本: $REKOR_VERSION"
echo "Trillian 版本: $TRILLIAN_VERSION"

# 创建目录结构
mkdir -p "$DEPLOY_DIR"/{config/mysql,data/mysql,data/redis,scripts,certs}
cd "$DEPLOY_DIR"

# 生成 .env 文件
cat > .env << EOF
REKOR_VERSION=$REKOR_VERSION
TRILLIAN_VERSION=$TRILLIAN_VERSION
MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
MYSQL_PASSWORD=$MYSQL_PASSWORD
EOF

# 生成 docker-compose.yml(同上)
# ... 省略,参考 9.2.2 节 ...

# 启动服务
echo ">>> 启动服务..."
docker compose up -d

# 等待 MySQL 就绪
echo ">>> 等待 MySQL 就绪..."
until docker compose exec mysql mysqladmin ping -h localhost -u root -p"$MYSQL_ROOT_PASSWORD" --silent; do
  sleep 5
done

# 等待 Trillian 就绪
echo ">>> 等待 Trillian 就绪..."
sleep 20

# 创建日志树
echo ">>> 创建 Trillian 日志树..."
TREE_ID=$(docker exec trillian-log-server createtree \
  --admin_server=trillian-log-server:8090 \
  --tree_type=LOG 2>&1 | grep -oP 'tree ID: \K[0-9]+')

if [ -z "$TREE_ID" ]; then
  echo "错误:无法创建日志树"
  exit 1
fi

echo "日志树 ID: $TREE_ID"

# 等待 Rekor 就绪
echo ">>> 等待 Rekor 就绪..."
sleep 10

# 验证部署
echo ">>> 验证部署..."
REKOR_INFO=$(curl -s http://localhost:3000/api/v1/log 2>/dev/null || echo '{}')
if echo "$REKOR_INFO" | jq -e '.treeSize' > /dev/null 2>&1; then
  echo "✅ Rekor 部署成功"
  echo "   API 地址: http://localhost:3000"
  echo "   日志树 ID: $TREE_ID"
  echo "   树大小: $(echo "$REKOR_INFO" | jq -r '.treeSize')"
else
  echo "⚠️  Rekor 服务可能还在启动中,请稍后验证"
fi

echo "=== 部署完成 ==="

9.3.2 健康检查脚本

#!/bin/bash
# scripts/healthcheck.sh
# Rekor 服务健康检查

set -euo pipefail

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

check_service() {
  local name="$1"
  local url="$2"
  local expected="${3:-200}"
  
  local status=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
  
  if [ "$status" = "$expected" ]; then
    echo -e "${GREEN}${NC} $name (HTTP $status)"
    return 0
  else
    echo -e "${RED}${NC} $name (HTTP $status)"
    return 1
  fi
}

check_docker() {
  local name="$1"
  
  if docker compose ps "$name" 2>/dev/null | grep -q "Up"; then
    echo -e "${GREEN}${NC} $name (running)"
    return 0
  else
    echo -e "${RED}${NC} $name (not running)"
    return 1
  fi
}

echo "=== Rekor 健康检查 ==="
echo ""

ERRORS=0

# Docker 服务状态
echo ">>> Docker 服务状态"
check_docker "rekor-mysql" || ((ERRORS++))
check_docker "rekor-redis" || ((ERRORS++))
check_docker "trillian-log-server" || ((ERRORS++))
check_docker "trillian-log-signer" || ((ERRORS++))
check_docker "rekor-server" || ((ERRORS++))

echo ""

# HTTP 端点
echo ">>> HTTP 端点检查"
check_service "Rekor API" "http://localhost:3000/api/v1/log" || ((ERRORS++))
check_service "Trillian HTTP" "http://localhost:8091/debug/vars" || ((ERRORS++))

echo ""

# API 功能测试
echo ">>> API 功能测试"
LOG_INFO=$(curl -s http://localhost:3000/api/v1/log 2>/dev/null)
if echo "$LOG_INFO" | jq -e '.treeSize' > /dev/null 2>&1; then
  TREE_SIZE=$(echo "$LOG_INFO" | jq -r '.treeSize')
  echo -e "${GREEN}${NC} 日志树大小: $TREE_SIZE"
else
  echo -e "${RED}${NC} 无法获取日志树信息"
  ((ERRORS++))
fi

echo ""
if [ $ERRORS -eq 0 ]; then
  echo -e "${GREEN}=== 所有检查通过 ===${NC}"
else
  echo -e "${RED}=== 发现 $ERRORS 个错误 ===${NC}"
  exit 1
fi

9.4 配置管理

9.4.1 环境变量配置

变量说明默认值
REKOR_VERSIONRekor 版本v1.3.6
TRILLIAN_VERSIONTrillian 版本v1.6.1
MYSQL_ROOT_PASSWORDMySQL root 密码必须设置
MYSQL_PASSWORDTrillian 用户密码必须设置
TRILLIAN_TREE_ID日志树 ID首次启动时创建
REDIS_URLRedis 连接 URLredis://localhost:6379
LOG_LEVEL日志级别info

9.4.2 使用 Docker Secrets

对于敏感配置,推荐使用 Docker Secrets:

# docker-compose.yml (Swarm Mode)
version: '3.8'

secrets:
  mysql_root_password:
    file: ./secrets/mysql_root_password.txt
  mysql_password:
    file: ./secrets/mysql_password.txt

services:
  mysql:
    image: mysql:8.0
    secrets:
      - mysql_root_password
      - mysql_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
      MYSQL_PASSWORD_FILE: /run/secrets/mysql_password

9.4.3 自定义 Rekor 配置

通过环境变量覆盖 Rekor Server 默认配置:

# docker-compose.yml 中的 rekor-server 服务
rekor-server:
  image: gcr.io/projectsigstore/rekor-server:v1.3.6
  environment:
    # Trillian 连接
    TRILLIAN_LOG_SERVER_ADDRESS: trillian-log-server
    TRILLIAN_LOG_SERVER_PORT: "8090"
    # Rekor 服务
    REKOR_SERVER_ADDRESS: "0.0.0.0"
    REKOR_SERVER_PORT: "3000"
    # Redis
    REDIS_SERVER_ADDRESS: redis
    REDIS_SERVER_PORT: "6379"
    # 日志
    LOG_TYPE: log
    LOG_LEVEL: info

9.5 数据持久化

9.5.1 Volume 配置

# docker-compose.yml 中的 volumes 定义
volumes:
  mysql-data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /opt/rekor/data/mysql  # 宿主机路径

  redis-data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /opt/rekor/data/redis

  # 如果使用本地文件存储证明数据
  rekor-attestations:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /opt/rekor/data/attestations

9.5.2 备份与恢复

#!/bin/bash
# scripts/backup.sh

set -euo pipefail

BACKUP_DIR="./backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"

echo "=== Rekor 数据备份 ==="

# 备份 MySQL
echo ">>> 备份 MySQL..."
docker compose exec -T mysql mysqldump \
  -u root -p"${MYSQL_ROOT_PASSWORD}" \
  --single-transaction \
  --routines \
  --triggers \
  trillian | gzip > "$BACKUP_DIR/trillian.sql.gz"

# 备份 Redis
echo ">>> 备份 Redis..."
docker compose exec -T redis redis-cli BGSAVE
sleep 5
docker compose exec -T redis cat /data/dump.rdb > "$BACKUP_DIR/redis.rdb"

# 备份配置
echo ">>> 备份配置文件..."
cp .env "$BACKUP_DIR/"
cp docker-compose.yml "$BACKUP_DIR/"

# 生成备份清单
cat > "$BACKUP_DIR/manifest.txt" << EOF
备份时间: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
MySQL 备份: trillian.sql.gz
Redis 备份: redis.rdb
配置文件: .env, docker-compose.yml
EOF

echo "=== 备份完成: $BACKUP_DIR ==="

恢复脚本:

#!/bin/bash
# scripts/restore.sh

set -euo pipefail

BACKUP_DIR="$1"

if [ -z "$BACKUP_DIR" ] || [ ! -d "$BACKUP_DIR" ]; then
  echo "用法: $0 <备份目录>"
  exit 1
fi

echo "=== Rekor 数据恢复 ==="
echo "从 $BACKUP_DIR 恢复"

# 停止服务
echo ">>> 停止服务..."
docker compose stop rekor-server trillian-log-signer trillian-log-server

# 恢复 MySQL
echo ">>> 恢复 MySQL..."
gunzip -c "$BACKUP_DIR/trillian.sql.gz" | docker compose exec -T mysql mysql \
  -u root -p"${MYSQL_ROOT_PASSWORD}" trillian

# 恢复 Redis
echo ">>> 恢复 Redis..."
docker compose stop redis
docker cp "$BACKUP_DIR/redis.rdb" rekor-redis:/data/dump.rdb
docker compose start redis

# 重启服务
echo ">>> 重启服务..."
docker compose start trillian-log-server trillian-log-signer rekor-server

echo "=== 恢复完成 ==="

9.6 Kubernetes 部署

9.6.1 Helm Chart

使用 Helm Chart 部署 Rekor 到 Kubernetes:

# 添加 Sigstore Helm 仓库(如果有官方 chart)
# helm repo add sigstore https://sigstore.github.io/helm-charts

# 或者使用自定义 Helm Chart
mkdir -p rekor-helm/{templates,charts}

9.6.2 Kubernetes 清单文件

# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: rekor-system
# k8s/mysql.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: rekor-system
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: root-password
            - name: MYSQL_DATABASE
              value: trillian
            - name: MYSQL_USER
              value: trillian
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
          volumeMounts:
            - name: mysql-data
              mountPath: /var/lib/mysql
          resources:
            requests:
              memory: "1Gi"
              cpu: "500m"
            limits:
              memory: "4Gi"
              cpu: "2000m"
          readinessProbe:
            exec:
              command:
                - mysqladmin
                - ping
                - -h
                - localhost
            initialDelaySeconds: 30
            periodSeconds: 10
  volumeClaimTemplates:
    - metadata:
        name: mysql-data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 50Gi
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: rekor-system
spec:
  selector:
    app: mysql
  ports:
    - port: 3306
      targetPort: 3306
  clusterIP: None
# k8s/redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: rekor-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          ports:
            - containerPort: 6379
          command: ["redis-server", "--appendonly", "yes", "--maxmemory", "512mb"]
          volumeMounts:
            - name: redis-data
              mountPath: /data
          resources:
            requests:
              memory: "256Mi"
              cpu: "100m"
            limits:
              memory: "1Gi"
              cpu: "500m"
          readinessProbe:
            exec:
              command: ["redis-cli", "ping"]
            initialDelaySeconds: 5
            periodSeconds: 10
      volumes:
        - name: redis-data
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: rekor-system
spec:
  selector:
    app: redis
  ports:
    - port: 6379
      targetPort: 6379
# k8s/trillian.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: trillian-log-server
  namespace: rekor-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app: trillian-log-server
  template:
    metadata:
      labels:
        app: trillian-log-server
    spec:
      initContainers:
        - name: wait-mysql
          image: busybox:1.36
          command: ['sh', '-c', 'until nc -z mysql 3306; do echo waiting for mysql; sleep 5; done']
      containers:
        - name: trillian-log-server
          image: gcr.io/trillian-opensource-ci/log_server:v1.6.1
          args:
            - --mysql_uri=trillian:$(MYSQL_PASSWORD)@tcp(mysql:3306)/trillian
            - --rpc_endpoint=0.0.0.0:8090
            - --http_endpoint=0.0.0.0:8091
            - --logtostderr
          ports:
            - containerPort: 8090
              name: rpc
            - containerPort: 8091
              name: http
          env:
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
---
apiVersion: v1
kind: Service
metadata:
  name: trillian-log-server
  namespace: rekor-system
spec:
  selector:
    app: trillian-log-server
  ports:
    - port: 8090
      name: rpc
    - port: 8091
      name: http
# k8s/rekor-server.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rekor-server
  namespace: rekor-system
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rekor-server
  template:
    metadata:
      labels:
        app: rekor-server
    spec:
      initContainers:
        - name: wait-trillian
          image: busybox:1.36
          command: ['sh', '-c', 'until nc -z trillian-log-server 8090; do echo waiting for trillian; sleep 5; done']
      containers:
        - name: rekor-server
          image: gcr.io/projectsigstore/rekor-server:v1.3.6
          args:
            - serve
            - --trillian_log_server.address=trillian-log-server
            - --trillian_log_server.port=8090
            - --rekor_server.address=0.0.0.0
            - --rekor_server.port=3000
            - --redis_server.address=redis
            - --redis_server.port=6379
            - --log_type=log
            - --log_level=info
          ports:
            - containerPort: 3000
              name: http
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
          livenessProbe:
            httpGet:
              path: /api/v1/log
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 15
          readinessProbe:
            httpGet:
              path: /api/v1/log
              port: 3000
            initialDelaySeconds: 20
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: rekor-server
  namespace: rekor-system
spec:
  selector:
    app: rekor-server
  ports:
    - port: 3000
      targetPort: 3000
      name: http
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rekor-server
  namespace: rekor-system
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - rekor.internal.example.com
      secretName: rekor-tls
  rules:
    - host: rekor.internal.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: rekor-server
                port:
                  number: 3000

9.6.3 Kubernetes Secrets

# k8s/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
  namespace: rekor-system
type: Opaque
data:
  root-password: <base64-encoded-root-password>
  password: <base64-encoded-trillian-password>
# 创建 Secret
kubectl create secret generic mysql-secret \
  --namespace=rekor-system \
  --from-literal=root-password='your_root_password' \
  --from-literal=password='your_trillian_password'

9.6.4 部署命令

# 创建命名空间
kubectl apply -f k8s/namespace.yaml

# 创建 Secret
kubectl create secret generic mysql-secret \
  --namespace=rekor-system \
  --from-literal=root-password='your_root_password' \
  --from-literal=password='your_trillian_password'

# 部署组件
kubectl apply -f k8s/mysql.yaml
kubectl apply -f k8s/redis.yaml
kubectl apply -f k8s/trillian.yaml

# 等待 MySQL 和 Trillian 就绪
kubectl -n rekor-system wait --for=condition=ready pod -l app=mysql --timeout=120s
kubectl -n rekor-system wait --for=condition=ready pod -l app=trillian-log-server --timeout=60s

# 部署 Rekor
kubectl apply -f k8s/rekor-server.yaml

# 查看状态
kubectl -n rekor-system get pods
kubectl -n rekor-system get services
kubectl -n rekor-system get ingress

9.7 Docker 网络配置

9.7.1 网络拓扑

┌─────────────────────────────────────────────────────┐
│                   Docker Network                     │
│                   (rekor-net)                        │
│                                                      │
│   ┌──────────┐  ┌──────────┐  ┌──────────┐         │
│   │  MySQL   │  │  Redis   │  │ Trillian │         │
│   │ :3306    │  │ :6379    │  │ :8090    │         │
│   └────┬─────┘  └────┬─────┘  └────┬─────┘         │
│        │              │              │               │
│        └──────────────┼──────────────┘               │
│                       │                              │
│                  ┌────▼─────┐                        │
│                  │  Rekor   │                        │
│                  │ :3000    │                        │
│                  └────┬─────┘                        │
│                       │                              │
└───────────────────────┼──────────────────────────────┘
                        │
                  ┌─────▼─────┐
                  │  Host     │
                  │ :3000     │
                  └───────────┘

9.7.2 自定义网络

# docker-compose.yml
networks:
  rekor-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.1
  
  # 管理网络(用于监控)
  management-net:
    driver: bridge

9.8 日志管理

9.8.1 Docker 日志配置

# docker-compose.yml
services:
  rekor-server:
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"
        tag: "{{.Name}}"

9.8.2 集中日志收集

# 使用 Loki 收集日志
services:
  rekor-server:
    labels:
      - "logging=promtail"
      - "logging_jobname=rekor"
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"

9.9 注意事项

首次启动初始化:第一次启动时需要手动创建 Trillian 日志树。确保在 rekor-server 启动前完成。

数据库迁移:升级 Rekor 版本时,检查是否有数据库 schema 变更。

资源限制:根据实际负载调整容器资源限制。MySQL 和 Rekor 是资源敏感型服务。

健康检查:所有服务都应配置健康检查,便于 Kubernetes 或 Docker Swarm 进行自动重启。

网络安全:不要将 MySQL、Redis、Trillian RPC 端口暴露到公网。仅通过 rekor-server 对外提供服务。


9.10 本章小结

部署方式适用场景复杂度可扩展性
Docker Compose开发/测试/小规模⭐⭐
Kubernetes生产环境⭐⭐⭐⭐
传统 VM遗留环境⭐⭐⭐

扩展阅读


下一章10 - 最佳实践 — 供应链安全策略、签名策略、监控和企业部署最佳实践。