AWS EC2のインスタンスを移管してみた

by ysawa

Publishers は、開発当初から Amazon Web Service(AWS) を利用して運用されています。そして、 AWS の中でも Amazon Elastic Compute Cloud (Amazon EC2) がメインでサービスの根幹となる機能をになっています。

今回、この EC2 のインスタンスをサービスを止めることなく、段階的に移管したというお話をしていこうと思います。 HTTPサーバを自分で建てようかと思っている人には、ちょうどいい内容かも!?

サーバ代がめちゃ高い

実は、 Publishers はかなり昔からあるサービスで、 EC2 のインスタンスも、古くて、今から考えるとコストパフォーマンス的には最悪の状況です。こちらのグラフを御覧ください。

サーバ代だけで毎月10万円ほど課金しています。現在のビジネスモデルを考えると、サーバ代が利益率の直接的な要因になっているので、これをどれだけ下げられるかにフォーカスしていかなくてはいけません。今、ドル円が111円くらいですが、円安の影響も利益率に跳ね返ってしまうので、何としてでも、サーバ代を浮かせていきたいところです。

いくつかあるEC2インスタンスのうち、今回注目したのが、ロードバランサ (lb1) です。これは、 c1.medium というインスタンスタイプで、一ヶ月起動しっぱなしにすると、それだけで1万円は下らないです。そして、アプリケーションが入っているインスタンスに比べると移管が楽そうな感覚があります。まずは、これにメスを入れていこうと思います。

まずは調査

Publishers は他社からある日突然、引き継ぐことになったのですが、なかなかサーバ構成を把握する時間がなかったです。しかし、そうも言ってはいられません。 lb1 がどのような働きをしているかをじっくりと見てみます。

top コマンドを叩いて、リソースの使用状況を確認します。

$ top

がばがばですね。ほとんど仕事していないような状況だということが解ります。

また、サーバ設計など何もいただいていない状況の中、手探りでインスタンスを移管するので、このサーバがどのような働きをしているのかを知らなければいけません。そのときには、まず、どういうプロセスが動いているのかを知れば良さそうです。 ps コマンドを使って見てみます。

$ ps aux

これによって、ほとんど Nginx というHTTPサーバが動いているだけのインスタンスだということが解りました。新しいサーバには Nginx をほぼ同じ設定ファイルと共にインストールするだけだということが解りました。

ここまでの話だと、 ELB (Elastic Load Balancing) で良いではないかという疑問の声も上がりそうです。しかし、今回の Nginx の設定が意外と複雑なため、汎用のロードバランサを入れてしまうとおそらくアプリケーションサーバ側の改修を入れないといけなくなります。なので、今回は、 ELB の導入は見送りました。

インスタンスの作成

EC2インスタンスを作っていきます。 Amazon Linux は、あまり上手く運用できる気がしないので、 Ubuntu Server 16.04 LTS を選択しました。

コア数もメモリも必要ないので t2.small にしました。 t2.small と c1.medium を比べるとコア数は減りますが、メモリが若干、 t2.small の方が多かったりします。

デフォルトの設定のまま、インスタンスをローンチします。プライベートキーをどうするか聞かれるので、いつも使っているものを設定しました。

セキュリティグループの設定

適切にセキュリティグループを設定しないと、 SSH でログインすることができないとおもいます。自分のIPの 22 番を開放するのと、あとは、今回はロードバランサなので、80番(HTTP)と443番(HTTPS)だけ開放します。

セキュリティグループの画面にアクセスし、

EC2インスタンスの画面で、インスタンス名の上で右クリックを押してセキュリティグループを設定します。

最後に、セキュリティグループがきちーっと設定されているかどうかを確認します。

SSHでログイン

やっとこれで、インスタンスにログインできるようになりました。ログインは、 ec2-user ではなくて、 ubuntu となります。ホストの IP は、 IPv4 Public IP と書かれているものを使用します。

$ ssh ubuntu@{EC2のホストIP} -i {プライベートキーのパス}

ロケールの設定

SSH でログインすると毎回、

Last login: Thu Mar 23 10:32:00 2017 from 123.123.123.123
_____________________________________________________________________
WARNING! Your environment specifies an invalid locale.
 The unknown environment variables are:
   LC_CTYPE=UTF-8 LC_ALL=
 This can affect your user experience significantly, including the
 ability to manage packages. You may install the locales by running:

   sudo apt-get install language-pack-UTF-8
     or
   sudo locale-gen UTF-8

To see all available language packs, run:
   apt-cache search "^language-pack-[a-z][a-z]$"
To disable this message for all users, run:
   sudo touch /var/lib/cloud/instance/locale-check.skip
_____________________________________________________________________

という警告が出るので、いろいろと試しましたが、使えるロケールを

$ locale -a
locale: Cannot set LC_CTYPE to default locale: No such file or directory
C
C.UTF-8
en_US.utf8
POSIX

のようにして確認して、その後、

$ sudo locale-gen en_US.UTF-8

