副業でTerraform化した時のお話

本記事は [個人開発 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/private-development) 19日目の記事です。

## 概要
[11日目の記事](https://qiita.com/yumatsud/items/c103d5633d1d16766a31) に引き続き、個人開発ではなく恐縮ではありますが、
副業で既存環境をTerraform化した時のお話を書かせて頂きます。

## 背景
私が副業でお手伝いしたサービスのインフラ（AWS）環境は、コード管理ができていない状況で、メインで構築した人の頭の中と、一部ドキュメントにネットワーク構成だけ記載がある状態でした。
(ただ、これが悪かったとかいう話ではなく、会社のリソース状況や事業状況などから、そこまでする必要がなかったため）

とはいえ、メインで構築した人の頭の中にしかないと、いざという時困るのでいつかはやりたいという話を聞いてました。

そして、一部の環境をEC2からFargateに移行しようとしていたのもあり、
そのタイミングで構築する環境はTerraformで構築しようということを決め、Terraform化に取り掛かりました。

## 方針
ネットワークなどは既存環境とも関わるのと、運用慣れしていない状態で全てTerraform化するのは怖かったので、下記のような方針で進めてみました

* DB（RDS）やS3のような永続化機構はやめておこう
* 既存のネットワーク周りは、コード化はするが、planを実行しても差分が出ないようにしておこう
* それ以外で新しく構築するものは、なるべきTerraform化していこう

## 既存のネットワーク周り
既に本番で運用しているサービスでもあるので、ネットワーク周りを下手にいじるのは怖い、、、でもコード化しておきたい、、、そんな時に便利だったのが [terraforming](https://github.com/dtan4/terraforming)というツールでした。
このツールを使い、下記の作業を繰り返し、planを実行しても差分が出なくなるまでひたすら繰り返します（これがなかなかの辛い作業w）

* `tf`ファイルの作成

```sh
$ terraforming <<対象resource。sgやaclなど>>
```

* `tfstate` ファイルの作成(or追記）

```sh
$ terraforming <<対象resource>> --tfstate  --merge=terraform.tfstate --overwrite
```

* `terraform plan` を実行し、差分が出たらtfファイルやtfstateファイルの修正

この作業を実施したおかげ？？で、現状はネットワーク周りもTerraformで作成・更新ができるようになりました。

## 新規構築
新しい環境を構築した際に迷ったのは、ディレクトリ構成でした。
インターネット上で調べると、色々な考え方があり、すごく迷いましたが（今でも迷っていますが）、

* 各アプリケーションの構成がそれぞれで異ならないので、何度も同じ記述をするのは避けたい → moduleを使って各アプリケーションの構築を簡単に作れるようにする
* test、staging、productionのように、各環境にアプリケーションを構築したい → workspaceを使って環境ごとの構築を簡単に作れるようにしよう！

となり、下記のような構成※にしてみました（一部抜粋）。

```
├── app
│   └── <component directory>
│       ├── _tfvars
│       │   ├── prod.tfvars
│       │   ├── README.md
│       │   ├── staging.tfvars
│       │   └── test.tfvars
│       ├── config.tf
│       ├── main.tf
│       ├── remote-state.tf
│       ├── task-definition.json
│       └── variables.tf 
├── modules
│   ├── codebuild
│   │   ├── iam.tf
│   │   └── main.tf
│   ├── codepipeline
│   │   ├── iam.tf
│   │   └── main.tf
│   └── ecs
│       ├── iam.tf
│       ├── main.tf
│       ├── sg.tf
│       └── variable.tf
├── network
│   ├── 各リソース系(vpc、subnet、aclなど）
│   └── variables.tf
├── pipeline
│   └── <component pipeline>
│       ├── config.tf
│       ├── main.tf
│       └── variables.tf
├── .gitignore
├── .terraform-version # for managing terraform version
├── README.md
└── secret.tfvars
```
※「なんでそこ分けた？」などあるかもしれませんが、現状のネットワーク構成などに合わせたこともありこのような構成にしております。もしこういう構成にするともっと良いかも！などありましたら、ご教授いただけると嬉しいですmm

今回新しく構築したのは、

* ECS(Fargate)のアプリケーション環境を各環境毎に、かつアプリケーション毎に作れるようにする
* その際のCI/CD環境も各環境毎に、かつアプリケーション毎に作れるようにする

ということで、moduleにはCI/CD(CodeBuild/CodePipeline)とECSを置き、
アプリケーションは `app/<<component name>>/main.tf` で下記のようにmoduleを使い、

```app/hogehoge/main.tf(一部抜粋)
/////
module "ecs" {
  source = "../../modules/ecs"
  // アプリケーション毎に必要な設定を記載
}
////
```

CI/CDのパイプラインも `pipeline/<<component name/main.tf` で下記のようにmoduleを使ってアプリケーション毎の環境を構築できるようにしてみました。

```pipline/hogehoge/main.tf(一部抜粋)
/////
## Continuous Integration
module "ci" {
  source = "../../modules/codebuild"
  // アプリケーション毎に必要な設定を記載
}
/////
## Continuous Delivery
module "cd" {
  source = "../../modules/codepipeline"
  // アプリケーション毎に必要な設定を記載
}
/////
```


## 構築してみて
1個反省点としてあるのは、 `各アプリケーション毎、かつ各環境毎に作れるようにしたけど、、、本当に各アプリケーション毎に構成同じにするんだっけ？` というところです。
現状の環境は同じ構成でも、アプリケーションを移行する時には変わるのでは？ということをあまり考えずにこの環境にしたことが良かったかどうかは今のところわかりません。
ただ、後でリファクタリングする前提で、最初はコピペしてもしょうが無いからシンプルに作る！ということをもう少し意識しても良かったなと感じる今日この頃です＞＜

## 最後に
個人開発ではないですが、副業でTerraform化してみた話を書かせて頂きました。
Terraformの構成、もっと良い構成がある？とは思いつつこの構成にしておりますが、
もし「こういう構成だとより良いのでは？」などありましたら、是非アドバイスいただけると嬉しいです！！

そして最後まで読んで頂きまして、誠にありがとうございましたmm

## 参考
* [Terraform職人入門: 日々の運用で学んだ知見を淡々とまとめる](https://qiita.com/minamijoyo/items/1f57c62bed781ab8f4d7)
* [Terraforming で既存のインフラを Terraform 管理下におく](https://qiita.com/dtan4/items/345c56281ab0e87d6646)
* [AWS FargateとTerraformで最強＆簡単なインフラ環境を目指す](https://qiita.com/tarumzu/items/2d7ed918f230fea957e8)