ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

自托管服务栈gstack:Docker Compose与反向代理统一管理实践

自托管服务栈gstack:Docker Compose与反向代理统一管理实践 去年冬天我把自己一台闲置的Linux服务器翻出来重新规划了一遍上面跑的服务最后整理出一套方案代号就叫“gstack”。G有两层意思一是General通用二是Gateway网关入口。说白了就是一套“以Docker Compose为骨架、以统一反代网关为入口、以集中数据目录为心脏”的自托管服务栈。所有服务装在一台机器上互不干扰配置集中在同一个文件夹里迁移时整个目录拷走到另一台机器上直接启动。这套方案解决了我很长一段时间的痛点服务装得越来越多端口记不住、配置改起来麻烦、备份靠手工、迁移全靠重来。这篇文章就把gstack的整个设计思路、搭建过程、备份恢复方法以及我实际踩过的坑完整记录下来给同样在搞自托管的朋友一个可以直接抄作业的参考。1. gstack的由来与设计目标自托管这件事做久了很容易把服务器搞得一团糟。最开始我只装了一个密码管理器后来陆陆续续加了RSS阅读器、代码仓库、笔记服务、网盘同步。服务一多问题就开始冒头端口号记不住每次登录都要翻配置文件有些服务要对外暴露有些只能内网访问没有统一入口梳理起来很麻烦更新靠手工执行docker pull docker compose up -d随时可能漏掉某个容器更惨的是备份今天想起备份这个目录明天想起备份那个目录时间长了自己都说不清数据到底放在哪些位置。gstack就是冲着这些问题去的。它的设计目标非常明确所有服务收敛成一个整体统一网关管所有域名和HTTPS证书统一数据目录方便备份统一Compose文件描述全部服务依赖关系。换机器时新服务器上只需要装好Docker把整个gstack目录复制过去再执行一次docker compose up -d服务就全部回来了。这套方案适合谁如果你有自托管需求比如密码管理、RSS阅读、代码托管、网盘、博客、Wiki或者你是小团队想统一管理几台服务器上的服务都可以参考这个思路。它的学习门槛不高不需要会用Kubernetes甚至不需要太懂Nginx会基本的Docker命令就能上手。我写这篇文章的时候假定你已经对Docker有一定概念知道镜像和容器是什么关系但还没有形成一套完整的服务编排思路。没关系接下来的内容会一步步把细节讲清楚。1.1 自托管服务分散管理的痛点拆解在我决定整理gstack之前服务器上的状态只能用“混乱”来形容。每个服务都是一套独立的Docker容器各自有各自的挂载目录、端口映射和启动参数。表面上看每个服务都是独立的想停哪个停哪个但实际上随着数量增加管理成本是成倍上升的。最典型的几个问题第一端口规划没有全局概念。今天装一个服务随手填了8080明天另一个服务也想用8080冲突了才发现之前的端口已经忘了。查一下还好但次数多了一台机器上几十个端口都在用谁来审计这些端口对应的服务没人说得准。第二升级维护没有统一节奏。每个镜像的更新周期不一样又不可能天天盯着Docker Hub时间一长某些容器里的镜像版本落后好几个大版本出问题的时候都不知道是配置问题还是版本问题。第三备份对象不明确。每个镜像在容器内部写数据的路径都不一样有的写/data有的写/var/lib有的写/config如果不给每个容器单独指定宿主机目录映射数据就落在容器可写层里容器一删数据全没。第四灾难恢复困难。机器故障换新机的时候要挨个安装服务、配置数据目录、重新建立反代规则耗时一两天都算正常而且过程极容易遗漏某个服务。这些痛点不是偶发而是所有自托管玩家的必经阶段。gstack把一个“多服务的杂乱环境”整理成一个“有结构的单一系统”这套思路本身比具体的工具选型更重要。1.2 为什么选择“单机Compose栈”而非Kubernetes我在设计gstack时身边也有朋友问为什么不用Kubernetes。我的回答很简单对于一台个人服务器Kubernetes是过度设计。Kubernetes擅长管理几十个节点的集群、自动扩缩容、滚动发布但这些在个人自托管场景里基本用不上。单机Docker Compose已经提供了足够的隔离能力、资源限制和网络管理而且配置是声明式的改完执行一下就能恢复状态。对比一下Kubernetes要把镜像仓库、Ingress Controller、存储类、命名空间、Helm Chart全部配好整套体系学下来就是好几周的时间日常运维还要关注Pod调度和节点健康。Compose只需要一个YAML文件目录结构清晰备份脚本简单出了问题可以快速定位。对于个人和小团队自托管Compose就是性价比最高的方案。2. 整体架构设计与选型逻辑gstack的架构说起来很简单就四个部分Compose编排、反代网关、数据目录、自动更新。每个部分的选型都有明确理由接下来逐一拆解。2.1 目录结构与统一配置管理我推荐的gstack目录结构是这样的~/gstack/ ├── docker-compose.yml ├── .env ├── data/ │ ├── vaultwarden/ │ ├── freshrss/ │ ├── gitea/ │ └── npm/ ├── backup/ └── scripts/.env文件负责存放所有全局变量TZAsia/Shanghai DOMAIN_ROOTexample.com DATA_DIR/srv/gstack-dataCompose文件里通过${VAR}引用这些变量。这样做的好处是换域名、换机器、调整时区时只需要改一个文件不需要在十几个服务配置里逐个修改。这个习惯看似简单实际用起来非常保值尤其是后来把gstack迁移到新机器的时候我只改了.env里的两个值其他完全没动。以容器内“数据目录映射”为例我做了统一约束所有服务的数据都映射到${DATA_DIR}下的独立子目录例如${DATA_DIR}/vaultwarden、${DATA_DIR}/freshrss。备份时只需要打包${DATA_DIR}这一个目录不需要关心每种镜像内部的数据路径。这个设计是整个备份体系的核心后面会详细说。2.2 反代网关选型为什么用Nginx Proxy Manager反向代理在gstack里的地位是“统一入口”。外部流量先到网关网关根据域名把请求分发给具体的容器服务。这个组件我先后试过三种方案手写Nginx、Caddy、Traefik最后固定在Nginx Proxy ManagerNPM上。先说明手写Nginx的问题。Nginx配置本身不复杂但服务一多每个服务要一段server块还要处理client_max_body_size、proxy_set_header、WebSocket升级等各种细节。写多了容易出错。有一次我忘记在某个location里加proxy_set_header Host $host导致那个服务的回环地址一直是网关的内部IP排查了整整一个小时才找到原因。Caddy确实优雅Caddyfile写法简洁自动申请和续期HTTPS证书开箱即用。但对于不熟悉命令行的用户来说每次改配置都要编辑文件缺少图形反馈出错了也不容易看出来。Traefik也很强但它的核心优势体现在动态服务发现和与编排平台深度集成的场景单机几个服务专门为它配一套标签和中间件学习成本偏高。NPM的优势在于它把反代配置可视化添加一个Proxy Host、选择转发协议和端口、申请Lets Encrypt证书全程鼠标点击就完成了。证书续期也是自动的大幅降低维护门槛。它的界面虽然不算现代但胜在稳定社区使用人数多遇到问题搜一下就有答案。对于gstack这种“个人维护、服务数量有限”的场景NPM是最均衡的选择。2.3 数据目录统一挂载与备份思路备份这件事最重要的是“对象明确”。很多人的备份方式是习惯性的比如今天导出一下数据库明天复制一下某个目录这种散点式备份最终必然有遗漏。gstack的核心设计之一就是把所有持久化数据收敛到一个根目录下。每个容器的volumes配置都遵循同一个模式services: vaultwarden: image: vaultwarden/server:latest volumes: - ${DATA_DIR}/vaultwarden:/data这样一来宿主机上只需要关心一个目录${DATA_DIR}。备份时执行一条命令把整个目录打包即可。这个设计用一句话概括就是“把不确定变成确定”。只要目录结构固定备份脚本写一次就永远适用新增服务时只需要沿用同样的映射规则不用单独修改备份逻辑。2.4 网络规划与服务互访容器之间的通信也是容易被新手忽略的点。默认情况下每个容器有自己的网络栈直接通过IP互相访问不仅麻烦而且容器一旦重建IP就可能变化。gstack的解决方案是创建一个固定的外部网络networks: gstack_net: external: true name: gstack_net所有服务加入这个网络services: vaultwarden: networks: - gstack_net在同一个Docker网络里容器可以直接用服务名来互相访问。例如NPM反代到vaultwarden容器时转发目标可以填http://vaultwarden:80不需要知道容器实际的IP。这是我踩过的一个坑一开始我图省事写容器IP结果某次重启后IP变了反代全部404。后来改用Docker内置DNS解析服务名再也没出现过这个问题。3. 从零搭建gstack的完整实操这一部分我把从零开始搭建gstack的完整过程记录下来包括配置文件、启动命令、反代设置和自动更新配置。不需要有特殊环境一台普通Linux服务器就够了。3.1 服务端准备与基础依赖安装操作系统建议用Debian或Ubuntu LTS版本。先更新系统然后安装Docker Engine和Compose插件sudo apt update sudo apt upgrade -y curl -fsSL https://get.docker.com | sh sudo systemctl enable --now docker docker compose version确认docker compose version能正常输出就说明Compose插件已经装好了。我这里特意用了docker compose带空格而不是docker-compose前者是Docker官方推荐的插件式命令功能上会保持持续更新。创建gstack目录和.env文件mkdir -p ~/gstack/{data,backup,scripts} cd ~/gstack cat .env EOF TZAsia/Shanghai DOMAIN_ROOTexample.com DATA_DIR/srv/gstack-data EOF注意DATA_DIR我设置的是/srv/gstack-data没有放在~/gstack/data下面。原因是/srv目录通常挂载在系统盘而系统盘和数据盘往往是分开的把数据放在/srv可以方便后续单独挂载大容量数据盘。如果你只有一块盘路径随意只要保持一致就行。3.2 编写docker-compose.yml以三个服务为例下面是一个最小可运行的gstack示例包含三个服务Vaultwarden密码管理、FreshRSSRSS阅读和Gitea代码托管外加NPM网关和Watchtower自动更新容器。完整文件内容如下services: nginx-proxy-manager: image: jc21/nginx-proxy-manager:latest container_name: npm restart: unless-stopped ports: - 80:80 - 443:443 - 81:81 volumes: - ${DATA_DIR}/npm:/data - ${DATA_DIR}/npm-letsencrypt:/etc/letsencrypt networks: - gstack_net environment: - TZ${TZ} vaultwarden: image: vaultwarden/server:latest container_name: vaultwarden restart: unless-stopped volumes: - ${DATA_DIR}/vaultwarden:/data networks: - gstack_net environment: - TZ${TZ} - DOMAINhttps://vault.${DOMAIN_ROOT} freshrss: image: freshrss/freshrss:latest container_name: freshrss restart: unless-stopped ports: - 8180:80 volumes: - ${DATA_DIR}/freshrss:/var/www/html networks: - gstack_net environment: - TZ${TZ} - FRESHRSS_ENABLE_BETA0 gitea: image: gitea/gitea:latest container_name: gitea restart: unless-stopped ports: - 3000:3000 - 2222:22 volumes: - ${DATA_DIR}/gitea:/data networks: - gstack_net environment: - TZ${TZ} - SSH_PORT2222 watchtower: image: containrrr/watchtower:latest container_name: watchtower restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - TZ${TZ} - WATCHTOWER_CLEANUPtrue - WATCHTOWER_SCHEDULE0 4 * * * networks: - gstack_net networks: gstack_net: external: true第一行创建外部网络这个网络需要提前创建docker network create gstack_net对于每个服务的关键配置我有几个说明NPM容器映射了80、443和81三个端口。81是NPM的Web管理界面端口按我的习惯是在内网访问不给公网暴露。如果你需要外网管理可以自行调整但强烈建议加一层访问控制或至少用强密码。FreshRSS我直接映射了8180这是为了在还没配置反代时可以先通过IP加端口访问界面完成初始化。实际使用中如果需要公网访问我会在NPM里再配置一条反代规则这样外部用户是通过https://rss.example.com访问而不会直接接触到8180这个端口。Gitea有两个端口映射3000是Web服务2222是SSH端口。这里有一个容易犯的错Gitea容器内部默认SSH端口是22如果直接映射成2222:22外部访问时需要连2222。但Gitea界面显示“克隆地址”时默认会显示22端口需要在Gitea配置里把SSH_PORT改成2222否则克隆地址会指向错误端口。Watchtower的WATCHTOWER_SCHEDULE0 4 * * *表示每天凌晨4点执行一次镜像更新。WATCHTOWER_CLEANUPtrue用于清理更新后遗留的旧镜像避免磁盘被堆积的镜像占满。3.3 启动容器与首次配置第一次启动前先检查YAML语法cd ~/gstack docker compose config --quiet没有任何输出就说明配置没问题。然后启动docker compose up -d第一次会拉取镜像时间取决于网络和镜像大小。拉取完成后查看状态docker compose ps正常情况下所有容器都处于running状态。如果某个容器反复重启可以用下面的命令看日志定位问题docker compose logs 服务名 docker inspect 容器名 --format {{json .State.Health}}首次启动后Vaultwarden会在数据目录生成配置文件FreshRSS需要打开http://服务器IP:8180完成安装向导Gitea打开http://服务器IP:3000完成初始设置。这些初始向导最好在配置反代之前完成不然容易混淆界面地址。3.4 配置反向代理与HTTPS证书打开NPM的管理界面http://服务器IP:81默认账号密码是adminexample.com / changeme首次登录会强制让你修改。登录后进入“Proxy Hosts”页面点击“Add Proxy Host”Domain Names填vault.example.comSchemehttpForward Hostname / IPvaultwardenForward Port80勾选“Block Common Exploits”保存后进入“SSL”标签页勾选“Request a new SSL Certificate”选“Force SSL”注意“Forward Hostname”这一项我填的是服务名vaultwarden而不是IP地址这个就是之前说的Docker内网DNS解析。只要容器都在同一个gstack_net网络里这样填大概率没问题。三个服务各自申请自己的证书全程无需手动上传证书文件续期也是NPM自动处理的。3.5 Watchtower自动更新的细节配置Watchtower默认会更新所有在运行的容器但有些服务我们希望锁住版本。例如如果某个镜像的新版本引入了不兼容的数据库迁移自动更新可能会打断正在使用的服务。我处理的方案是在Compose文件给对应服务加标签services: freshrss: labels: - com.centurylinklabs.watchtower.enablefalseWatchtower会读取这个标签跳过带enablefalse标签的容器。这个配置非常实用实测下来比手动排除清单更好维护因为标签跟着服务定义走Compose文件就是唯一的配置来源不会因为忘记更新清单而出错。4. 备份恢复与迁移实录备份是自托管中最不能省的一环。gstack把数据全部收敛到${DATA_DIR}之后备份脚本就变得非常简单。但简单归简单里面还是有一些值得注意的细节。4.1 备份脚本的设计思路我用的备份脚本逻辑是打包数据目录再额外导出数据库。以SQLite为存储的镜像Vaultwarden、FreshRSS直接打包文件即可但PostgreSQL这类独立数据库最好还是用pg_dump导出逻辑备份避免文件系统不一致。脚本大致如下#!/bin/bash set -e BACKUP_BASE/srv/backups/gstack DATA_DIR/srv/gstack-data STAMP$(date %Y%m%d_%H%M%S) BACKUP_DIR${BACKUP_BASE}/${STAMP} mkdir -p ${BACKUP_DIR} cd ${DATA_DIR} tar czf ${BACKUP_DIR}/data.tar.gz . # 如果compose里有PostgreSQL可以再加一段导出逻辑 # docker compose exec -T postgres pg_dump -U gstack gstack ${BACKUP_DIR}/postgres.sql find ${BACKUP_BASE} -maxdepth 1 -type d -mtime 7 -exec rm -rf {} \; echo backup done: ${BACKUP_DIR}脚本里的find ... -mtime 7用于清理7天之前的备份目录避免磁盘被备份占满。可以用crontab设置每天凌晨执行0 3 * * * /home/user/gstack/scripts/backup.sh /var/log/gstack-backup.log 214.2 热备份还是冷备份一个关键取舍直接对运行中的容器数据目录执行tar属于“热备份”。对于SQLite这类数据库小规模个人使用问题不大因为SQLite本身有WAL机制打包出来的文件大致一致。但如果你的数据非常关键比如是公司的财务系统或核心数据库我建议先停容器再备份也就是“冷备份”docker compose stop vaultwarden tar czf backup.tar.gz ${DATA_DIR}/vaultwarden docker compose start vaultwarden停容器的时间可以控制在几秒到几十秒对个人服务来说完全可接受。我自己在gstack的备份脚本里预设了一个可选的STOP_SERVICES变量值为true时先停容器为false时跳过停止步骤这样不同服务的备份策略可以分开控制。务实来说个人自托管用热备份再配合定期“冷备份抽查”已经足够稳妥。4.3 恢复流程从备份包还原到原服务器恢复时最重要的原则是“先停容器再动数据”。流程如下cd ~/gstack docker compose down mv /srv/gstack-data /srv/gstack-data.bak mkdir -p /srv/gstack-data tar xzf /srv/backups/gstack/20250101_030000/data.tar.gz -C /srv/gstack-data docker compose up -d容器启动后检查数据是否正常。确认无误后再把/srv/gstack-data.bak清理掉。在整个过程中最容易被忽略的是备份包里如果打包时包含了完整路径解压时路径层次会错乱。我打包时是在${DATA_DIR}目录内部执行的tar czf ... .这样备份包的第一层就是各个服务的子目录解压恢复时正好对应${DATA_DIR}下面路径不会嵌套。4.4 迁移到新服务器的完整路线换机器迁移时思路和恢复一样只是多了一个“在新机器上初始化环境”的步骤。我会先在新机器上装好Docker把~/gstack目录整个复制过去然后把备份包传到新机器解压到${DATA_DIR}最后执行docker compose up -d。迁移过程中有几件事必须做第一检查.env文件里的DOMAIN_ROOT是否需要调整因为新机器的域名可能换了。第二检查NPM管理界面里是否生成了新的默认密码如果直接沿用旧数据NPM的用户信息也会一并恢复用旧密码登录即可。第三确认新机器的防火墙或安全组放行了80、443、81等端口。这个步骤在旧机器上已经做过但新机器往往会忘。第四如果新老机器的架构不同比如从x86迁移到ARM镜像需要重新拉取对应架构的版本。不管docker compose up -d会自动处理只要仓库支持多架构镜像就不会有问题。迁移这件事理论上10分钟就能完成基于模板的恢复真正费时的是那些“非标准”配置比如手动改过的容器参数、自定义的网络、某种特殊的数据导出。所以我在gstack里坚持“一切以Compose文件和.env为准”目的就是让机器本身变成可替代品任何一台新机器都能随时接盘。5. 常见问题与排查技巧实录这套gstack我从搭好到现在运行了几个月中间遇到过不少问题。下面挑几个典型问题和排查思路做成一个速查清单大家遇到类似情况可以直接对照。5.1 端口占用导致容器启动失败症状是docker compose up -d时报Bind for 0.0.0.0:80 failed: port is already allocated。常见原因是系统里已经有一个Nginx或Apache占用了80端口也可能是Docker里其他容器也在用80。排查思路sudo ss -tlnp | grep :80 sudo lsof -i :80如果确实是系统自带的Nginx在占用可以直接停掉或卸载sudo systemctl stop nginx sudo systemctl disable nginx如果是其他容器占用检查那个容器是否还需要运行不需要就停掉需要就改端口。我在gstack里特别设计了NPM作为唯一占用80和443的组件其他服务一律不直接暴露这两个端口从根源上避免冲突。5.2 容器时区不一致导致日志和任务时间错乱默认情况下很多镜像的时区是UTC导致容器里的定时任务、日志时间、备份时间都和宿主机差8小时。gstack的.env里统一设置了TZAsia/Shanghai并在每个服务里通过environment注入。实际操作中发现光是TZ还不够某些应用读的是系统时区文件例如Alpine基础镜像需要额外安装tzdata。遇到这类镜像我会在Compose里增加一个挂载volumes: - /etc/localtime:/etc/localtime:ro这也是“看起来不起眼、但没配好就特别难受”的细节。有一次我半夜3点在备份日志里看到备份时间写的是19:00排查下来就是时区没统一的问题。5.3 服务启动顺序导致应用连不上数据库Compose本身不保证容器的启动顺序。如果Web应用和数据库同时启动应用可能比数据库先启动导致应用尝试连接数据库时被拒绝。Gitea、FreshRSS这类服务一般有重连机制应用起来后会自动重试所以问题往往会在首次启动或重启后出现表现为页面打开报“数据库连接失败”。gstack的解决办法有两个方向一是给应用容器加depends_onservices: gitea: depends_on: - db但depends_on只保证数据库容器先启动不保证数据库“内部服务已经就绪”。更稳妥的做法是给应用容器加健康检查并让依赖等待健康检查通过。Compose规范里支持condition: service_healthy例如services: gitea: depends_on: db: condition: service_healthy同时在数据库容器上定义healthcheck。实话说个人自托管阶段不必把所有服务都做到这么严谨但如果你有经常重启服务的习惯这套健康检查配置能省掉不少排查时间。5.4 Docker日志文件无限增长占满磁盘默认的json-file日志驱动会把容器日志无限写入文件时间久了可能占满磁盘。常表现为docker compose logs能输出大量日志、df -h显示磁盘已满、容器写数据报no space left on device。在gstack里我给每个服务统一配置了日志轮转services: vaultwarden: logging: driver: json-file options: max-size: 10m max-file: 3每个容器日志文件最大10MB最多保留3个文件。这个配置可以写进Compose文件全局生效。如果你已经有一堆容器跑了一段时间也可以用docker system prune和docker logs --tail手动清理但长期方案还是要靠理解配置。5.5 自动更新后某个容器反复重启Watchtower更新完镜像后有时候容器会进入restarting状态。原因一般是新版本镜像兼容性变化、应用数据需要迁移或者某个环境变量在新版本里已经改名。排查步骤docker compose logs 服务名 --tail 100看日志里报什么错。如果是环境变量的问题检查新版本的官方镜像文档调整.env或Compose里的environment段落。如果是数据迁移问题通常需要在容器启动前手动执行一次迁移脚本。我自己的习惯是对于核心服务比如密码管理器不会让Watchtower自动更新而是给它打上com.centurylinklabs.watchtower.enablefalse标签月末手动更新一次。这样既不会错过新功能也避免夜间自动更新打断心血来潮时凌晨访问服务的场景。6. 几个提升使用体验的小技巧主体搭建完成之后gstack还有几个顺手的小优化成本不高但体验提升很明显值得一并记录。6.1 用Homepage做统一导航首页服务一多“入口”就成了新的问题。虽然NPM记住了域名但访问前还是要先想起来服务叫什么、域名是什么。我部署了一个轻量的导航页把所有服务图标集中展示。比较常用的是gethomepage/homepage容器支持从Compose环境变量和Docker标签里自动发现服务并显示状态。配置很简单一个容器加上一个配置文件把自己常用的几个入口写进去。实测下来导航页成为我浏览器书签栏里的第一顺位打开gstack的所有服务都从一个页面点进去不再需要记忆域名和端口。6.2 给容器设置合理的资源限制单机自托管最怕的是某个容器内存泄漏把整台机器的资源吃光。我在Compose里给每个服务都加了资源限制services: gitea: deploy: resources: limits: memory: 512Mdeploy配置在单机Compose模式下也能生效只要你使用Docker Compose v2。限制内存之后即使某个容器异常占用内存超过限制也会被系统杀掉或重启而不是拖垮整机其他服务。实际使用中Vaultwarden和FreshRSS这类服务128M到256M就很宽裕了Gitea这种代码托管可以给到512M根据你的机器配置来调整。6.3 定期记录资源占用规划扩容运行一段时间后可以通过docker stats看到每个容器的CPU和内存占用情况。我习惯把每个月的资源占用记录保存下来这样能发现哪些服务其实并不常用、哪些服务占用越来越高。gstack的好处是因为所有服务都井然有序地列在Compose文件里资源统计和规划扩容也变成了一件清晰的事。如果某个服务长期占用超过预期就可以考虑把它迁移到另一台机器或调整资源限制参数。6.4 把备份从本机复制到另一台设备备份再好如果备份文件存放在同一台服务器上那么服务器硬件故障时备份也会跟着消失。我的做法是把备份目录用rsync增量同步到局域网里的一台NAS设备。这部分也是脚本里的一段rsync -av --delete /srv/backups/gstack/ usernas:/volume1/backups/gstack/这样即使服务器整块硬盘报废备份依然在NAS上。如果连NAS都没有用一块带USB接口的外置硬盘定期导出备份也可以关键是“备份不能只存在同一台设备里”。这个原则比备份脚本本身更重要。我自己在实际维护过程中最深的体会是gstack的价值不在于某个特定工具有多厉害而在于把“管理多服务”这件事的复杂度降下来。只要目录结构一致、配置集中、备份路径单一即使未来要加服务也只需要在Compose里增加一段定义然后“新服务器几分钟接盘”这个特性就一直在。希望这套思路能帮你减少折腾的时间把更多精力留给真正想用的服务本身。
返回列表