/ Blog

木匣子 2017 on AWS

原来用的共享主机不支持 Nodejs 应用,于是就将新的博客架设到了 AWS 上。本文记录一些迁移时遇到的技术问题。

Target

保留原来两个博客:木匣子 2010(基于 wordpress)和 木匣子 2013(基于 typecho),并设置重定向。设置 Nodejs 环境运行 Ghost 博客引擎,三博客共存。

How

EC2

AWS 提供了 EC2 云服务器,可以选择所需的服务器镜像(image)随时创建新实例(instance)。并提供公网 IP ,使用 SSH 连接 或 Microsoft Remote Desktop 即可接管整个服务器。比起用 cPanel 管理来服务器,简直是爽翻天。

Region

AWS 是一个全球性的服务,可以根据需要选择服务器所在的地区。因为只是个人博客,所以我选了离我最近的澳洲境内的悉尼机房。

Image

本来想使用 CentOS,但是看到版本比较低,就跳过了。扫了一遍列表看到 AWS 自家的 Amazon Linux AMI,查了一下也是基于 RHEL 定制的,感觉可以试试,于是就选了它。

后来装 Ghost 的时候发现 ghost-cli 1.x 暂时是基于 ubuntu 深度定制的,结果折腾了好久。早知道选 ubuntu 了,大学期间白当了几年 ubuntu 用户,Orz。

Instance Type:Cpu / Memory

一开始觉得运行个 LAMP + Node,放三个博客,1 core / 1 GB 的 t2.micro 就够用了吧,结果 Node 跑起来后没用多久,就耗尽内存自杀了。只好上 1 core / 2 GB 的 t2.small,价格自然是贵了不少。还好一开始是用按需实例先测试。等稳定了,再切换成预留实例(包年),可以得到 30% 的折扣。这点也正是云计算的优势。

Elastic IP

创建完实例后记得申请一个 Elastic IP 并绑定到实例上,不然每次重启实例后会被分配新的 IP 。

SSH

创建实例的时候,可以选择在本地创建并上传密钥,也可以让 AWS 生成一个给你。反正我没有什么安全洁癖也比较懒,就直接用 AWS 自动生成的了。如果不放心,可以用 ssh-keygen 命令自己生成一对密钥。

接下来在本地创建一个 ssh 服务器配置,在 ~/.ssh/config 添加以下几行,这样就不需要每次指定 key 和 服务器地址:

Host ec2
HostName <public-dns>.compute.amazonaws.com
User ec2-user
Port 22
IdentityFile /path/to/key_for_ec2.pem

以后要登陆服务器只需要输入 $ ssh ec2 即可。如果需要复制文件到服务器,可以用 $ scp ./local ec2:/remote/path,非常方便。

LAMP

平时在 Mac 上主要是用 MAMP 管理本地开发环境。所以相较于 Nginx 我比较熟悉 Apache 。连上云主机第一件就是 LAMP 全家桶来一套。AWS 的社区文档非常健全,像是安装 LAMP 这种家常便饭有很详细的资料可以参考,整个过程非常顺利。

phpMyAdmin

不想记一堆的 MySQL 命令,装一个 Web 数据库管理工具是必不可少的。默认只能通过 127.0.0.1 访问 phpMyAdmin ,但由于家里没有固定 IP ,所以改成了 Http Auth 的方式,加上数据库原有的密码,一共两道防线,全部用随机生成的强密码。

MySQL

接下来就可以创建用户以及数据库,并导入之前备份好的数据库。这里会遇到备份的数据库大小超过上传限制。可以修改 php.ini 并重启 apache 取消限制。或者用 scp 将备份文件上传到 server,然后在 mysql 里 > source backup.sql 导入备份,绕过限制。

Apache

创建 VirtualHost 并设置相应的转发。这里以 木匣子 2017 为例:

<VirtualHost *:443>
  ServerName 2017.mutoo.im
  ServerAlias blog.mutoo.im

  ErrorLog "logs/2017.mutoo.im-error_log"
  CustomLog "logs/2017.mutoo.im-access_log" combined

  RewriteEngine on

  RewriteCond %{SERVER_NAME}    =blog.mutoo.im
  RewriteCond %{REQUEST_URI}    ^/2010/    [OR]
  RewriteCond %{REQUEST_URI}    ^/2011/    [OR]
  RewriteCond %{REQUEST_URI}    ^/2012/    [OR]
  RewriteCond %{REQUEST_URI}    ^/2013/01/ [OR]
  RewriteCond %{REQUEST_URI}    ^/2013/02/ [OR]
  RewriteCond %{REQUEST_URI}    ^/2013/03/
  RewriteRule ^(.*)$ https://2010.mutoo.im/$1 [L,R=permanent,NE]

  RewriteCond %{SERVER_NAME}    =blog.mutoo.im
  RewriteCond %{REQUEST_URI}    ^/2013/12/ [OR]
  RewriteCond %{REQUEST_URI}    ^/2014/    [OR]
  RewriteCond %{REQUEST_URI}    ^/2015/    [OR]
  RewriteCond %{REQUEST_URI}    ^/2016/01/ [OR]
  RewriteCond %{REQUEST_URI}    ^/2016/02/
  RewriteRule ^(.*)$ https://2013.mutoo.im/$1 [L,R=permanent,NE]

  ProxyPreserveHost on
  ProxyPass / http://localhost:2368/

  SSLCertificateFile /etc/letsencrypt/live/mutoo.im/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/mutoo.im/privkey.pem
  Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>

