跳至主要内容 跳至导航 跳至页脚
限时:设计合作伙伴计划 — 永久享有 BUSINESS 套餐

服务

使用 Rediaccfile、服务网络和开机自启来部署和管理容器化服务。

服务

如果你不确定该使用哪个工具,请参考 rdc vs renet

本页面介绍如何部署和管理容器化服务:Rediaccfile、服务网络、启动/停止、批量操作和开机自启。

Rediaccfile

Rediaccfile 是一个 Bash 脚本,用于定义服务的启动和停止方式。文件必须命名为 Rediaccfilerediaccfile(不区分大小写),并放置在仓库的已挂载文件系统中。

Rediaccfile 在两个位置被发现:

  1. 仓库挂载路径的根目录
  2. 挂载路径的第一级子目录(不递归)

隐藏目录(名称以 . 开头)将被跳过。

生命周期函数

一个 Rediaccfile 最多包含两个函数:

函数运行时机用途错误行为
up()启动时启动服务(例如 renet compose -- up -d根 Rediaccfile 失败是关键性的(停止一切)。子目录失败是非关键性的(记录日志,继续下一个)。
down()停止时停止服务(例如 renet compose -- down尽力而为 — 失败会被记录,但所有 Rediaccfile 都会被尝试执行。

两个函数都是可选的。如果某个函数未在 Rediaccfile 中定义,它会被静默跳过。

执行顺序

  • 启动(up): 先执行根 Rediaccfile,然后按字母顺序(A 到 Z)执行子目录。
  • 停止(down): 先按反向字母顺序(Z 到 A)执行子目录,最后执行根 Rediaccfile。

环境变量

当 Rediaccfile 函数执行时,以下环境变量可用:

变量描述示例
REDIACC_WORKING_DIR仓库的挂载路径/mnt/rediacc/mounts/abc123
REDIACC_REPOSITORY仓库 GUIDa1b2c3d4-e5f6-...
REDIACC_NETWORK_ID网络 ID(整数)2816
DOCKER_HOST此仓库隔离守护进程的 Docker 套接字unix:///var/run/rediacc/docker-2816.sock
{SERVICE}_IP.rediacc.json 中定义的每个服务的回环 IPPOSTGRES_IP=127.0.11.2

{SERVICE}_IP 变量从 .rediacc.json 自动生成。命名规则是将服务名称转换为大写,连字符替换为下划线,然后追加 _IP。例如,listmonk-app 变为 LISTMONK_APP_IP

警告:请勿在 Rediaccfile 中使用 sudo docker sudo 命令会重置环境变量,导致 DOCKER_HOST 丢失,Docker 命令将指向系统守护进程而非仓库的隔离守护进程。这会破坏容器隔离并可能导致端口冲突。如果 Rediacc 检测到未带 -Esudo docker,将阻止执行。

在 Rediaccfile 中请使用 renet compose, 它会自动处理 DOCKER_HOST,注入路由发现所需的网络标签,并配置服务网络。有关服务如何通过反向代理暴露的详细信息,请参阅网络。如果直接调用 Docker,请使用不带 sudodocker, Rediaccfile 函数已经拥有足够的权限。如果必须使用 sudo,请使用 sudo -E docker 来保留环境变量。

示例

#!/bin/bash

up() {
    echo "Starting services..."
    renet compose -- up -d
}

down() {
    echo "Stopping services..."
    renet compose -- down
}

重要: 请始终使用 renet compose -- 而非 docker composerenet compose 封装器强制实施主机网络、IP 分配以及 renet-proxy 所需的服务发现标签。CRIU 检查点/恢复功能会添加到带有 rediacc.checkpoint=true 标签的容器。直接使用 docker compose 会被 Rediaccfile 验证拒绝。详情请参阅网络

多服务布局

对于包含多个独立服务组的项目,使用子目录:

/mnt/rediacc/mounts/my-app/
├── Rediaccfile              # 根目录:共享设置
├── docker-compose.yml
├── database/
│   ├── Rediaccfile          # 数据库服务
│   └── docker-compose.yml
├── backend/
│   ├── Rediaccfile          # API 服务器
│   └── docker-compose.yml
└── monitoring/
    ├── Rediaccfile          # Prometheus、Grafana 等
    └── docker-compose.yml

up 执行顺序:根目录,然后 backenddatabasemonitoring(A-Z)。 down 执行顺序:monitoringdatabasebackend,然后根目录(Z-A)。

服务网络(.rediacc.json)

每个仓库获得一个 /26 子网(64 个 IP),位于 127.x.x.x 回环地址范围内。服务绑定到唯一的回环 IP,因此它们可以使用相同的端口而不会冲突。

.rediacc.json 文件

将服务名称映射到槽位编号。每个槽位对应仓库子网内的一个唯一 IP 地址。

{
  "services": {
    "api": {"slot": 0},
    "postgres": {"slot": 1},
    "redis": {"slot": 2}
  }
}

从 Docker Compose 自动生成

您无需手动创建 .rediacc.json。当您运行 rdc repo up 时,Rediacc 会自动:

  1. 扫描所有包含 Rediaccfile 的目录中的 compose 文件(docker-compose.ymldocker-compose.yamlcompose.ymlcompose.yaml)。
  2. 从每个 compose 文件的 services: 部分提取服务名称。
  3. 为任何新服务分配下一个可用槽位。
  4. 将结果保存到 {repository}/.rediacc.json

IP 计算

服务的 IP 根据仓库的网络 ID 和服务的槽位计算。网络 ID 分布在 127.x.y.z 回环地址的第二、第三和第四个八位组中。每个服务会在网络 ID 的基础上加上 slot + 2 的偏移量(偏移量 0 和 1 保留给网络地址和网关)。

OffsetAddressPurpose
.0127.0.11.0Network address (reserved)
.1127.0.11.1Gateway (reserved)
.2 – .62127.0.11.2127.0.11.62Services (slot + 2)
.63127.0.11.63Broadcast (reserved)

示例:网络 ID 为 28160x0B00)时,基础地址为 127.0.11.0

服务槽位IP 地址
api0127.0.11.2
postgres1127.0.11.3
redis2127.0.11.4

每个仓库最多支持 61 个服务(槽位 0 到 60)。

在 Docker Compose 中使用服务 IP

由于每个仓库运行隔离的 Docker 守护进程,renet compose 会自动为所有服务配置 network_mode: host。内核透明地将 bind() 调用重写到服务分配的回环 IP,因此服务可以绑定到 0.0.0.0localhost 而不会产生冲突。对于连接到其他服务,请使用服务名称 — renet 将每个服务名称作为主机名注入,始终解析到正确的 IP,即使在 fork 中也是如此:

services:
  postgres:
    image: postgres:16
    environment:
      PGDATA: /var/lib/postgresql/data
      POSTGRES_PASSWORD: secret
    # 无需显式 listen_addresses -- 内核将 bind 重写到正确的回环 IP

  api:
    image: my-api:latest
    environment:
      DATABASE_URL: postgresql://postgres:secret@postgres:5432/mydb  # 使用服务名称
      LISTEN_ADDR: 0.0.0.0:8080                                      # 内核重写到服务 IP

连接用服务名称: 使用服务名称(如 postgresredis连接到其他服务 — renet 通过 /etc/hosts 自动将每个服务名称映射到其回环 IP。在数据库或配置文件中存储的连接字符串中嵌入 ${POSTGRES_IP} 会固化原始 IP,这会破坏 fork 隔离并且是验证错误${SERVICE_IP} 变量仍可用于显式使用,但绑定由内核自动处理。

注意: 不要手动添加 network_mode: host, renet compose 会自动注入。重启策略(如 restart: always)可以安全使用, renet 会为了 CRIU 兼容性自动移除它们,路由器看门狗负责容器恢复。

容器恢复与重启策略

renet 和 Docker 有意在如何处理容器重启方面存在分歧。在调试容器为什么恢复或未能恢复时,理解这种分工很重要。

重启策略转换。 当你在 compose 文件中写入 restart: always(或 unless-stopped,或 on-failure)时,renet 在合成实际 compose 部署时会删除它,并用 restart: no 替换。原始值保存到仓库的 .rediacc.jsonservices.<name>.restart_policy 下。这可以防止 Docker 守护进程级别的自动重启干扰 CRIU 检查点/恢复(守护进程驱动的重启会从过时的检查点前状态恢复)。

看门狗执行。 路由器看门狗在每台机器上定期运行。每次滴答时:

  1. 读取每个仓库的 .rediacc.json,找到具有可恢复 restart_policy 的服务。
  2. 列出该仓库守护进程的所有容器,识别已停止的容器,并按保存的策略重启它们。30 秒的宽限期防止与刚刚运行 docker stop 的操作员发生冲突。
  3. 同一循环还处理 /var/run/rediacc/cold-backup-<guid>.running.json(参见冷备份语义)。列出的容器无论保存的策略如何都会重启,因为 sidecar 意味着”renet 有意停止了这些容器,欠操作员一次重启。”

为什么 on-failure 看起来像坏掉了。 Docker 的 on-failure 策略仅在容器以非零代码退出时重启。来自 docker stop 或守护进程关闭的正常停止(exit 0)不是”失败”,不会触发重启,无论是通过 Docker 的原生逻辑还是看门狗的保存策略路径。冷备份 sidecar 是安全网:我们有意停止的任何容器都会重启,无论其策略如何。

如何解读运行时状态:

  • docker inspect <container>RestartPolicy.Name:对于 renet 管理的容器始终为 no。不要依赖这个来判断语义策略。
  • 仓库挂载根目录的 .rediacc.jsonservices.<name>.restart_policy:真实意图。
  • docker ps --format '{{.Status}}':运行时状态。

如何修复偏差。 如果容器的 .rediacc.json 保存策略错误(例如,因为你编辑了 compose 但从未重新创建容器),请重新运行 rdc repo up --name <repo> -m <machine>。容器将以更新的策略重新创建。

实验性: 基于冷备份 sidecar 的恢复和 rdc machine query 上的 --sync-certs 标志在 renet 0.9+ 中发布。旧版本仅依赖保存的 restart_policy 进行看门狗恢复,这可能在冷备份后使 on-failure 容器搁浅。

由 rediacc 管理的守护进程禁用了 Docker bridge 网络。 每个仓库的守护进程都配置了 "bridge": "none""iptables": false。在仓库 shell 内运行一个简单的 docker run <image> 仍然能启动容器,但该容器只有 loopback 接口,没有 DNS 或出站连接。这是设计使然,因为仓库之间的 loopback 隔离由 eBPF cgroup 钩子强制执行,而 bridge 容器会绕过这些钩子。生产服务应使用 renet compose(它会为你自动注入主机网络);对于临时调试,请显式传递 --network hostdocker run --rm --network host -it ubuntu bash

注意: Fork 仓库在父仓库子域下获得自动路由:{service}-fork-{tag}.{repo}.{machine}.{baseDomain}。自定义域名在 fork 中被跳过。

启动服务

挂载仓库并启动所有服务:

rdc repo up --name my-app -m server-1
选项描述
--skip-router-restart跳过操作后重启路由服务器

执行顺序为:

  1. 挂载 LUKS 加密仓库(未挂载时自动挂载)
  2. 为此仓库启动隔离的 Docker 守护进程
  3. 从 compose 文件自动生成 .rediacc.json
  4. 按 A-Z 顺序在所有 Rediaccfile 中运行 up()

部署后,输出显示 PROXY ROUTES 部分,其中包含每个服务的实际 URL。具有自定义 Traefik 标签的服务将其自定义域名显示为主要 URL:

HTTP services (accessible via proxy after ~3s):
  gitlab-server:
    HTTPS: https://gitlab.example.com  (custom)
    Auto:  https://gitlab-server.gitlab.server-1.example.com
    IP:    127.0.11.130

停止服务

rdc repo down --name my-app -m server-1
选项描述
--unmount停止服务后卸载加密仓库。如果此操作未生效,请单独使用 rdc repo unmount
--skip-router-restart跳过操作后重启路由服务器

执行顺序为:

  1. 按 Z-A 反向顺序在所有 Rediaccfile 中运行 down()(尽力而为)
  2. 停止隔离的 Docker 守护进程(如果指定了 --unmount
  3. 卸载并关闭 LUKS 加密卷(如果指定了 --unmount

批量操作

一次启动或停止机器上的所有仓库:

rdc repo up -m server-1
选项描述
--include-forks包含复刻仓库
--mount-only仅挂载,不启动容器
--dry-run显示将要执行的操作
--parallel并行运行操作
--concurrency <n>最大并发操作数(默认:3)
--skip-router-restart跳过操作后重启路由服务器

开机自启

默认情况下,服务器重启后需要手动挂载和启动仓库。开机自启功能可配置仓库在服务器启动时自动挂载、启动 Docker 并运行 Rediaccfile up()

工作原理

当您为仓库启用开机自启时:

  1. 生成一个 256 字节的随机 LUKS 密钥文件,并添加到仓库的 LUKS 槽位 1(槽位 0 仍为用户密码短语)。
  2. 密钥文件存储在 {datastore}/.credentials/keys/{guid}.key,权限为 0600(仅 root 可读)。
  3. 安装一个 systemd 服务(rediacc-autostart),在启动时挂载所有已启用的仓库并启动其服务。

在系统关机或重启时,该服务会优雅地停止所有服务(Rediaccfile down())、停止 Docker 守护进程并关闭 LUKS 卷。

**安全提示:**启用开机自启会将 LUKS 密钥文件存储在服务器磁盘上。任何拥有服务器 root 权限的人都可以在无需密码短语的情况下挂载仓库。请根据您的威胁模型进行评估。

启用

rdc repo autostart enable --name my-app -m server-1

系统将提示您输入仓库密码短语。

为所有仓库启用

rdc repo autostart enable -m server-1

禁用

rdc repo autostart disable --name my-app -m server-1

此操作将删除密钥文件并清除 LUKS 槽位 1。

部署时刷新密钥文件

当开机自启已启用时,rdc repo up 会验证 LUKS 槽位 1 的密钥文件。 如果磁盘上的密钥文件仍与 LUKS 槽位匹配,则不会进行任何更改。

通过 repo push / repo pull 在机器之间传输仓库后, 新机器上的密钥文件将不匹配。此时,repo up 会自动重新生成密钥文件并更新 LUKS 槽位 1。您将看到以下日志消息:

Refreshing keyfile credential for <guid>
Killing LUKS slot 1: /mnt/rediacc/repositories/<guid>
Adding keyfile to LUKS slot 1: /mnt/rediacc/repositories/<guid>

这是安全的,槽位 0(您的密码短语)永远不会被修改。如果未启用开机自启, 此检查将静默跳过。失败不是致命的,不会阻止部署。

查看状态

rdc repo autostart list -m server-1

完整示例

以下示例部署一个包含 PostgreSQL、Redis 和 API 服务器的 Web 应用。

1. 设置环境

curl -fsSL https://www.rediacc.com/install.sh | bash
rdc config init --name production --ssh-key ~/.ssh/id_ed25519
rdc config machine add --name prod-1 --ip 203.0.113.50 --user deploy
rdc config machine setup --name prod-1
rdc repo create --name webapp -m prod-1 --size 10G

2. 挂载并准备

rdc repo mount --name webapp -m prod-1

3. 创建应用文件

在仓库内创建以下文件:

docker-compose.yml:

services:
  postgres:
    image: postgres:16
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: webapp
      POSTGRES_USER: app
      POSTGRES_PASSWORD: changeme

  redis:
    image: redis:7-alpine

  api:
    image: myregistry/api:latest
    environment:
      DATABASE_URL: postgresql://app:changeme@postgres:5432/webapp
      REDIS_URL: redis://redis:6379
      LISTEN_ADDR: 0.0.0.0:8080

Rediaccfile:

#!/bin/bash

up() {
    mkdir -p data/postgres
    renet compose -- up -d

    echo "Waiting for PostgreSQL..."
    for i in $(seq 1 30); do
        if renet compose -- exec postgres pg_isready -q 2>/dev/null; then
            echo "PostgreSQL is ready."
            return 0
        fi
        sleep 1
    done
    echo "Warning: PostgreSQL did not become ready within 30 seconds."
}

down() {
    renet compose -- down
}

4. 启动

rdc repo up --name webapp -m prod-1

5. 启用开机自启

rdc repo autostart enable --name webapp -m prod-1