として、 en_US.UTF-8 を標準のロケールとすることができました。これで、先ほどの警告は出なくなりました。

必要そうなパッケージのインストール

Ubuntu なので、標準のパッケージ管理ソフトは、 apt-get ですね。必要そうなパッケージをどんどんインストールしていきます。

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install -y python-pip python-dev build-essential libffi-dev libxml2-dev libxslt1-dev zlibc zlib1g-dev libreadline-dev libxslt1-dev libjpeg62-dev libfreetype6-dev libsqlite3-dev libssl-dev libgdbm-dev libbz2-dev

標準のエディタをVimに

nano とか Emacs の操作方法は閉じる操作もろくにできないので、 Vim を標準のエディタにします。これによって visudo したら nano を開くというバグを直すことができます。

$ sudo update-alternatives --config editor
There are 4 choices for the alternative editor (providing /usr/bin/editor).

  Selection    Path                Priority   Status
------------------------------------------------------------
* 0            /bin/nano            40        auto mode
  1            /bin/ed             -100       manual mode
  2            /bin/nano            40        manual mode
  3            /usr/bin/vim.basic   30        manual mode
  4            /usr/bin/vim.tiny    10        manual mode

Press <enter> to keep the current choice[*], or type selection number: 3
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/editor (editor) in manual mode

ntpのインストール

サーバの時刻設定が狂うと、様々な API との通信で、エラーになることがあります。 ntp をインストールしていきます。どうやら、ntpはインストールされているようなので、有効可するだけでした。

$ sudo apt-get install ntp -y
$ sudo service ntp start
$ sudo systemctl enable ntp

ファイアウォールの設定

セキュリティグループは、上で設定済みですが、念には念をということで。 Ubuntu には ufw という素敵なファイアウォールがあります。設定は、

$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
$ sudo ufw allow 22
$ sudo ufw allow 80
$ sudo ufw allow 443
$ sudo ufw enable # or sudo ufw reload

としました。これによって、内部へのアクセスを制限、外部へのアクセスを許可した上で、内部への SSH(20) 、 HTTP(80) 、 HTTPS (443) を許可しました。

ホスト名の設定

EC2 のインスタンスは、落ちてしまうと基本的に IP が変わってしまいます。これによって、ホスト名がIPベースのものだと問題が発生する可能性があります。ホスト名を適切なものに変えておきましょう。

$ sudo hostnamectl set-hostname lb3.publishers.fm
$ sudo hostnamectl
   Static hostname: lb3.publishers.fm
         Icon name: computer-vm
           Chassis: vm
(以下略)

Nginxのインストール

いよいよ、HTTPサーバのインストールです。 lb1 と同じく Nginx を選択します。

$ sudo apt-get install nginx -y
$ sudo service nginx start
$ sudo systemctl enable nginx

この後、 IP 打ってアクセスすると、お決まりのあの画面が見られます。

ログも一応確認しておきましょう。

$ sudo tail -f /var/log/nginx/access.log
126.66.4.51 - - [24/Mar/2017:07:27:22 +0000] "GET / HTTP/1.1" 200 6067 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" "-" 0.071

としてログも残ることを確認しました。移管のときは、元々のサーバにアクセスが行ってしまうなどのミスがないようにきちーっと確認することが重要です。

SSL を設定ファイルで適応後 SSL が機能しているかチェックします。

ワイルドカードドメインでは一番安い Positive SSL Wildcard を使用しています。 Publishers は、配信者用にサブドメインを切って使用するので、ワイルドカードドメインに対応した SSL ではないといけないです。

logrotateの設定

logrotate を設定することで、ログを一定期間毎に保存させたりログの肥大化を防ぎます。

コマンドを叩き、 logrotate が使えることを確認します。

$ sudo apt-get install logrotate -y
$ logrotate
logrotate 3.8.7 - Copyright (C) 1995-2001 Red Hat, Inc.
This may be freely redistributed under the terms of the GNU Public License

Usage: logrotate [-dfv?] [-d|--debug] [-f|--force] [-m|--mail=command] [-s|--state=statefile]
        [-v|--verbose] [--version] [-?|--help] [--usage] [OPTION...] <configfile>

/etc/logrotate.d/nginx があれば、それでほとんど設定は完了していて、

/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    prerotate
        if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
            run-parts /etc/logrotate.d/httpd-prerotate; \
        fi \
    endscript
    postrotate
        invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

の様になっていれば、問題ないです。 rotate で14日間ログを保持してくれて、それ以降は compress によりGzip圧縮されます。ただし、 delaycompress されているので、1回目のローテーションでは圧縮されずに、2回目以降のローテーションで圧縮されるようになります。

logrotate が毎日動いているかどうかは、

$ sudo cat /etc/cron.daily/logrotate

とか、

$ sudo cat /var/lib/logrotate/status

でチェックできます。ここまでで、基本的なHTTPサーバができあがりました。実際、もっとやった作業としては細かいですが、割愛させていただきます。

Cloud Watchの設定

