Rekor 透明日志完整教程 / 09 - Docker 部署
第 9 章:Docker 部署
本章介绍如何使用 Docker、Docker Compose 和 Kubernetes 部署 Rekor 透明日志服务,涵盖配置管理、数据持久化和生产级部署实践。
9.1 Docker 部署概览
9.1.1 镜像列表
| 组件 | 镜像 | 用途 |
|---|---|---|
| rekor-server | gcr.io/projectsigstore/rekor-server | Rekor API 服务 |
| rekor-cli | gcr.io/projectsigstore/rekor-cli | 命令行客户端 |
| trillian-log-server | gcr.io/trillian-opensource-ci/log_server | Trillian 日志服务 |
| trillian-log-signer | gcr.io/trillian-opensource-ci/log_signer | Trillian 签名服务 |
| mysql | mysql:8.0 | 数据库后端 |
| redis | redis: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_VERSION | Rekor 版本 | v1.3.6 |
TRILLIAN_VERSION | Trillian 版本 | v1.6.1 |
MYSQL_ROOT_PASSWORD | MySQL root 密码 | 必须设置 |
MYSQL_PASSWORD | Trillian 用户密码 | 必须设置 |
TRILLIAN_TREE_ID | 日志树 ID | 首次启动时创建 |
REDIS_URL | Redis 连接 URL | redis://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 - 最佳实践 — 供应链安全策略、签名策略、监控和企业部署最佳实践。