这个过程安全吗?我在开发环境中运行多容器设置完全没问题,但如果将其用于生产环境,当用户正在访问旧容器,而新容器正在启动并执行数据库迁移步骤时,发往旧容器的请求仍会使用旧的后端逻辑,并按照上一版本的定义保存数据,即使数据库迁移步骤已经结束(但在整个启动过程完成之前)。
虽然我知道这与 Discourse 本身无关(如果某个副本在升级前未停止所有副本,具有多个副本的环境也可能出现此类问题,除非你想实现高可用性,否则这可能不适用),但你描述的过程在总体上是否仍然安全?
我能想到的一点是,确保 Discourse 始终处于最新状态,以尽量减少重建之间的数据库迁移。但无论如何,这仍然不够理想,即使在这种情况下也可能出现问题。
多容器设置似乎是推荐的方案之一(尽管不是仅使用单个容器的标准方案),因此我认为它应该是安全的,我只是想多了。
你是否知道它在生产站点中是否运行良好(即在一个容器执行启动过程的同时,另一个容器仍在运行)?我只是想向那些已经在生产站点中这样做过的人了解一些反馈,确认它在多次重建后是否依然正常,是否存在一些需要注意的陷阱等……正如我所说,在开发环境中它运行良好。
riking
(Kane York)
69
如果你希望实现零停机时间,需要额外执行几个步骤:在新容器上禁用“迁移后操作”,完全部署新容器,启用“迁移后操作”,然后再次部署。这将阻止任何删除列的迁移操作执行,直到旧代码不再运行。
目前尚无相关操作指南,仅在以下页面有文档说明:
不过,大多数论坛每月承受一两分钟的停机时间通常是可以接受的。
4 个赞
pfaffman
(Jay Pfaffman)
70
是的。大多数情况下,不会存在破坏正在运行的容器的迁移操作。
您也可以将 web_only.yml 中的迁移功能禁用,待新容器启动后,在运行的容器内执行类似以下命令:
cd /var/www/discourse;SKIP_POST_DEPLOYMENT_MIGRATIONS=0 rake db:migrate
3 个赞
neounix
(Dark Matter)
71
是的,配合 nginx 作为前端 反向代理服务器,这在生产环境中可以完美运行。
以下是我的做法:
三个容器:
- 数据容器 (data.yml)
- Socket1 (socket1.yml)
- Socket2 (socket2.yml)
我们在 nginx 配置 中指定一个唯一的 Unix 域套接字,例如:
location / {
proxy_pass http://unix:/var/run/nginx.http.sock;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
}
然后,我们通过符号链接到实际的套接字来选择我们要上线的容器,如下所示:
假设我们希望 socket2 容器上线:
ls -sf /var/discourse/shared/socket2/nginx.http.sock /var/run/nginx.http.sock
假设我们想在 socket1 上进行更改,并使 socket1 上线:
cd /var/discourse
./launcher rebuild socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock
请注意,没有必要只 bootstrap socket1 容器,因为该容器通过其自己的共享目录/卷中的 Unix 域套接字暴露,因此这两个“Web 应用”容器可以同时运行:
- Socket1: /var/discourse/shared/socket1/nginx.http.sock
- Socket2: /var/discourse/shared/socket2/nginx.http.sock
这不会像暴露 TCP/IP 容器端口时那样出现“端口绑定冲突”。因此,我在生产环境中只暴露 Unix 域套接字(不暴露 TCP/IP 端口)。
当然,如果您愿意,也可以进行 bootstrap:
cd /var/discourse
./launcher bootstrap socket1
./launcher start socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock
这取决于您,但请记住,如果同时运行两个容器,两者都会运行 sidekiq 并执行计划任务,这是我们的经验;因此,我们偶尔会在两个容器中同步上传的文件。
这对我们来说运行完美,我们可以重建 Web 应用容器并使其上线,基本实现零停机时间。我们在生产环境中非常重视停机时间,并尽可能避免。
注意:
上述方法是为 Web 应用部分设计的,不适用于数据容器。我还没有为数据容器创建类似的解决方案;但谁知道呢,也许有一天我会花些时间为数据容器构建一个类似(但不同)的解决方案(某种“两个数据容器,同步数据库”的方法,目前在我的脑海中还完全未定)。
所以,我实际上是在做与此相反的事情:
正如我之前所说,在开发环境中它工作得很好。
我通常不在开发环境中设置这个,因为设置起来更耗时,而且在开发环境中没有必要,因为一点停机时间是可以接受的,毕竟只是“我和代码”(没有真实用户和机器人访问站点),而且我在开发中根本不使用 Docker(在桌面上)。
希望这能帮到您。
这里的“开发”指的是软件(例如插件)开发;而不仅仅是 Discourse 安装的“预发布(staging)”,我将后者称为“预发布”而不是“开发”(为了表述清晰)。
2 个赞
谢谢,我之前不知道这一点。这是一个很棒的功能,可以看到它能避免我之前提到的那种问题(虽然这无法避免对已存在列的逻辑修改,但这种情况应该比较少见)。
感谢回答。SKIP_POST_DEPLOYMENT_MIGRATIONS 看起来就是 @riking 提到的内容,也是我想要的,以避免迁移破坏正在运行容器中的操作。
谢谢你的解释。这对我来说是个不错的方案,在两个容器之间交替使用(以便在一个容器启动时另一个容器仍在运行)。
我提到的开发环境是指远程开发环境,我曾用它来测试多容器设置(无论是在同一台机器上,还是将容器分布在不同的机器上)。
我说的是开发环境而不是预发布环境,因为在预发布环境中,我会使用包含相同插件的 yml 文件,并连接生产数据库的备份进行测试。不过确实,如果我只是想搭建一个开发环境,在大多数情况下,我会采用单容器方案。
2 个赞
mcdanlj
(Michael K Johnson)
73
据我所知,这篇指南只是围绕以下几点说了很多话:
- 备份
- 创建一个全新的 Discourse 实例,虽然用词更多,但结果与直接运行
discourse_setup 2container 相同
- 恢复
为什么不在干净关机后、从独立的 containers/*.yml 文件启动两个新容器之前,直接将 /var/discourse/shared/standalone/{postgres,redis}* 移动或复制到 /var/discourse/shared/data 呢?备份/恢复似乎是一种过于笨重的方式来迁移所有这些数据,不必要地给整个过程增加了数小时。我是不是忽略了什么显而易见的事情?
我刚刚在我的测试 Discourse 上测试了这个流程,并顺便将 Redis 也分离出来了,以确保覆盖了所有方面。编辑:我已将描述移动到了一个新的主题:
该站点在没有备份/恢复周期的情况下似乎运行正常。是否有某些不那么明显但我应该检查的事项?
我对 一个相对较大的 Discourse 论坛 也执行了相同的流程,目前运行良好。我决定在生产环境中将新的 web_only 容器命名为 app,这样我的手指就会自然地执行正确的操作。在编写完新的 container/*.yml 文件后,整个迁移的停机时间仅为 12 分钟,远快于备份/恢复周期所需的时间。
pfaffman
(Jay Pfaffman)
74
只是因为这需要更多的工作量,并且要理解其运作方式。备份恢复则更加稳妥可靠。
1 个赞
mcdanlj
(Michael K Johnson)
75
我想我们只好保留各自的分歧了。我认为,如果一个人有能力运行多个容器,那么运行几条命令应该不成问题。而且,我并不觉得这些命令比在这里输入 bin/rails c 然后开始编写 Ruby 代码更难敲,也不比使用多个容器所需具备的技能更多或更复杂。
不过,我会将相关内容迁移到一篇新的独立帖子中,而不是让它埋没在这里的评论里。
1 个赞
pfaffman
(Jay Pfaffman)
76
这个观点很有道理。也许让流程看起来更可怕,反而能阻止那些不具备相关技能的人贸然尝试。
mcdanlj
(Michael K Johnson)
77
……而且,如果他们犯了错,在迁移之前进行备份是无可替代的!希望我在上面链接的文章中已经讲清楚了!
1 个赞
Stephen
(Stephen)
78
这正是你论点的漏洞所在。在 ./discourse-setup 中添加 2container 并不代表任何能力水平。很多人运行两个容器,仅仅是因为他们看到了类似的话题,便误以为其中有什么“秘诀”,或者觉得这是“该做的事”。
关于 Postgres 12 的讨论应作为一个警示,提醒人们注意由此增加的复杂性。使用备份作为状态转换的中间步骤,允许用户通过重命名单个文件即可回退到单容器模式;而一旦开始移动文件夹,这种简洁性就不复存在了。
2 个赞
mcdanlj
(Michael K Johnson)
79
@Stephen 你的论点有一个漏洞:多容器说明中充满了警告,强调你必须对更新负责并理解其工作原理,而上面的长篇说明又过于晦涩,恐怕任何看过的人都会放弃。请去阅读我的 https://meta.discourse.org/t/how-to-migrate-quickly-to-separate-web-and-data-containers/153355,然后告诉我,它不会吓退那些难以遵循步骤的人,或者它未能强调备份的必要性,以及在出现问题时能够回退到备份的能力!
当我迁移到一台性能更强的服务器(为了安全修复)后不久,执行了 ./launcher rebuild app,结果网站停摆了异常漫长的时间,其中大部分时间都花在重建容器的 PostgreSQL 部分上。那时我非常不愉快。正是在那时,我发现了双容器文档和这份文档,但实在不想再经历一次长达 4 小时的停机来迁移,所以我一直选择忍受 ./launcher rebuild app 带来的长时间停机,以避免恢复备份所需的 4 小时停机。作为一个略具能力的人,我长期以来对此配置被有效隐藏感到非常恼火。
关于 PostgreSQL 12 的帖子是一个很好的参考,因为人们最终会遭遇更多的停机时间,因为他们不得不多次重建整个应用,而实际上他们只需要重建 PostgreSQL 容器两次即可。由于存在 6 天自动删除的机制,我没法读完整个讨论串,但在我看来,完全看不出“不称职的多容器部署”是那里的大问题,甚至算不上主要问题。
(抱歉,有时候我有点厌倦这里那种“所有用户都不称职”的氛围。)
2 个赞
Stephen
(Stephen)
80
这可能对你来说没有意义,但对于我们这些在 meta 上待了 6 到 7 年、在 Support 频道协助他人的人来说,制定回滚策略始终是有意义的。
确实 会有漏洞混入测试通过的版本中,偶尔 RubyGems 的速率限制会影响重建,甚至 GitHub 也曾出现过短暂的波动。仅出于这一点,我认为没有任何理由进行一种状态变更,使得简单地重命名文件并执行 ./launcher start app 变得更加困难。
你的风险承受能力可能不同,如果是这样,你可以选择其他路径。对于那些经常帮忙收拾残局的人来说,当前的指南非常适用。
3 个赞
mcdanlj
(Michael K Johnson)
81
我觉得你并没有真正阅读我所写的流程,因为你的行文仿佛我从未强调过恢复能力的必要性。请仔细阅读我的说明,然后回来修改你的内容,使其成为对我指示的真实陈述。就目前而言,我觉得你是在对我“说教”,却连花点时间阅读我所写内容的礼貌都没有。
不过,我已经添加了许多额外的警告,包括顶部的一个警告。这些警告的数量已经超过了这篇帖子中的警告数量,而该帖子作为此迁移流程指令的权威来源,已经存在了五年并仍在发挥作用。
2 个赞
Benjamin_D
(Benjamin Decotte)
84
首先,感谢您为我们这些冒险者清理这片“丛林”:sweat_smile:,我大概几天后就会跟上您的步伐……
multisite.yml 文件里有什么内容?
cyberczar
(Michael Collins)
85
你好。
我最近在 VPS 上搭建了一个自托管的 Discourse 论坛。上传文件和备份都存储在 Wasabi 上。所有服务都托管在 Linode 上。
我使用了独立模板,与其他软件相比,其设置过程令人耳目一新。简直太棒了!纯粹是一种享受。我希望每个开源项目都能像 Discourse 那样在安装和设置上投入这么多精力。
不过,有个问题。我在另一台服务器上运行了一个专用的 PostgreSQL 数据库,该服务器仅可通过 RFC-1918 地址访问,无法从互联网直接访问,我希望 Discourse 能使用它。我不太喜欢将数据库服务器与 Web/应用服务器运行在同一台机器上。
那么,是否有可能将独立模板中的数据库分离出来,迁移到我的专用数据库集群中?
我假设我只需要对 Discourse 数据库执行 pgdump,将其迁移到专用数据库并恢复,然后在恢复/导入后对所有表执行 postgres vacuum analyze,最后将 Discourse 应用指向新数据库即可?
但我似乎找不到数据库凭据的存储位置。我查看了 app.yml,但里面似乎没有数据库相关的条目;当我查看 ../templates/ 文件夹时,也没有发现任何 yml 文件包含数据库凭据。
我尝试做的这件事真的可行吗?
1 个赞
cyberczar
(Michael Collins)
87
太好了!这正是我想要的。非常感谢!
现在要让 nginx 看到 CloudFlare 后面的真实 IP…… 
cyberczar
(Michael Collins)
88
如果内置的 PostgreSQL 数据库通常没有凭据,是否有一种方法可以从独立容器中运行 pg_dump 并导出数据库?
1 个赞
Falco
(Falco)
89
标准的 Discourse 备份是一个打包在 tar 归档中的 pg_dump,因此您可以直接使用它。
如果您想执行自定义操作,例如通过管道传输转储数据或使用其他格式,您随时可以这样做:
ssh root@forum
cd /var/discourse
./launcher enter app
su postgres
psql # 或 pg_dump
2 个赞