なにもしてないのにEC2インスタンスが壊れた

# この記事について

`DMMグループ Advent Calendar 2020` の 17日目の投稿記事となります
https://qiita.com/advent-calendar/2020/dmm

また、発表に向けた記事のため、スライドモードでの回覧を推奨しています。

---

# 自己紹介

名前 : 松田 弘樹
入社 : 2015年５月入社の6年目
担当 : バックエンド、インフラ担当
趣味 : 乗馬🐎 全乗協の5級ライセンス取得しました。 　

---

# 想定読者

* IaC (Terraformなど)の概要を理解している人
* CI/CDによる、自動デプロイの概要を理解している人
* AWSのEC2インスタンスを構築したことがある人

---

# 発端
## 通常の運用で
ある日、Terraformにて構築済みのEC2インスタンスから、作業のために `mysql`コマンドで接続をしてみると
` mysql: command not found `
というエラーコードが返却された

--- 

# 発端
## ！？
数日前まで、使えていたコマンドが使えなくなっていること示すをエラーが返却された。
histroyを確認してみると、実行したコマンド履歴が一切無い。つまり、EC2インスタンスが初期化されている。

自分「(構築後にインフラので変更していないはずなのに)何もしていないのに壊れた」

--- 

# 発端
## というわけで 
本記事では私が遭遇した `なにもしてないのにEC2インスタンスが壊れた` について、ストーリ仕立てでお話をしていきます。

最後にまとめがあるので、忙しい人はそちらへどうぞ

--- 

# 状況整理
まずは事象と影響範囲の確認から

## 事象
Terraformのtfstateで管理さている、EC2インスタンスが初期化された。

## 影響範囲
他のTerraform管理のインフラ要素に影響はなし。

---

# 環境について
概要をざっくり説明

## インフラ構成管理
基本と、Terraformを利用し、インフラ構成をコードで管理している

---

# 環境について

## Terraformコードを管理するリポジトリは2種類
*  inf: VPC、サブネット、ネットワーク定義など管理。デプロイは手動適用。
*  app: アプリ特有の定義管理。WEBサーバ、RDB定義など。EC2もここで管理。デプロイはCI/CDに組み込まれ自動適用。

---

# 環境について

## CI/CDフロー

利用しているツール類は次の通り。

- GithubEnterprise (ソースコードのリポジトリ管理)
- CircleCI (Docker Build、コンテナ上での単体テスト、Terraformのapply、Docker ImageのPUSH)
- AWS ECR (Docker Image用リポジトリ)
- AWS ECS (コンテナの実行環境)

上から順番に連携するフローとなっている

---

# 調査

ここからは調査です。
可能性のありそうな仮設をたてながら、検証を繰り返していきました。

---

# 調査

## 仮設1 手動で、メンバーか削除した可能性
一番わかり易いのは、誰かが手動でEC2を作り直したことです。
しかし、メンバーに確認をとってみましたが、該当者がいないため、この説否定されました

---


# 調査

## 仮設2 tfstateの状態が破損し、作り直した可能性

何かの理由でstateファイルが破損したことでEC2インスタンスがterraform管理から外れ、
新しくTerraform管理とするため作り直された可能性を考えました。

tfstateファイルの状態確認のため、手動で `Terraform plan` を実行しました。
結果、エラーなく正常に動作し、tfstateファイルは正しく機能しているため、この説は否定されました

---

# 調査

## 仮設3 TerraformのEC2の記述が修正されて、EC２が再構築した可能性

EC2インスタンスを管理しているtfファイルの直近の修正履歴を確認しましたが、直近ではtfファイルは修正されていませんでした。
症状発生時には編集されていないため、この説は否定されました

---

# 調査
他にどんな原因が考えられるかと仮設をかんがえていたなかで、調査をお願いしていたメンバーから新たな情報をいただく

