1. 项目概述:ryujin 是什么,为什么值得花时间搞懂它
ryujin 不是一个大众耳熟能详的工具,但它在特定技术圈层里,尤其是关注轻量级、高可控性服务编排与部署的开发者群体中,正快速建立口碑。它不是 Docker Compose 的替代品,也不是 Kubernetes 的简化版,而是一个定位非常清晰的“中间态”工具——专为单机或小规模集群设计的、基于 YAML 声明式配置的服务生命周期管理器。你可以把它理解成一个“带状态感知的 systemd + 智能化日志路由 + 内置健康检查”的合体:它不负责容器调度,但能精准控制容器启停顺序、依赖关系、重启策略;它不提供服务网格,但能自动收集各服务 stdout/stderr 并按服务名打标归类;它不内置 API 网关,但通过简单配置就能把 HTTP 请求代理到对应服务端口,并支持基础路径重写和超时控制。
我第一次接触 ryujin 是在维护一套内部 CI/CD 测试环境时。当时用的是纯 shell 脚本 + systemctl 启停服务,每次新增一个依赖服务(比如加个 Redis 缓存层),就得手动改启动顺序、加 wait-for-it 检查、补日志轮转逻辑,出错后排查要翻七八个 journalctl -u 日志。换成 ryujin 后,整个流程收敛到一个 ryujin.yaml 文件里:定义 service A 依赖 service B,B 启动成功后才拉起 A;A 的日志自动归档到 /var/log/ryujin/a/;HTTP 请求 /api/v1/cache 被自动转发到 localhost:6379;所有服务异常退出时,ryujin 自动按预设策略重启(比如前 5 分钟最多重启 3 次,之后暂停并告警)。实测下来,部署时间从平均 22 分钟压到 3 分钟以内,故障定位时间从平均 40 分钟降到 5 分钟内——不是因为它多炫酷,而是它把那些“每个项目都要重复造一遍的轮子”,做成了可复用、可版本化、可 diff 的声明式配置。
关键词 “ryujin,安装,更新,使用” 看似平平无奇,但背后藏着三个真实痛点:第一,“安装”难在环境适配——它不提供 Windows 安装包,也不打包进主流 Linux 发行版仓库,必须自己编译或下载预编译二进制;第二,“更新”难在配置兼容性——v0.8 到 v0.9 重构了健康检查字段名,旧配置直接运行会报错,但错误提示不明确;第三,“使用”难在概念抽象——它用 “unit” 代替 “service”,用 “profile” 管理环境变量,这些术语不看文档根本猜不出含义。所以这篇内容不是教你怎么敲几行命令,而是带你真正吃透 ryujin 的设计哲学、踩坑现场和落地节奏。适合正在评估轻量级服务编排方案的运维工程师、需要快速搭建本地开发环境的后端开发者,以及厌倦了写一堆启动脚本的技术负责人。你不需要提前掌握 Rust 或系统编程,只要用过 Docker 和 systemd,就能顺畅跟进。
2. 安装全流程拆解:从零开始构建可信赖的 ryujin 运行环境
2.1 安装方式选型:为什么放弃包管理器,坚持二进制直装
ryujin 官方目前(截至 v0.9.3)不提供 apt/yum/dnf 包,也未入驻 Homebrew 主仓库。社区曾有人提 PR 尝试加入 Ubuntu 官方源,但因上游审核周期长、版本更新不同步被搁置。这意味着你无法用sudo apt install ryujin一键搞定。有人会说:“那我自己打包一个 deb 包不行吗?”——理论上可以,但实际操作中会立刻撞上三个硬伤:一是 ryujin 依赖特定版本的 OpenSSL 和 libsystemd,不同发行版默认版本差异大,打包时容易漏掉动态链接库;二是它的配置文件模板(如/etc/ryujin/ryujin.yaml.example)需要随二进制一起分发,而 dpkg 规范对非标准路径文件处理复杂;三是 ryujin 的升级机制依赖校验二进制哈希值,如果通过包管理器安装,后续ryujin update命令会因文件权限问题失败。
所以我最终选择预编译二进制直装,这是官方文档明确推荐、且经我们团队 17 个生产环境验证最稳的路径。它有三个不可替代的优势:第一,二进制是静态链接的(Rust 默认行为),自带全部依赖,扔到任何 x86_64 Linux 发行版上都能跑;第二,安装过程就是curl + chmod + mv三步,全程无 root 权限外的操作,审计日志干净;第三,ryujin self-update命令能直接替换二进制,无需重新下载、解压、覆盖,升级原子性有保障。当然,它也有代价:你需要自己管理二进制存放路径(建议统一用/usr/local/bin/ryujin),并确保该路径在$PATH中。这点看似麻烦,实则换来的是完全掌控权——你知道每一个字节从哪来、到哪去,而不是把信任交给某个第三方仓库的 maintainer。
2.2 实操安装步骤:附带校验、权限、路径三重保险
下面是我在线上环境标准化执行的安装脚本,已适配 Ubuntu 22.04、CentOS 7.9、Alpine 3.18 三种主流系统:
# 1. 创建临时目录并进入 mkdir -p /tmp/ryujin-install && cd /tmp/ryujin-install # 2. 下载最新稳定版二进制(以 v0.9.3 为例) curl -fsSL https://github.com/ryujin-org/ryujin/releases/download/v0.9.3/ryujin-linux-amd64 -o ryujin # 3. 下载对应 SHA256 校验和(关键!不能跳过) curl -fsSL https://github.com/ryujin-org/ryujin/releases/download/v0.9.3/ryujin-linux-amd64.sha256 -o ryujin.sha256 # 4. 校验二进制完整性(输出 'OK' 表示校验通过) sha256sum -c ryujin.sha256 2>/dev/null | grep -q "OK" || { echo "校验失败!请检查网络或 GitHub 是否被干扰"; exit 1; } # 5. 添加可执行权限 chmod +x ryujin # 6. 移动到系统 PATH 目录(优先选 /usr/local/bin,次选 /usr/bin) sudo mv ryujin /usr/local/bin/ # 7. 验证安装结果 ryujin --version # 应输出 "ryujin v0.9.3"提示:第 4 步的校验是安全底线。我见过两次因 CDN 缓存污染导致下载的二进制被篡改的案例——一次是某云服务商 CDN 节点缓存了旧版 release,另一次是公司内部镜像站同步脚本 bug。没有校验,你等于把 root 权限白送给未知代码。
注意:不要用
sudo curl ... | sudo bash这类“一键安装”方式。它绕过了校验步骤,且执行过程不可审计。真正的生产环境,每一步操作都必须可追溯、可回滚。
安装完成后,别急着写配置。先运行ryujin init初始化默认环境:
# 创建默认配置目录(/etc/ryujin)和数据目录(/var/lib/ryujin) sudo ryujin init # 查看生成的示例配置(重点看 units 下的 nginx 和 redis 示例) sudo cat /etc/ryujin/ryujin.yaml这个命令会创建/etc/ryujin/目录,并生成一个带详细注释的ryujin.yaml。它不是“开箱即用”的配置,而是你理解 ryujin 语法的起点。你会发现里面定义了两个 unit:nginx(监听 80 端口)和redis(监听 6379 端口),它们之间有明确的depends_on关系。这正是 ryujin 的核心思想:服务不是孤立的进程,而是有依赖、有状态、有生命周期的单元(unit)。
2.3 环境适配要点:针对不同系统的微调技巧
虽然二进制是静态链接的,但 ryujin 在不同系统上的行为仍有细微差别,必须针对性处理:
CentOS 7 / RHEL 7:默认 systemd 版本较老(v219),不支持
RestartSec的浮点数写法(如RestartSec: 0.5)。如果你在配置里写了RestartSec: 0.5,ryujin 启动时会报错Invalid restart sec value。解决方案是统一改成整数,比如RestartSec: 1,或者升级 systemd(不推荐,风险高)。Alpine Linux:musl libc 环境下,ryujin 的日志轮转功能(logrotate)可能失效,因为其内部调用的
logrotate二进制路径与 glibc 系统不同。解决方法是在ryujin.yaml的 global 配置块里显式指定路径:global: logrotate_binary: "/sbin/logrotate" # Alpine 的路径Ubuntu 22.04+(启用 systemd-resolved):ryujin 的 DNS 解析默认走系统默认 resolver,而 systemd-resolved 监听在
127.0.0.53:53,某些容器网络模式下可能无法访问。此时需在 unit 配置里强制指定 DNS:units: myapp: image: myapp:latest dns: ["8.8.8.8", "114.114.114.114"] # 绕过 systemd-resolved
这些细节不会写在官方 Quick Start 里,但它们是线上稳定运行的关键。我建议你在首次安装后,立即在测试机上跑一遍ryujin validate(配置语法检查)和ryujin status(服务状态快照),确认所有 unit 都处于inactive (dead)状态——这是健康基线,说明安装没引入意外副作用。
3. 更新机制深度解析:如何安全、可控地完成版本跃迁
3.1 更新的两种模式:自动 vs 手动,何时该用哪一种
ryujin 提供两种更新方式:ryujin self-update(自动)和手动下载替换。很多人觉得“自动更新”更省事,但我的经验是:生产环境永远用手动更新,开发/测试环境才考虑自动更新。
ryujin self-update的原理很简单:它会向 GitHub Releases API 发起请求,获取最新 release 的 tag 名(如v0.9.4),然后下载对应二进制,校验 SHA256,最后用mv原子替换/usr/local/bin/ryujin。整个过程不到 2 秒,看起来很美。但它隐藏着一个致命假设:你的网络能稳定访问 github.com,且 GitHub 的 release 页面结构不会变。现实中,我们遇到过三次失败:第一次是公司防火墙策略变更,阻断了对api.github.com的 HTTPS 请求;第二次是 GitHub API 限流,返回 403;第三次是某次 release 上传时,.sha256文件晚于二进制 3 秒上传,导致校验失败。这三次都导致ryujin self-update卡死在 “Downloading...” 状态,进而阻塞了后续所有ryujin命令。
相比之下,手动更新虽然多敲几行命令,但完全可控:
# 1. 查看当前版本和最新可用版本 ryujin --version # v0.9.3 curl -s https://api.github.com/repos/ryujin-org/ryujin/releases/latest | grep '"tag_name"' | cut -d '"' -f4 # v0.9.4 # 2. 下载新版本(带校验) curl -fsSL https://github.com/ryujin-org/ryujin/releases/download/v0.9.4/ryujin-linux-amd64 -o /tmp/ryujin-v0.9.4 curl -fsSL https://github.com/ryujin-org/ryujin/releases/download/v0.9.4/ryujin-linux-amd64.sha256 -o /tmp/ryujin-v0.9.4.sha256 sha256sum -c /tmp/ryujin-v0.9.4.sha256 # 3. 原子替换(先备份旧版,再移动新版) sudo cp /usr/local/bin/ryujin /usr/local/bin/ryujin-v0.9.3.bak sudo mv /tmp/ryujin-v0.9.4 /usr/local/bin/ryujin sudo chmod +x /usr/local/bin/ryujin # 4. 验证 ryujin --version # 必须输出 v0.9.4这个流程的核心是“先验证,后替换,有备份”。它把不可控的网络环节(下载)和可控的本地操作(校验、替换)彻底分离,即使下载失败,也不会影响现有 ryujin 功能。更重要的是,它让你有机会在替换前,仔细阅读 v0.9.4 的 CHANGELOG ,重点关注 Breaking Changes。比如 v0.9.4 就废弃了health_check.timeout字段,改用health_check.http_timeout,如果你没注意到,直接替换后,所有带健康检查的 unit 都会启动失败。
3.2 配置兼容性迁移:从 v0.8.x 到 v0.9.x 的实战避坑指南
ryujin 的版本迭代中,v0.9 是一次重大重构,主要变化集中在健康检查(health check)和日志配置(logging)两大模块。如果你是从 v0.8.x 升级,必须做三件事,缺一不可:
第一,重写 health_check 块
v0.8 的写法:
units: web: image: nginx:alpine health_check: type: http endpoint: /health timeout: 5 interval: 10v0.9 的等效写法:
units: web: image: nginx:alpine health_check: http: url: http://localhost:80/health timeout: 5s # 注意单位必须带 's' interval: 10s关键变化:endpoint→url,timeout/interval必须带单位(5s,10s),且url必须是完整 URL(含协议和端口)。很多用户卡在这里,因为http://localhost/health会失败——ryujin 的健康检查是容器内发起的,localhost指向容器自身,而非宿主机。正确写法是http://127.0.0.1:80/health或直接写宿主机 IP。
第二,迁移 logging 配置
v0.8 的全局日志设置:
global: log_level: info log_format: jsonv0.9 已移除log_format,改为 per-unit 控制,且新增log_driver:
global: log_level: info units: web: image: nginx:alpine logging: driver: "json-file" # 可选 json-file 或 journald options: max-size: "10m" max-file: "3"第三,检查所有depends_on的 target
v0.9 加强了依赖解析的严格性。v0.8 允许写depends_on: [redis],即使redisunit 不存在,也只警告。v0.9 会直接报错Unit 'redis' not found in depends_on。所以升级前,务必运行ryujin validate,它会扫描整个配置,列出所有缺失的依赖 unit。
我建议把这次迁移做成一个 checklist,在更新前逐项核对:
| 检查项 | v0.8 写法 | v0.9 正确写法 | 是否已修复 |
|---|---|---|---|
| health_check.url | /health | http://127.0.0.1:80/health | ☐ |
| health_check.timeout | 5 | 5s | ☐ |
| logging.format | json | 移除,改用logging.driver | ☐ |
| depends_on 引用 | redis | 确认redisunit 存在且拼写一致 | ☐ |
这个表不是摆设。我们团队在升级时,就因漏掉第一项,导致 Web 服务反复重启——健康检查一直超时,ryujin 认为服务不健康,不断 kill-restart。花了 47 分钟才定位到localhost的坑。
3.3 回滚机制设计:当更新出错时,如何 30 秒内恢复服务
再严谨的更新流程,也无法 100% 规避意外。所以必须设计回滚路径。ryujin 本身不提供ryujin rollback命令,但我们可以用操作系统能力实现秒级回滚:
第一步:建立二进制版本快照
在每次更新前,执行:
# 保存当前二进制哈希(用于事后审计) sha256sum /usr/local/bin/ryujin > /var/log/ryujin/ryujin-v$(ryujin --version | cut -d' ' -f2).sha256 # 备份二进制(带时间戳) sudo cp /usr/local/bin/ryujin /usr/local/bin/ryujin-$(date +%Y%m%d-%H%M%S)第二步:配置版本化管理
把ryujin.yaml放进 Git 仓库,每次更新前 commit:
cd /etc/ryujin git add ryujin.yaml git commit -m "chore(ryujin): prepare for v0.9.4 upgrade" git push第三步:回滚执行脚本
当发现 v0.9.4 有问题时,运行:
# 1. 恢复二进制(找最近的备份) sudo cp /usr/local/bin/ryujin-20240520-143022 /usr/local/bin/ryujin # 2. 恢复配置(从 Git 撤销) cd /etc/ryujin git checkout HEAD~1 ryujin.yaml # 3. 重启 ryujin(它会自动 reload 配置) sudo systemctl restart ryujin # 4. 验证 ryujin status # 所有 unit 应回到升级前状态整个过程,熟练操作者可在 25 秒内完成。关键是把“备份”动作变成日常习惯,而不是出事后再手忙脚乱。我们线上 SRE 团队甚至把这个流程写进了 on-call runbook,作为 P1 故障的标准响应步骤。
4. 核心使用场景详解:从单服务托管到多服务协同编排
4.1 单服务托管:超越 docker run 的精细化控制
很多人以为 ryujin 就是 “docker run 的 YAML 化”,其实远不止。以部署一个简单的 Python Flask API 为例,传统做法是:
docker run -d --name myapi -p 5000:5000 -v /data:/app/data myapi:latest这行命令隐含了至少 5 个未声明的假设:容器退出后不重启;日志直接输出到 stdout,不轮转;没有健康检查;挂载目录权限由 Docker 自动处理;网络用默认 bridge。一旦出问题,排查成本很高。
用 ryujin 管理,配置ryujin.yaml如下:
units: myapi: image: myapi:latest ports: - "5000:5000" volumes: - "/data:/app/data:rw,z" # z 标签确保 SELinux 上下文正确 restart: always restart_sec: 5 health_check: http: url: http://127.0.0.1:5000/health timeout: 3s interval: 10s logging: driver: "journald" # 直接对接 systemd journal options: tag: "myapi" # 日志打标,方便 journalctl -t myapi environment: - "FLASK_ENV=production" - "DATABASE_URL=sqlite:////app/data/db.sqlite"这个配置带来了 5 个质变:
- 重启策略可控:
restart: always+restart_sec: 5意味着服务崩溃后,5 秒内必重启,且不受 Docker daemon 重启影响; - 日志可追溯:
logging.driver: journald让所有日志进入 systemd journal,用journalctl -t myapi -n 100即可查看最近 100 行,无需docker logs; - 健康检查闭环:
health_check不仅用于 ryujin 自身判断,还会暴露给外部监控系统(如 Prometheus 的/metrics端点会包含ryujin_unit_health_status{unit="myapi"} 1); - 安全加固:
volumes中的:z标签在 SELinux 环境下自动设置正确的上下文,避免 “Permission denied” 错误; - 环境隔离:
environment块让敏感配置(如数据库 URL)与镜像解耦,同一镜像可部署到 dev/staging/prod 环境,只需切换 profile。
实操心得:
volumes的:z和:Z标签常被混淆。:z表示“此卷将被多个容器共享,需添加shared上下文”,:Z表示“此卷仅供本容器使用,需添加private上下文”。在单服务场景下,一律用:z,否则容器启动失败。
4.2 多服务协同:用 depends_on 和 profiles 构建可靠依赖链
真实业务从不是单个容器。一个典型 Web 应用包含:前端 Nginx、后端 API、Redis 缓存、PostgreSQL 数据库。它们之间有严格的启动顺序和依赖关系。ryujin 用depends_on和profiles优雅解决:
# 定义 profiles(环境变量集) profiles: common: environment: - "TZ=Asia/Shanghai" prod: extends: common environment: - "NODE_ENV=production" - "LOG_LEVEL=warn" units: db: image: postgres:15-alpine environment: - "POSTGRES_PASSWORD=secret" volumes: - "/var/lib/postgres:/var/lib/postgresql/data:z" health_check: http: url: http://127.0.0.1:5432/health # 需应用层提供 timeout: 5s interval: 30s cache: image: redis:7-alpine depends_on: - db # cache 启动前,db 必须 healthy health_check: http: url: http://127.0.0.1:6379/health timeout: 3s interval: 10s api: image: myapi:latest depends_on: - db - cache environment: - "DATABASE_URL=postgresql://postgres:secret@db:5432/myapp" - "REDIS_URL=redis://cache:6379/0" # 使用 prod profile profile: prod nginx: image: nginx:alpine depends_on: - api # nginx 启动前,api 必须 healthy ports: - "80:80" volumes: - "/etc/nginx/conf.d:/etc/nginx/conf.d:ro,z"这个配置实现了三层依赖:
- 物理依赖:
cache依赖db,意味着db的容器必须先启动、并通过健康检查,cache才会启动; - 网络依赖:
api的DATABASE_URL中@db:5432,利用了 ryujin 内置的 DNS 服务(所有 unit 名自动注册为 DNS 名); - 逻辑依赖:
nginx依赖api,确保流量入口只在后端就绪后才开放。
注意:
depends_on只保证启动顺序,不保证应用层就绪。比如 PostgreSQL 容器启动很快,但初始化数据库可能要 20 秒。所以health_check必须由应用提供/health接口,返回{"status": "ok"}才算真正 ready。我们给所有服务都加了 health check,哪怕只是curl -f http://localhost:$PORT/health || exit 1。
4.3 高级技巧:用 hooks 实现部署前/后自动化
ryujin 的hooks是被严重低估的功能。它允许你在 unit 生命周期的关键节点执行自定义命令,比如:
pre-start: 启动容器前执行(可用于数据库迁移)post-start: 容器启动后、健康检查前执行(可用于配置热加载)pre-stop: 停止容器前执行(可用于优雅关闭连接)post-stop: 容器停止后执行(可用于清理临时文件)
以数据库迁移为例:
units: db: image: postgres:15-alpine # ... 其他配置 hooks: pre-start: - "sh -c 'if [ ! -f /var/lib/postgresql/data/migrated ]; then alembic upgrade head && touch /var/lib/postgresql/data/migrated; fi'"这段 hook 的意思是:每次dbunit 启动前,检查/var/lib/postgresql/data/migrated文件是否存在;不存在则执行alembic upgrade head(SQLAlchemy 迁移命令),成功后创建标记文件。这样,无论你是首次部署,还是升级后重启,数据库 schema 总是最新。
另一个经典场景是前端资源预热:
units: nginx: image: nginx:alpine hooks: post-start: - "curl -s http://localhost:80/static/app.js > /dev/null" - "curl -s http://localhost:80/api/health > /dev/null"post-start在 nginx worker 进程 ready 后触发,用curl预热静态资源和 API,避免用户首屏请求时遭遇 cold start 延迟。
实操心得:hook 命令默认在 ryujin 主进程的 namespace 中执行,所以能访问 host 网络(
localhost指宿主机)。但如果需要访问容器内网络,必须用ryujin exec:hooks: post-start: - "ryujin exec api -- curl -s http://localhost:5000/health"这行命令会在
api容器内执行 curl,用于验证 API 服务是否真正在容器内就绪。
5. 常见问题与排查技巧实录:来自 17 个生产环境的真实战报
5.1 启动失败:Unit stuck in 'activating' 状态的 5 种根因
ryujin status显示某个 unit 状态为activating (auto-restart),且长时间不变成active (running),这是最常见也最让人抓狂的问题。根据我们处理过的 43 起同类故障,根因分布如下:
| 排查顺序 | 现象 | 检查命令 | 解决方案 |
|---|---|---|---|
| 1. 健康检查失败 | ryujin logs <unit>显示反复 restart,且health_check配置存在 | ryujin logs <unit> | grep "health" | 检查health_check.url是否可达;确认应用是否监听在127.0.0.1而非0.0.0.0;增加health_check.start_period: 30s给慢启动应用缓冲期 |
| 2. 依赖未就绪 | ryujin status显示依赖 unit 状态为inactive (dead)或activating | ryujin status | grep -A5 "<dep-unit>" | 进入依赖 unit 目录cd /var/lib/ryujin/<dep-unit>,查看logs/下的容器日志;常见原因是 volume 权限错误(chown -R 999:999 /data) |
| 3. 端口冲突 | ryujin logs <unit>出现bind: address already in use | sudo ss -tuln | grep :<port> | 用sudo lsof -i :<port>找出占用进程,kill -9或修改配置中ports |
| 4. 镜像拉取失败 | ryujin logs <unit>出现pull access denied或not found | sudo docker pull <image> | 检查镜像名拼写;确认私有 registry 认证(ryujin login);若用latest标签,建议改用具体 hash(myapp@sha256:abc...) |
| 5. SELinux 阻断 | ryujin logs <unit>出现Permission denied,且系统启用了 SELinux | sudo sestatus | 临时禁用测试:sudo setenforce 0;永久解决:sudo semanage fcontext -a -t container_file_t "/data(/.*)?"+sudo restorecon -Rv /data |
独家技巧:当
ryujin logs <unit>输出为空时,不要慌。这是因为日志驱动还没初始化。直接看 Docker 日志:sudo docker ps -a \| grep <unit>找到容器 ID,然后sudo docker logs <container-id>。90% 的“无日志”问题,根源都在容器启动阶段。
5.2 日志丢失:为什么 journalctl 看不到 unit 日志
现象:journalctl -t myapi返回No entries,但ryujin logs myapi能看到日志。这是因为 ryujin 默认日志驱动是json-file,日志写入/var/lib/ryujin/myapi/logs/,而非 systemd journal。
解决方案分两步:
- 强制使用 journald 驱动:在
ryujin.yaml的 unit 或 global 块中添加:logging: driver: "journald" - 确保 ryujin 进程有 journal 权限:默认情况下,ryujin 以普通用户运行,无法写入 journal。需修改 systemd service 文件:
输入:sudo systemctl edit ryujin[Service] StandardOutput=journal StandardError=journal
注意:
journald驱动下,ryujin logs <unit>命令会失效(因为它只读取json-file日志)。此时必须用journalctl -t <unit>。我们团队的做法是:开发环境用json-file(方便ryujin logs快速调试),生产环境用journald(对接 ELK 日志平台)。
5.3 更新后配置不生效:validate 通过但 status 显示 old config
这是 v0.9 升级后最高频的问题。ryujin validate返回Configuration is valid,但ryujin status显示的仍是旧 unit 列表。根因只有一个:ryujin daemon 没有 reload 配置。
ryujin 的设计是:ryujin.yaml修改后,必须显式通知 daemon。有两种方式:
- 优雅 reload:
sudo systemctl reload ryujin(推荐)。它会平滑过渡,新配置生效,旧 unit 保持运行直到自然退出; - 强制 restart:
sudo systemctl restart ryujin。它会 kill 所有 unit,再按新配置启动。
实操心得:
reload不是万能的。如果新配置中删除了某个 unit,reload不会 stop 它;只有restart才会彻底清理。所以我们的发布 SOP 是:先reload,观察 2 分钟;若一切正常,再restart清理残留。
5.4 网络不通:容器内无法访问宿主机服务
现象:unit 内curl http://host.docker.internal:3000失败。这是因为 ryujin 默认不启用 Docker 的host.docker.internal别名。
解决方案:
- 方法一(推荐):在 unit 配置中显式添加 host entry:
units: myapp: image: myapp:latest extra_hosts: - "host.docker.internal:host-gateway" # Docker 20.10+ - 方法二(兼容旧版):用宿主机真实 IP:
units: myapp: image: myapp:latest environment: - "HOST_IP=192.168.1.100" # 替换为宿主机 IP
独家技巧:获取宿主机 IP 的通用命令(适配所有网络环境):
ip route | awk '/default/ {