在 fly.io 免费运行 Ghost 博客:安装、备份和恢复

这篇文章介绍了在 fly.io 上免费运行 Ghost 博客,备份和还原数据的方法。备份和恢复数据对其他服务器上的用例具有参考意义。
目录

最近把论文交了后,手又痒痒,折腾了下 ghost。fly.io是一个 PaaS,现在可以在上面安装若干个 Ghost 博客,而且完全免费。

这篇文章介绍了在 fly.io 上免费安装、备份和还原 Ghost 的方法。备份和恢复数据对其他服务器上的用例具有参考意义。配合使用 cron,定时将全站数据备份到 Github,可以说,真的可以安心用 Ghost,舍弃静态博客了。

1.前置条件

  1. 注册 fly.io 账号,安装 flyctl 命令行,需要一张支持外币的信用卡。(必要
  2. 熟悉基本的 git 命令。(非必要)
  3. 了解 fly.io 的定价。(非必要)
  4. 了解 Mailgun。(非必要)

安装命令行

curl -L https://fly.io/install.sh | sh

对于 Windows,运行

iwr https://fly.io/install.ps1 -useb | iex

2.安装 Ghost

下面的命令在 Linux 和 Mac 上通用。

打开终端,执行:

mkdir blog #创建一个目录
cd blog #进入这个目录
flyctl auth login #登录,之后浏览器会弹出登录会话
flyctl launch --image=ghost:5 -r hkg --name=<AppName> --no-deploy

对最后一条命令的解释:

  • --image=ghost:5,表示 5.x 大版本会自动在每一次部署时更新,若打算选中一个具体的版本,比如 5.36.0,则把 5 换成它。官方镜像在这里

  • - r hkg表示选择服务器的节点为香港(离我们最近的节点)。 区域代码在这里

  • --name=<AppName> 表示创建的 app 名字,将<AppName> 替换成你自己的,并且注意,将来生成的默认网址是<AppName>.fly.dev,所以请想一个独一无二的名称。

  • --no-deploy表示暂时不部署。

这会在 blog 目录里生成一个 fly.toml 文件,使用文本编辑器打开它,使用如下代码覆盖。


app = "<AppName>"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[build]
  image = "ghost:5"

[env]
  url = "https://<AppName>.fly.dev"
  database__client = "sqlite3"
  database__connection__filename = "content/data/ghost.db"
  database__debug = "false"
  database__useNullAsDefault = "true"
  mail__from = "noreply@example.com"
  mail__options__auth__pass = "<YourMailgunPassword>"
  mail__options__auth__user = "postmaster@example.com"
  mail__options__host = "smtp.mailgun.org"
  mail__options__port = "465"
  mail__transport = "SMTP"

[experimental]
  auto_rollback = true

[mounts]
  destination = "/var/lib/ghost/content"
  source = "data"

[[services]]
  http_checks = []
  internal_port = 2368
  processes = ["app"]
  protocol = "tcp"
  script_checks = []
  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    force_https = true
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

    [services.ports.http_options.response.headers]
      Referrer-Policy = "strict-origin"
      Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload"
      X-Content-Type-Options = "nosniff"
      X-Frame-Options = "SAMEORIGIN"
      x-xss-protection = "1; mode=block"
      Permissions-Policy = "camera=(), microphone=(), geolocation=(), browsing-topics=()"

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

请将<AppName>替换成你自己的,并且,如果不用Mailgun,请删除[env]mail__开头的所有代码。

其中[services.ports.http_options.response.headers]这一部分是可选的,用于设定 http header。可以逐项搜索Strict-Transport-Security,比如,确定你是否要确定将这项加入。部署完毕之后,http header 检测地址在这里

检查完毕后,保存并关闭 fly.toml 文件。然后回到终端,继续执行:

  flyctl volumes create data  -r hkg --no-encryption --size 1 #创建一个大小为1Gb的卷,区域为hkg,不加密(有助于提高性能)。免费额度共3GB。
  flyctl deploy #部署

等待片刻,部署成功!会显示successful.

3. 初始化 Ghost

访问

https://<AppName>.fly.dev/ghost/

以初始化 ghost。在这个之后,你就可以自由使用 ghost 来发布内容了。绑定域名、备份和恢复数据是可选的操作。

4. 绑定域名

运行:

  flyctl ips list

以查看你的 app 的 IP 地址。然后在你的 DNS 服务器上为 ipv4 创建 A 记录,为 ipv6 创建 AAAA 记录,指向你的域名example.com(必须)。创建 CNAME,<AppName>.fly.dev,指向你的www.example.com (非必须)。也就是

类型记录
A@ipv4 address
AAAA@ipv6 address
CNAMEwww<AppName>.fly.dev

注意:在绑定域名之前,一定要确保正确创建 DNS 记录。

然后运行:

flyctl certs create example.com
flyctl certs create www.example.com

等待片刻,运行

flyctl certs check example.com
flyctl certs check www.example.com

以检查证书颁布的情况,一般只要 DNS提前配置正确,大约需要一两分钟。运行

flyctl ips list

以查看证书颁发情况。

注意:不要反复安装证书,因为有限额,每 7 天只能颁布 5 张证书。建议在调试完 app 后,再绑定。

5.重新部署

注意到 fly.toml 的

[env]
  url = "https://<AppName>.fly.dev"

是 fly.io 分配的地址,如果你点击 ghost 网站的主页标题,就会转向这个地址,所以,当你绑定了自己的域名,将其换成

[env]
  url = "https://example.com"

保存并关闭,然后运行flyctl deploy

6. 备份和恢复

以后每一次需要进入你本地的配置目录,才能正确使用 flyctl。

6.1 方法 1:使用 SFTP 备份 Ghost

优点是无需复杂配置,但复制速度极慢。

6.1.1 备份

首先登录(假设你以后需要备份);

cd blog
flyctl auth login

然后运行

flyctl ssh sftp shell -r -a <AppName>

成功后,会显示>>(我这里是红色的),然后输入

get /var/lib/ghost/content/

这将备份全站数据,包括主题、图片、视频、文章等等。速度很慢,请耐心等待。

也可以只备份图片和视频,运行

get /var/lib/ghost/content/images/
get /var/lib/ghost/content/media/

这在 blog 目录里将生成一个 content.zip,或,images.zip 和 media.zip。将它们备份到其他地方,或命名为 content-YYYY-MM-DD.zip。

6.1.2 恢复

现在假设你重新安装了一个新的 ghost,假设还是在 fly.io,那么安装目录仍然保持为/var/lib/ghost/content/,假设你今后在别的地方,比如 AWS 上安装 ghost,确保创建的目录为这个而不是一般教程所说的/var/www/ghost/content/,否则恢复数据会遇到麻烦。

仍然运行

cd blog
flyctl auth login
flyctl ssh sftp shell -r -a <AppName>

然后运行

put content.zip /var/lib/ghost/content.zip

成功后,在终端新一个标签页,然后运行

flyctl ssh console

进入 ssh 后,运行

apt-get update && apt-get upgrade && apt-get install unzip

以安装unzip。然后运行

cd /var/lib/ghost/
unzip content.zip

会直接覆盖原先的 content 文件夹。

为验证是否成功解压,运行

cd /var/lib/ghost/content/images/
ls #列出当前文件夹下的所有的文件

查看是否有新的数据。

为修复权限,运行

chown -R node:node /var/lib/ghost/content/

注意:可能的情况,无法正确覆盖 content 文件夹,那么请尝试只备份 /var/lib/ghost/content/images//var/lib/ghost/content/media/。此时运行的是

put images.zip /var/lib/ghost/content/images.zip
put media.zip /var/lib/ghost/content/media.zip

rm -rf /var/lib/ghost/content/images/
rm -rf var/lib/ghost/content/media/

unzip /var/lib/ghost/content/images.zip
unzip /var/lib/ghost/content/media.zip

可能会破坏 content 文件夹的权限,运行

ls -l /var/lib/ghost/content/

以查看 images 和 media 文件夹的用户和权限,若其权限为 root,则不能上传图片和视频了,则运行

chown -R node:node /var/lib/ghost/content/
chown -R node:node /var/lib/ghost/content/images/
chown -R node:node /var/lib/ghost/content/media/

以修复,注意,其中的node是在 fly.io 案例中的 ghost 用户,具体情况视ls -l /var/lib/ghost/content/命令的结果为准,总之确保 images 和 media 文件夹的用户和 content 下的其他文件的用户一样。

6.2 方法 2:使用 Github 备份 Ghost

优点是复制速度快,但缺点是安装配置相对复杂。

6.2.1 备份

1.登录并进入 ssh

cd blog
flyctl auth login
flyctl ssh console

2.安装 git

apt-get update && apt-get upgrade && apt-get install git

3.在 github 网页端先创建一个私密仓库(推荐),假设名为<YourRepository>

4.生成密钥

ssh-keygen -t rsa -C "<GithubEmail>"  #连续按回车键

5.终端新建标签,运行

flyctl ssh sftp shell -r -a <AppName>
get /root/.ssh/id_rsa.pub

回到 blog 文件夹里找到 id_rsa.pub 文件,用文本编辑器打开,并复制其中的密钥。将其添加到 github 的setting→SSH and GPG keys→SSH keys中,保存。

测试与 github 的通信

ssh git@github.com

留意有没有 successful 的字眼。

6.初始化 content 的 git

cd /var/lib/ghost/content/
git init
git config --global user.name "<GithubUsername>"
git config --global user.email <GithubEmail>
git remote add origin git@github.
com:<GithubUsername>/<YourRepository>.git
git config --global --add safe.directory /var/lib/ghost/content
git add .
git commit -m "auto backup"
git push -u origin master --force

确保成功 push 到 github。

7.新建一个脚本

apt-get update && apt-get upgrade && apt-get install nano
cd /var/lib/ghost/content/
nano auto_run.sh

复制以下代码到其中

#!/bin/sh

cd /var/lib/ghost/content # 切换到目录
git config --global init.defaultBranch master
git config --global --add safe.directory /var/lib/ghost/content
git pull # 拉取仓库
git add . # 添加暂存
git commit -m "auto backup" # 提交
git push --force

ctrl+O之后再回车,以写入;按ctrl+x,以退出 nano。

赋予该文件运行权限

chmod a+x auto_run.sh

以后,登录 ssh 后,可以直接运行

sh /var/lib/ghost/content/auto_run.sh

以执行新的 push。

6.2.2 恢复

1.登录 shh,重新安装 git,添加密钥,不再赘述。

2.清空 content 文件夹

cd /var/lib/ghost/
rm -rf content

会返回说,该文件夹无法删除,因为其busy,没关系,它已经被清空,但没有被删除。

3.初始化 content 文件中的 git,并强制 pull

cd /var/lib/ghost/content/
git init
git config --global user.name "<GithubUsername>"
git config --global user.email <GithubEmail>
git pull --force git@github.com:<GithubUsername>/<YourRepository>.git

4.验证是否成功拉取

ls

5.确保成功拉取数据后,重启 app

fly apps restart <AppName>

目前来看,重启或重新部署,会清除安装的环境和软件,但不会删除 ghost 的数据。

6.在 docker 中运行 cron 实在困难,不用尝试了。在普通虚拟机中,可以设置 cron,以定时运行

sh /var/lib/ghost/content/auto_run.sh