原来的博客都有开启固定链接(permalink),所以搜索引擎收录的链接都是当时的地址。为了保证这些链接仍然可以访问,减少死链,免得网站被搜索引擎降权,可以通过 HTTP 301 永久重定向到新的域名。可以看到上面的 VirtualHost 设置了相应的 RewriteCond 和 RewriteRule 等规则。这里我只用了年份和月份,即可满足所有文章的重定向。如果还要重定向自定义页面,可以另外添加规则,具体玩法参见这里

Let's Encrypt

已经 2017 年了,该给博客上 HTTPS 了。当下最火的选择必然是 Let's Encrypt。而且它提供了全自动的 Certbot 证书申请/更新脚本,并且会自动 Update 你的 Apache VirtualHosts 。整个过程基本上不费吹灰之力。AWS 也提供了相应的文档

Cron task

由于 Let's Encrypt 的证书有效期只有三个月,所以需要在三个月内进行 renew ,不然就会失效。如果怕忘了,可以设置一个自动任务进行自动 renew:

# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
  0  2 15  *  * root /home/ec2-user/bin/certbot-auto renew

并不需要真的掐着点运行这个 renew ,官方的文档是设置一周 renew 一次。只要频率不超过 certbot 的 API 限制就行了。

Route 53

LAMP 服务都架好了,我在想是不是要自己架个 DNS 服务器管理子域名。但是我只有一个 EC2 实例,DNS 至少也要两个 Servers 才比较稳定。但是再开一个实例只跑 DNS 也太费钱了吧。幸好 AWS 提供了非常傻瓜化的 Route 53 服务,只要在里面创建自己的 Hosted Zones 就可以。并且是按查询次数收费,对个人博客来说非常省。创建后将得到的 4 个 Name Servers 地址填到你的域名提供商配置等生效即可,一般 24~48 小时。

Ghost

搬家搬得差不多上,该添置新物件了。

nvm

虽然 ghost 官方文档不推荐使用 nvm 管理 node (估计是因为目前 ghost-cli 对路径处理比较死板),但是我就好这口。

# install nvm via curl
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash

node

运行 $ nvm install v6 安装 6.x LTS 的 nodejs

ghost-cli

一开始在 1 Core / 1GB 的实例上运行 $ npm install ghost-cli -g 竟然因为内存不足挂掉了。到这里还没换 2GB 实例,而是通过 $ yum install yarn 装了个 yarn,再通过 $ yarn global add ghost-cli 装上了 ghost-cli,果然 yarn 才是最省油的灯。

install

之后照着文档安装 ghost ,并不是很顺利。遇到了 nvm 的坑,于是 sudo 用户找不到 /usr/bin/node,只好手动执行了数据库初始化命令。另外因为 Amazon Linux 没有 Systemd,所以 ghost-cli 的进程管理也基本上废了。经过一番搜索,可以用 pm2 这个非常牛屄的跨平台进程管理器代替 ghost-cli,这货还能一键安装自启动脚本,赞一个。

$ cd /path/to/ghost

# start and add process to pm2
$ pm2 start current/index.js --name "ghost"

$ pm2 show ghost
 Describing process with id 0 - name ghost 
┌───────────────────┬────────────────────────────────────────────┐
│ status            │ online                                     │
│ name              │ ghost                                      │
│ restarts          │ 2                                          │
│ uptime            │ 3D                                         │
│ script path       │ /home/ec2-user/ghost/current/index.js      │
│ script args       │ N/A                                        │
│ error log path    │ /home/ec2-user/.pm2/logs/ghost-error-0.log │
│ out log path      │ /home/ec2-user/.pm2/logs/ghost-out-0.log   │
│ pid path          │ /home/ec2-user/.pm2/pids/ghost-0.pid       │
│ interpreter       │ node                                       │
│ interpreter args  │ N/A                                        │
│ script id         │ 0                                          │
│ exec cwd          │ /home/ec2-user/ghost                       │
│ exec mode         │ fork_mode                                  │
│ node.js version   │ 6.11.1                                     │
│ watch & reload    │ ✘                                          │
│ unstable restarts │ 0                                          │
│ created at        │ 2017-07-30T09:26:45.308Z                   │
└───────────────────┴────────────────────────────────────────────┘
 Code metrics value 
┌────────────┬────────┐
│ Loop delay │ 0.56ms │
└────────────┴────────┘
 Add your own code metrics: http://bit.ly/code-metrics
 Use `pm2 logs ghost [--lines 1000]` to display logs
 Use `pm2 monit` to monitor CPU and Memory usage ghost

# Detect init system, generate and configure pm2 boot on startup
$ pm2 startup

运行了一段时间,发现跑没多久 ghost 就因为内存不足当机了。而 EC2 要给实例增加内存非常简单。只需要停止实例,然后在 Dashboard 修改实例类型(Type)就可以。将 t2.micro 改成 t2.small 之后重启启动 Instance ,可以看到内存变成了 2G 。

Next?

数据库有了,Web 服务器也上线了,域名也对接好了。还差什么?想了一下,发现少了邮件服务器。正好刚来澳洲的时候做了一些兼职,其中有跟 Mail Server 相关的业务。这东西也是一个大头,在考虑是不是用收费的服务,相对稳定一点;还是用 iRedMail 之类的开源方案自己架设一个。不过还是老问题,稳定的 Mail Servers 至少要一主一备两台,不然主服务器重启的时候会遗漏邮件。而我只有一个 EC2 实例,暂时搁置吧……


邮件服务器最后选用了 mailgun 的服务,因为平时主力邮件是 gmail ,很少用这个域名做邮件。所以 mailgun 的 10,000/月 的免费额度完全够用了。

Aug 7, 2017 Appended.