Terraformを使って自動デプロイのインフラ構築を自動化するまで

はじめに

はじめまして。
エンジニアの村脇です。この記事ではTerraformを用いてインフラ構築をコードで管理することについて記します。

プロダクト開発において、インフラ構築は開発の進捗を妨害する要因の一つでした。
弊社ispecでは小さなサービスを短期間でいくつも立ち上げている都合上、全てのサービス開発チームに「インフラ専門」で関わる人を割り振ることができず、サーバサイドをやっている人が、慣れないインフラ構築までやったりしていました。
その結果、インフラの担当者が希薄化してしまい、環境構築が後回しになってしまうことで、開発全体の進捗に影響が出てしまっていました。

そこでInfrastructure as Codeを導入し、インフラの属人性を和らげつつ、簡単に自動デプロイの環境を構築できるようにしたいと考えました。

具体的にはTerraformというツールを使い、「自動デプロイに必要インフラ構築」をモジュール化して使い回せるようにしました。

目的

今回の構成の目的は、githubにpushされたソースコードを自動的にデプロイ、公開することです。

構成

今回はAWSを使って以下のような構成のインフラを構築しました。
Image
CodeBuildを用いることで、GithubからのWebhookを受け取り、CodeBuildでDockerImageをビルドし、ECR保存。その後CodeDeployを動かしてECS経由で公開します。

Terraformによる実装

Terraformを用いて、上記の構成をサクッと構築できるモジュールを実装しました。

以下のファイルを一枚記述して、

$ terraform init

$ terrafomr apply

を実行するだけで、Githubと連携して自動デプロイするインフラを構築することができます。

provider "aws" {
  region = "ap-northeast-1"
}

locals {
  application_name       = "simple-go-ping-api"
  application_name_lower = replace(lower(local.application_name), "/[^a-z0-9]/", "")
}

module "ecs-deployline" {
  source  = "ispec-inc/ecs-deployline/aws"
  version = "0.4.3"

  vpc_id         = "vpc-0000000"
  public_subnets = ["subnet-1112", "subnet-2222"]

  cluster_name        = local.application_name
  app_repository_name = local.application_name
  container_name      = local.application_name

  alb_port         = "8005"
  container_port   = "8005"
  helth_check_path = "/ping"

  git_repository = {
    owner  = "murawakimitsuhiro"
    name   = "go-simple-RESTful-api"
    branch = "feature/only-ping"
  }
}

リンク

Terraformのバージョンはv0.12.24を使用しています。(2020/03/20ブログ記述時)

※以下は、今回のTerraform構成の詳細な説明と設計の目的などの解説になります。

Terraformのコードの構成について

Terraformは、上記のAWSのサービスなどの設定ファイルなどを記述しておくことでコマンド一つでインフラ構築ができるソフトウェアです。
Terraformを使う際にも、他のコードを書くときと同様、「再利用性」「結合の密度」「カスタマイズ性」などに気を配ってあげると、よりInfrastructure as Codeの利点を活かせるかと思います。

それを踏まえた上で、今回気を配った(実現したかった)点を列挙すると以下のようになります。

1. RDS(データベース)や、S3(メディアサーバとして利用)を環境によって柔軟に増やしたり減らしたりできる

  • 今回のAPIサーバは、DBとメディアサーバを使いましたが、ものによっては「DBが不要」であったり、そもそも「フロントエンドの用のインフラ」といった状況もあり得るためこれらの「付属サービス」に対する柔軟さが必要でした。

2. 1つのインフラは1つのリポジトリをpullしてくるだけで管理できるようにしたい

  • これはInfrastructure as Codeをやる以上はやはり最低限実現したい部分です。インフラをコードで管理しはじめたはずなのに、このリポジトリを動かすには「違うレポジトリにあるコードを配置して、その上で設定を変更する必要がある」となっていては意味が薄れてしまうので、、依存関係をリポジトリ単位できっちり区切りたいと考えました。

3. コードの継続的なメンテナンスをできるようにしたい

  • 特定のサービスの速度を向上させたり、特定のプロパティをいじったり、新しいサービスに乗り換えたりといった変更も、随時行えるようにしたいと考えました。そのためには、これらを構成するコードを簡単に編集でき、その変更の影響の範囲をコントロールできる必要がありました。

以上の項目を実現するためにそれぞれ行ったことは以下の三つです。

  1. RDS(データベース)や、S3(メディアサーバ)を環境によって柔軟に増やしたり減らしたりできる
    • Terraformのモジュール機能を使う
  2. 1つのインフラは1つのリポジトリをpullしてくるだけで管理できるようにしたい
    • Terraform Registryを使う
  3. 構成を作るコードの継続的なメンテナンスをできるようにしたい
    • バージョン管理する

1. Terraformのモジュール機能を使う

Terraformには、よく使う構成・設定を「モジュール」として定義して再利用する仕組みがあります。この機能をうまく利用することで、ライブラリのように、疎結合を維持して機能を柔軟に実装していくことができます。
今回は、モジュールとして
- ecs-deploy-pipeline - rds-mysql-utf8

を新規に作りました。

ecs-deploy-pipelineは、その名の通りGithubからecsへのデプロイまでが入ったモジュールです。都度設定する項目を絞ることで素早く自動デプロイの構築をできるようにしました。
rds-mysql-utf8は、Terraform公式から提供されているrdsのモジュールのラッパーで、日本語と顔文字に対応させるための設定を隠蔽しています。
実際に使う時はecs-pipelineのモジュールを定義し、rds-mysql-utfや、S3のモジュール(公式から提供)はそれぞれの構成ごとに定義するかを決めることができます。

2. Terraform Registryを使う

Terraform Registryは、①のように、独自で作成したTerraformのモジュールをGithubのリポジトリのようにインターネット上に置いておくことができるサービスです。(Terraform公式が運営しています。)

公開したモジュールは、ローカルからURL経由で参照することができるようになります。
この機能により、terraformのコマンドを叩くだけで依存関係にあるモジュールを全て参照し、ローカルに落としてきて即実行ということが可能になります。

3. バージョン管理する

Terraform Registyにはバージョン管理機能があり、具体的にはGithubのリリース別にバージョンを割り振ることができます。
モジュールを参照する際に、URLとバージョンの二つを指定することで特定バージョンのモジュールを使うことができます。

この機能を使うことで、「共通モジュールに変更を加えたら前まで使っていたインフラでエラーが出てしまった…」みたいなことを避けることができます。
これでモジュールを継続的にメンテしつつ、以前のインフラへの影響もコントロールできるようになりました。

まとめ

今回はTerraformでベーシックなインフラ環境の構築を自動化する方法と、そのソースコードの再利用性を高めたりバージョン管理をする方法について書かせていただきました。

チーム間でインフラの構成をコードとして共有できる上、誰かが作ったものを再利用できるという意味で、DRYなチームインフラ開発ができるので、ispecではこれからもベストプラクティスを模索し続けたいと思っています。

最新技術を試したいアナタへ

ispecでは、弊社の技術スタックをどんどん広げてくれるエンジニアを募集しています。

アナタが今気になっている技術、プロジェクトで採用してみませんか?

参考文献