Cloud Watch による監視も入れておきましょう。

  • 落ちているかどうか StatusCheckFailed >= 1 for 10 minutes
  • CPU使用率が連続して 70% を超えているか CPUUtilization >= 70 for 5 minutes

の2つの監視設定をしました。では、AWSの画面に戻って、

status check cpu などで監視する項目を検索するとよいでしょう。

アラート先や、分単位での設定をします。

これで監視の設定は完了です。落としてみたりしてメールが飛ぶか確認して見るとよいです。 Cloud Watch の監視は安く、数十円とかで絶大なる安心感を得ることができます。ぜひ使ってみてください。

Elastic IPの設定

ロードバランサのために、Elastic IP(固定IP)を設定します。

Scope は VPC のものにしないといけません。ご注意ください。 Allocate で設定できます。

作成できたら、EC2インスタンスに最後関連付けして完了です。

Classic Linkの設定

古い EC2 インスタンス (non-VPC) を使用している場合は、おそらく VPC には直接接続できません。なので、 Classic Link を設定することで、おなじ VPN 内で VPC 内のインスタンスと通信できます。

VPC を指定すると、その VPC 関連のセキュリティグループを選択することになります。

ここが、一番何していいかわからず苦労したところかもしれないです。ネットで検索してのですが、 non-VPC のインスタンスをどうやって、 VPC に接続するのか上手く探しあげることができませんでした。こういうときは、大体は問題設定を脳に与えて昼寝することです。目覚めたときに答えが見えるようになっていることが多いです。

Route 53の設定

Route 53(DNS) の設定をします。 Elastic IP で作成したIPアドレスを、 Route 53 の設定画面に入力していきます。

数分から数時間するとDNSレコードが切り替わっていることが確認できるはずです。 nslookup コマンドで確認しましょう。

$ nslookup publishers.fm
Server:     10.0.1.1
Address:    10.0.1.1#53

Non-authoritative answer:
Name:   publishers.fm
Address: 123.123.123.123

適切なIPアドレスに切り替わっていれば、完了です。そして、十分な時間が立った後、旧インスタンス lb1 にアクセスが来ていないことを確認します。

$ tail -f /var/log/nginx/access.log

このときに、ログがタラタラと出力されるようだとまだ、DNSレコードが切り替わっていない可能性があります。

ちなみに、最後は、検索エンジンとかマーケティングツールのボットからのアクセスだけになりました。 TTL (Time to live) をあえて無視して必要に応じてキャッシュさせているのでしょう。帯域に負荷をかけないための工夫なのかもしれません。

46.229.168.72 - - [26/Mar/2017:21:24:02 +0900] "GET /article/14506/ HTTP/1.1" 200 9412 "-" "Mozilla/5.0 (compatible; SemrushBot/1.2~bl; +http://www.semrush.com/bot.html)" "-" 5.369
46.229.168.68 - - [26/Mar/2017:21:24:03 +0900] "GET / HTTP/1.1" 200 7190 "-" "Mozilla/5.0 (compatible; SemrushBot/1.2~bl; +http://www.semrush.com/bot.html)" "-" 0.570
46.229.168.69 - - [26/Mar/2017:21:24:17 +0900] "GET /article/7979 HTTP/1.1" 301 5 "-" "Mozilla/5.0 (compatible; SemrushBot/1.2~bl; +http://www.semrush.com/bot.html)" "-" 0.184
46.229.168.74 - - [26/Mar/2017:21:24:31 +0900] "GET /editor/755/ HTTP/1.1" 200 4276 "-" "Mozilla/5.0 (compatible; SemrushBot/1.2~bl; +http://www.semrush.com/bot.html)" "-" 0.288

このようにして、DNSレコードが確実に切り替わったことを確認した後、 lb1 を落として完了になります。TTL が切れてからも、大体1日以上掛かりますね。スマホや IE とかは特に、DNSをずーっとキャッシュするみたいな動きをしていました。

移管を終えて

EC2 の古いロードバランサの設計を調べつつ、新しく作成したロードバランサに切り替えることでコスト削減を図りました。

サーバ停止することなく、問題もなく移管が完了できたので、 AWS のインフラの簡易さと汎用性の高さに感謝するところではあります。特に、 Classic Link などの前方互換の機能が充実しているのもさすがというところですね。

心臓を痛めることなく移管ができ、この勢いのまま、他のインスタンスも新しいものに移管していければ幸いです。時間が許すのであれば。

この記事を読んだあとに

ysawa

エヌ次元株式会社代表取締役
東京工業大学工学部計算工学専攻卒業
符号理論の応用に関する研究
在学中よりフリーランスエンジニアとして活動
「持続可能な設計」を得意領域とする
会社設立後も設計からアプリ制作や
Webサイトのコーディングまでを幅広く担当
セキュリティスペシャリスト

 このブログについて

このブログは、プログラマやエンジニアのためになる情報を垂れ流しています。
ちょっと異端的なものも含まれているかもしれません。