## 新く判明した情報
CloudTrail上のイベントにおいて、Terraform実行ユーザが、EC2インスタンス対して `Terminateinstance` を複数回、実行していることが判明。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211196/90d20f6b-78ed-682e-e812-92a3ba87bf54.png)
同様に。EC2インスタンスが作成されているイベントも確認。

---

# 調査
上記の情報より、原因がTerraformが怪しいとの絞り込みができたので新たな仮設が浮かぶ。

## 仮設４ Terraformにおいて、意図せずEC２が再構築した可能性
EC2の何かしらのリソースが動的取得しており、その部分が更新されるたびにEC2インスタンスが再構築されるのではないか。

---

# 調査

というわけで、EC２関連の記載について細かく見ていきました。

## 当時のソース

AMI情報を`aws_ami` の `filter` で絞り込んだあと、`aws_instance`に指定する流れとなっています。

```
# AMI情報を取得
data "aws_ami" "amazon_linux" {	
  most_recent = true	
  owners      = ["amazon"]	

  filter {
    name   = "architecture"	
    values = ["x86_64"]	
  }

  filter {
    name   = "root-device-type"	
    values = ["ebs"]
  }

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "block-device-mapping.volume-type"
    values = ["gp2"]
  }	
}

# AMIのIDを指定して構築
resource "aws_instance" "rds_access_ec2" {
  ami = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro" 
}
```

---

# 調査

`aws_ami` のパラメータをひとつづつ確認していると、怪しい記述を発見しました。

## 怪しい部分

``` 
most_recent - (Optional) If more than one result is returned, use the most recent AMI. 
most_recent-（オプション）複数の結果が返される場合は、最新のAMIを使用します。
```

これだ！！！！!!!!!!!!!!!
原因が特定できました。

---

# 原因

`CDCDの自動デプロイフローにおいて、EC2のAMIを動的に取得しており、デプロイのたびに最新版のAMIを取得して、更新がある場合はEC2再構築を行うため。`

## 発生時のフロー

1. GithubEnterpirseに、コードをpushする。
2. 連携しているCircleCIで、push検知してCI/CDが起動する。
3. デプロイ処理のなかで、毎回最新のAMIイメージを取得する。
4. AMIイメージが更新されている場合、古いEC2インスタンスを削除し、新しいAMIイメージでEC2インスタンスを作成する。

このため、直接のEC2の定義部分のtfファイルの修正なくとも、EC2インスタンスが新しいものに差し替わってしまったのです。

---

# 対応
原因がわかったので対応をしていきましょう。

## 案1
EC2インスタンスの再作成を許容する。
EC2インスタンスの更新のたびに、コマンドが消失しないように、`aws_instance` の `user_data`に必要なインストールコマンドを定義しておく。

```
resource "aws_instance" "rds_access_ec2" {
  user_data = <<EOF
    #!/bin/bash
    sudo yum install -y mysql-community-client
  EOF
}
```
欠点は、ローカルにおいたファイルなどが消えてしまうことです。

---

# 対応

## 案2

AMIの値を固定値に変更する。
更新したい場合は、AMIの値を修正する運用とする。

```
resource "aws_instance" "rds_access_ec2" {
  # 固定値に修正
  ami = "ami-00f045aed21a55240"
}
```

欠点は、手動運用の手間が増えることとなります。


---

# 対応

どちらの案も欠点があるので、要件などと照らし合わせて判断すればいいかなと思います。

---

# まとめ

次の環境の場合は、意図せずにリソースが更新されることがあるので注意しましょう。

* インフラの構成を、Terraformなどでコード管理している
* デプロイが、手動ではなく自動でデプロイする
* リソースを動的に取得している部分がある

---

# Special Thanks

調査に協力してくれた @moroishisan 

---

# 宣伝
弊社ではエンジニアを積極募集中です!
https://dmm-corp.com/recruit/

個人あてに、カジュアルにお話が聞きたいかたもどうぞ！

---

# 引用

Data Source: aws_ami
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami

---

# ご視聴ありがとうございました