AmazonLinuxのEOL対応でLinux2にサーバー移行した際にやったこと（学びと失敗談）

# はじめに
<!-- 発端や概要を記載 -->

株式会社LITALICOでWebエンジニアをやってる@H-asakawaです。
新卒で入社してから3年が経ちました、はやいものです。

この記事は[LITALICO Engineers Advent Calendar 2020](https://qiita.com/advent-calendar/2020/litalico)の19日目の記事です。

AmazonLinuxのEnd Of Lifeは当初は2020年6月30日のはずでしたが[1月頃](https://aws.amazon.com/jp/blogs/aws/update-on-amazon-linux-ami-end-of-life/)に2020年12月31日に延長されました(なので後回しにしちゃっていました)。
EOL対応としてAmazonLinux2のEC2を新しく構築して本番サーバを移行した際の学びと失敗談などを書きます。
もう年末近いですしAmazonLinuxAMIで稼働してるEC2達はきっと居ないはずだと思いますが、もしこれから移行を担当する方が居れば参考になればと思います。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [前提](#Chapter1)
1. [移行計画を立てよう](#Chapter2)
1. [環境調査しよう](#Chapter3)
1. [手動構築しよう](#Chapter4)
1. [自動構築しよう](#Chapter5)
1. [動作確認しよう](#Chapter6)
1. [おわりに](#Chapter7)
1. [参考文献](#reference)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 前提
前提として私のインフラスキル感をざっくり掴んでもらえるように、インフラを学んできた過程を書いときます。

- 入社：インフラ何もわからん状態
- 入社〜1年目：業務でサーバにsshしたり障害対応に参加したりする際に先輩の横で見てる状態
- 2年目：社内のインフラ研修に参加
- 2年目：webブラウザが表示されるまで、というお題を中心に関連書籍を読んで勉強（研修）
- 2年目：ラズパイ複数台を使った仮想aws環境の構築ハンズオンで勉強（研修）
- 2年目：社内のISUCONに参加（一緒にチーム組んでた同僚が記事を書いてるので興味ある方ぜひどうぞ：[社内ISUCONに参加した話](https://www.kaqiita.com/entry/2019/12/22/111151)）
- 3年目：業務でfargateを使って小規模なサービス構築
- 3年目：AmazonLinuxAMIのEOL対応でサーバー移行（⬅︎イマココ）

私は最初は本番サーバにsshするのが怖かったです。っていうか本当の最初はsshってなに？状態でした。
先輩の横で作業を見てたり、指差し確認ヨシで参加してたんですが、その恐怖感は特に消えませんでした。
書籍を読んで勉強し始めたあたりから仕組みや全体像が少し把握できたことで恐怖感は薄れていきました。
社内研修に参加させてもらって少しずつ知識がついてきたことで、自分でググって調べて構築するところまではなんとかできるかなぁくらいの状態で、まだまだ新人気分です。
インフラは地道に調べて設定してうまく動いたりしたときは小躍りするほど楽しいけど、１ミスの影響範囲がデカイのがつらいです..（泣

ここからは、そんな私がサーバ移行にあたってどういう手順で進めようとしたか、そして実践してみて何に失敗したかを振り返りながら書いていきます。

<a id="#Chapter2"></a>
# 移行計画を立てよう

EOL対応の移行計画として、まず２択の方針を考えました。

- この機会に丸ごとfargateにしてサーバレス化する
- 新しいEC2を立ててEC2のみ移行する

移行までの猶予期間が短かったのと工数が私の分しか無かったので、完全サーバレス化した場合にデプロイ周りやsshしてconsoleなどで作業してる業務などをどう処理するか考えるのに手が回らなさそうだったのでやめました。
そこで今回のEOL対応ではEC2だけLinux2の新しいサーバーに移行することに決めました。
これなら影響範囲も少ないし、fargate化より全然簡単なはずです。

次にlinux2移行に関する公式情報をググって以下の事柄を確認しました。

- [Update on Amazon Linux AMI end-of-life](https://aws.amazon.com/jp/blogs/aws/update-on-amazon-linux-ami-end-of-life/)（全然関係ないけど音声が付いてるの面白い）
- Linux2への移行ツールなどは用意されておらず、新しくEC2を立てるしかない
- [プレアップグレードアシスタントツール](https://github.com/amazonlinux/upgrade-modules/blob/master/README.md)という移行にあたって問題がないか診断してくれるawsのサポートツールがある（使ってみましたが、特に移行の参考にはなりませんでした）

そして、何をやるべきか考えました。
EC2を新しいのに変えるだけならfargate化するより簡単そうだし、構築手順とかサーバ設定情報をこの機会に構成管理ツールでドキュメント化しておきたいなと思いました。

`infrastructure as code`をやってみたかったのと、自分の作業に不備があった場合にドキュメントが残ってないとデバッグも大変だと思ったのと、`development`,`staging`,`production`環境で合わせて5台のEC2を構築する必要があったためです。

手作業でもめっちゃ集中してやれば手順をミスらない自信がないでもないですが、絶対ミスりますよね。
手順書って大体どこか抜け漏れたりしてない？ってのもあるし、それ以前に単純に5台も同じ作業をやる気が起きませんでした。

当時は1台EC2を立てた後にAMIから5台立てて、sshして少し設定ファイルなどを弄ればすぐ終わりそうだし、構築を自動化したときの恩恵にコスパが見合うのだろうか？とか懸念してました。
しかし、いざやってみるとsshして設定を弄り回す負担がないだけですごく楽な気持ちになれたし、ボタンぽちで終わる安心感もあるし、構築の再現性が担保されたドキュメントも資産として残るので、3台以上くらいの複製の要件だったら自動化しちゃったほうが良いんだろうなと実感しました。

今回やると決めたこと

- 既存のEC2の環境調査
- Linux2で新規EC2を手動構築して動くことを確認
- 構築手順をansible化
- ansibleで5台のEC2構築
- devとstagingのサーバ移行作業
- productionサーバーの移行作業

<a id="#Chapter3"></a>
# 環境調査しよう

やったこと

- 過去の構築ドキュメント（Googleドキュメント）をざっと眺める
- sshしてインスコされてるライブラリを確認
- /etc以下や/var/log以下で稼働してそうなライブラリの設定ファイルの情報を取得して読む
- cronに直書きで登録されている処理がないか確認
- 環境ごとに異なる値が使われてる箇所がないか確認

たしかこれって本番サーバで使ってたよな。。という業務経験知と、サーバにsshして色々覗いていく中で得た知識を組み合わせて既存の環境を把握しようとしてました。

## このフェーズの反省点
移行前の環境調査ってどうしても少し考古学っぽくなるところがあるのかなと思います。なので下記の記事のアプローチをもっと参考にすればよかったなぁと後で記事を見て思いました。
[インフラエンジニアと謎のサーバ](https://qiita.com/kanga/items/979b0bdc4653e025483a)

大事だと思ったのはこれです

- 知ってそうな先輩によく聞く
- どんなプロセスが動いてるか調べる
- 通信を把握して何のポートで待ち受けてるか調べる

通信がどのポートで待ち受けているかや、プロセス稼働状況を見れば、いま何を使ってるのかは容易に把握できそうです。
ライブラリのインスコ有無だけをみててもある時期までは使っていたがある時期からは利用をやめてゴミが残ってるケースとかざらにありそうだし、issuesとかにその旨を記すドキュメントが残っていても全部読みにいくのはすごく大変でコストがかかります。
業務経験に基づく勘や闇雲な調査に頼らずとも、ちゃんと今のファクトを調べる方法があるじゃないかと気づかされました。

<a id="#Chapter4"></a>
# 手動構築しよう

新しいEC2を前と同じ状態で動かすために何を入れないといけないかが決まったら、あとはコツコツとインストールしていくだけです。

このフェーズは所々インストール手法でハマったりしますが、地道にやっていくだけの印象でした。

のちのち構築手順を自動化するために、どんな手順でどんなコマンド叩いたかは全部記録しておきます。

<a id="#Chapter5"></a>
# 自動構築しよう

手動構築してデプロイして動くとこまで確認できたら、構成管理ツールを使って自動で構築します。

今回はEC2を立てるところまでは手動でやっておいて、そのあとのサーバ設定作業を`Ansible`で自動化することにしました。
Ansibleってのを導入してみたよって話はこの記事が面白かったので興味ある方どうぞ
[エンプラ&オンプレでもAnsible導入成功したのでユーザー会で発表してきた](https://future-architect.github.io/articles/20180209/)

EC2を立ち上げる部分は今回自動化しませんでしたが、OSやボリュームを選択して既存のawsリソースを紐づけるだけなので、1台3分ほどですぐ終わりました。
awsリソース全般を構成管理ツール経由でしか触らない・弄らないという運用は安全なだけでなく設定情報がいつでもコードで共有できて望ましい理想状態ではありそうなんですが、タグ1つ付与するのにも構成管理ツールを経由するって中々硬い運用体制になりそうなので、ちょっと今回は敬遠しました。

`Terraform`で他のawsリソースも含めて全部管理できたら、あそこの設定値ってどうなってたっけ？って時にいちいちAWS consoleを開いてリソースをクリックして確認しにいく手間が減りそうなのが個人的には一番嬉しいです。

最終的に作った`Ansible`のplaybookファイル達はこちらになります。それぞれ一行解説。

## EC2構築用

- create-my-user.yml
真っ白な状態のEC2に新規ユーザーを作成し、sshできるようにします。

- delete-ec2-user.yml
上記のユーザーをメインで使うようにするため、ec2ユーザーはセキュリティ対策のためにも削除します。

- ec2-my-service.yml
タイムゾーンやホスト名を設定したり、必要なyumライブラリをインスコしたり、rubyやmysqlクライアントやapacheやpassenger、その他諸々沢山を入れたり、各種設定ファイルを配置したり、サービスの自動起動設定をします。

- install-newrelic-agent.yml
newRelicでcpuやmemoryの使用状況を監視してるので、agentを入れます。
詳細はこちら：[AmazonLinux2にAnsibleでNewRelicのエージェントを入れる方法](https://qiita.com/H-Asakawa/items/237946114156836d3cc6)

- install-td-agent.yml
td-agentでBigqueryにアクセスログを送信しているので、agentを入れます。
詳細はこちら：[AmazonLinux2にAnsibleでtd-agent v4を入れる方法](https://qiita.com/H-Asakawa/items/1645d4f8b682745e0a9c)

- test.yml
接続先ホスト名を表示したり、環境ごとの変数が読み込めてるかなど、コマンドを試したいときの確認用。

## セキュリティアプデ用

- ec2-yum-security-check-update.yml
脆弱性対応で使う。`$ yum --security check-update`を実行して、どのライブラリがどのバージョンに上がるのかをログに出力する。

- ec2-yum-security-update.yml
脆弱性対応で使う。 `$ yum --security update`を実行する。

- ec2-yum-update.yml
使う予定今のところなし。`$ yum update`を実行して全パッケージを最新版に更新。

fargateならsshができなくなる分、ユーザー管理も不要だしライブラリの脆弱性対応も不要なんですが、EC2を使う以上はセキュリティアプデの対応が避けられません。なんとかそれを楽にできないかと思ってセキュリティアプデを実行するだけのplaybookも用意してみました。

devで試しにupdateしてみて悪影響がなければ、staging2台に実行して、productionは念のためにAMIを取ってから2台同時に実行してしまえば、そこまで手間はかからないのかなという印象です。
（まだちゃんと運用してないのでなんともですが）

## Ansibleのディレクトリ構成
ansibleについて何もわからん状態から調べ始めた時、web上の記事や公式ドキュメントを読んでも、なぜそのディレクトリ構成をとっているのかの意図が掴みづらくて困りました。
なので実際に動かしてみて疎通確認したり、環境ごとに値を変える処理を追加していくなかで、やりたいことを実現するための最小限っぽいディレクトリ構成で運用することにしました。
全然練られてないしレビューもまだなので、あとで[ansibleベストプラクティス](https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_best_practices.html)とよく見比べてみようと思いますが、一応ここに載っけておきます。

```
infrastructure
└── ansible
    ├── ansible.cfg
    ├── ansible.log
    ├── 実行用playbookファイル群.yml
    ├── hosts
    ├── roles
    │   └── nrinfragent
    ├── templates
    │   ├── サーバ設定用ファイル群.j2
    │   ├── development
    │   ├── production
    │   ├── sitemaps
    │   ├── staging
    │   └── td-agent
    └── vars
        ├── dev.yml
        ├── prod.yml
        ├── prod2.yml
        ├── stg.yml
        └── stg2.yml
```

hostsは接続先の全サーバ情報を記載したファイルです。
playbook.ymlはansibleがサーバにsshして実行する処理を記載したファイルです。
varsディレクトリには各環境ごとの定数ファイルを配置してます。
templatesディレクトリにはJinja2という形式の各種設定用のファイルを配置してます。

ようするにj2形式のファイルが色んなserviceを動かす設定ファイルなんですが、設定ファイル内で各環境の定数を呼び出すことができるので、設定ファイルを共通化しつつ環境ごとに値を出し分けることが可能です。

Ansibleのこの仕様のおかげで環境ごとの設定値の差分を簡単に吸収することができ、３環境分の構築を自動化できました。

## このフェーズの学び
- 一度playbookを作っちゃえばあとはコマンド１発で自動設定してくれるので非常に楽チン
- 環境ごとに定数を持たせて設定ファイル内の値も柔軟に書き換えられるし、Ansible最高
- 最初はディレクトリ構成を眺めてもよくわからんと思うので、とにかく動かしてみて早く理解してから構成考えればおk
- ユーザー管理やssh周りの管理コストを何も気にしなくてよくなる点はやっぱりfargateのが良いなって思う
- 5台のwebサーバ以外にもjenkins、踏み台、定期タスク用、redash、などなどインフラ環境を見渡してみると継続的なメンテが必要なEC2達が沢山居ることに気づく
- それらのEC2にセキュリティアプデのplaybookが共通で適用できれば、脆弱性対応もいささか楽になりそう（な気がする）。
- たとえ小台数でも複製しないといけない場合は構成管理ツールによる自動化の恩恵はデカい

<a id="#Chapter6"></a>
# 動作確認しよう

ここが個人的には一番難しいフェーズでした。

新しいEC2を立てて、デプロイもうまくいきました。
もうこの後は事前確認作業を済ませて、ロードバランサに繋いで古いのを切り離すだけなんですが、確認リストを用意するのが非常に大変です。
ブラウザでの動作確認以外にチェックしないといけない箇所が色々ありました。

最終的には、確認項目を異常系・正常系で分けて、どこにどんなエラーが出ていなければ大丈夫か、どんな処理が成功してればいいのかをリストアップしていって、ひとつひとつ問題がないことを確認していきました。

ただ、そこに至るまでには思い至らない確認項目があったり、抜け漏れていた箇所があったりなど何度も修正と見直しを迫られました。これまでと同じ状態で稼働させることの難しさを感じた次第です。
ということで待ちに待った本番やらかし失敗談です。

## 本番でやらかした失敗

### Bigqueryのアクセスログの1日分欠損
私は何を血迷っていたのか、devやstaging2台のサーバを新しいEC2に切り替えた後、Bigqueryでアクセスログが正常に取得できることをチェックし忘れていました（施策のプランニングや効果検証に使っている重要な指標）。
元々、このEC2ではアプリケーションのアクセスログをtd-agentを使ってBigqueryに転送していました。
しかし、私はtd-agentをインストールすらしていません（Bigquery側のログ計測のことを完全に失念）。

もちろんログが送信されるはずもありません。
ですが、特別なことがない限りはdevやstagingというテスト環境のアクセスログをチェックしている人は事業部に誰も居ません。

そして、何も気づかないまま本番メンテの日を迎えてしまったのです。
ロードバランサに新しいサーバ2台を繋げて4台構成にして様子を見ることにし、次の日の朝を迎えました（この時も半分ログを欠損してるのにまだ気づけていない）。

朝になったら旧サーバとの別れを告げるデタッチ作業を始めました。
見事に切り替えが終わったかのように見え、エラーが出てないことをモニタリングして異常がないことを確認し、その日のメンテ作業を終えました（作業時間30分ほど）。

そして、次の日の朝、slackに通知されているredashの数値の値がおかしいことに気づき、ログが欠損してしまったことを知って絶望したのです。
（障害報告書きました..泣）

反省としては、なんでBigqueryのアクセスログというめちゃくちゃ重要なものが確認項目内に入ってなかったのかという点です。エラーログばかり見ていて、「エラー無し、ヨシ」ってしてれば当然こうなる運命でした。異常系だけでなく正常系をもっと見ておく必要がありました。
また、`td-agent.log`に異常がみられた場合はアラートをあげるという仕組みがあってもいいのかなと思いました。

さらにもうひとつの問題も発覚しました。

### cronのUTC時刻による実行

本番サーバではcronで定期タスクの処理を複数実行していたんですが、このタスクがサーバ切り替え後からJSTでなくUTC時刻で実行されていました。
そのため、本来なら実行されるべき処理が実行されていなかったり、9時間遅れで処理が実行されるなどの事態を引き起こしていました。

指定した定刻に実行したい処理（ユーザーへの様々なメール送信だったり、お知らせの作成だったり）が、未実行であったり、9時間遅れだったり、は中々まずい事態です。
結果、どの処理が未実行で、どの処理が9時間遅れとなったのか、影響範囲を正確に洗い出す調査が必要になりました。
（障害報告書きました..泣）

後で調べるとweb上には沢山の[注意喚起記事](https://qiita.com/owlbeck/items/b8379971890edb755cac)があったんですが、OSのタイムゾーンを修正した際はcronの再起動、もしくはサーバのrebootをしないと、cronのタイムゾーンが切り替わりません。
EC2起動時はタイムゾーンがUTCになっており、その後に私がansibleで構築した際にタイムゾーンをJSTにする設定を行ってはいるんですが、rebootしていなかったためにcronがUTCで実行されてしまったのです。

反省としては、そもそも各serviceの自動起動設定とかもしてあってそれの確認もしておくべきなのだから、一度くらいはサーバをrebootしておけよってところでしょうか。。
あとはcronに登録されてる時刻が正しいかどうかだけで判断せず、テスト用のタスクを回してみて正常な時刻に実行されることまで検証できてればよかったなと思います。

テストや事前確認の重要性が身に沁みました。。

<a id="#Chapter7"></a>
# おわりに

本番でやらかしてしまいましたが、なんとか移行作業を終えて`AmazonLinux２`に無事移行することができました。
インフラを学ぶきっかけを作ってもらった社内研修や、それを実施してくれた先輩方、同僚に感謝しています。
今後は現状構成を整理した図を作成してチームメンバーと共有し、今後のインフラ周りの改善点などを話し合っていこうと思います。

移行を進める中でいくつか寄り道をしたので、最後に少し紹介しておきます。

- AWS Perspective
この機会に既存のインフラ構成図（パワポ）を綺麗にしたいなと考えました。何か楽をする方法がないかということで、aws-perspectiveを使ってササっと図を作ってみようとしました。そして失敗しました。インフラ構成図の作成には向いてませんでした。しかも料金が高い($0.79/h)。そしてcloudformation経由で大量のリソースが作られるのはいいけど、リソース削除でエラーが出たりして面倒でした。
参考：[AWS Perspectiveを使ってリソースの構成図を可視化してみた。](https://qiita.com/SraSora/items/9b29832d291447fc7bdf)

- vscode（drawioプラグイン）
結局、インフラ構成図を自動生成したりコード管理したりするのは一旦諦めて、vscode上でdrawioが使えるプラグインを入れてポチポチ作成することにしました。更新はけっこう大変そうですが、サクッと作れるのでとても気に入りました。
参考：[VSCodeでDraw.ioが使えるようになったらしい！](https://qiita.com/riku-shiru/items/5ab7c5aecdfea323ec4e)

- awsの月次請求書明細をガッツリ読み込んだ
インフラを触るならコスト感覚をもっと養っておこうと、事業部向けのリソース別のaws月次請求書明細を`billings`でネチネチと読みました。すると、EC2のAMIを取得した際などにできたEBSやスナップショットなどに溜まったゴミが大層な課金をされていることに気づくことができました。今はいちいち計算しなくてもaws料金の0.79$/hとかを見るだけで、あーたけぇやべえとか感じられるようになったので以前よりもaws料金と仲良くなれた気がします（白目）。

- ArmベースEC2
ansibleのplaybookが完成した頃に、ArmベースのEC2がコスパ良いとの記事を読みました。せっかくならとちょっと試してみたくなり、a1インスタンスを立ててansibleを実行してみましたが、mysql5.7が非対応のようでインスコに失敗しました。ansibleを少し書き換えたり、mysql8系に上げないといけないかもですがコストダウンが見込めそうです。
参考：[Amazon EC2 を Arm に切り替えたら幸せなことしかありませんでした](https://developers.cyberagent.co.jp/blog/archives/27782/)

ここまで読んで頂いた方、ありがとうございます。
最近やってきたこと、考えてたことをただ羅列しただけの記事になってしまいましたが、何か皆さんの役に立つことが一つでもあれば幸いです。

明日は@negiさんの記事です。お楽しみに。

<a id="#reference"></a>
# 参考文献
- [Update on Amazon Linux AMI end-of-life](https://aws.amazon.com/jp/blogs/aws/update-on-amazon-linux-ami-end-of-life/)
- [Amazon Linux 2 FAQs 日本語訳](https://qiita.com/e__koma/items/600556415086032667f1#q-amazon-linux-2%E3%82%92%E3%82%AA%E3%83%B3%E3%83%97%E3%83%AC%E3%83%9F%E3%82%B9%E3%81%A7%E7%A8%BC%E5%83%8D%E3%81%95%E3%81%9B%E3%82%8B%E5%A0%B4%E5%90%88%E3%82%B3%E3%82%B9%E3%83%88%E3%81%AF%E3%81%8B%E3%81%8B%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B)
- [社内ISUCONに参加した話](https://www.kaqiita.com/entry/2019/12/22/111151)
- [エンプラ&オンプレでもAnsible導入成功したのでユーザー会で発表してきた](https://future-architect.github.io/articles/20180209/)
- [インフラエンジニアと謎のサーバ](https://qiita.com/kanga/items/979b0bdc4653e025483a)
- [AmazonLinux2にAnsibleでNewRelicのエージェントを入れる方法](https://qiita.com/H-Asakawa/items/237946114156836d3cc6)
- [AmazonLinux2にAnsibleでtd-agent v4を入れる方法](https://qiita.com/H-Asakawa/items/1645d4f8b682745e0a9c)
- [ansibleベストプラクティス](https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_best_practices.html)
- [ディレクトリ構成図を書くときに便利な記号](https://qiita.com/paty-fakename/items/c82ed27b4070feeceff6)
- [Amazon Linuxでタイムゾーンを変更してcronの実行時刻にも反映させる](https://qiita.com/owlbeck/items/b8379971890edb755cac)
- [AWS Perspectiveを使ってリソースの構成図を可視化してみた。](https://qiita.com/SraSora/items/9b29832d291447fc7bdf)
- [VSCodeでDraw.ioが使えるようになったらしい！](https://qiita.com/riku-shiru/items/5ab7c5aecdfea323ec4e)
- [Amazon EC2 を Arm に切り替えたら幸せなことしかありませんでした](https://developers.cyberagent.co.jp/blog/archives/27782/)