TerraformとAtlasとGitHubを使ってインフラを構築、自動化してみた話

みなさんこんにちは、今村(@kyuns)です。今回は弊社の新規サービス開発にて、Hashicorp製品を中心にインフラ周りを整えたお話をしていきたいと思います。今回はTerraformとAtlasの話が中心になります。

今回実現したこと

  • TerraformでAWS上のリソースをコードで管理
  • GithubでPullRequestを作ってインフラに対する変更をコードベースでレビュー
  • Github上でPullRequestに対して変更がテストされ、テスト結果が貼られる
  • Pull Requestをマージすると自動的にAtlas経由でterraformが実行されてインフラの変更が適用される

いわゆるインフラのコード化&自動化です。

導入によるメリット

  • インフラがコードで管理されることにより属人性を排除することができる
  • インフラの変更に対して事前にレビューすることにより事故を減らせる
  • 変更の適用はPullRequestのmergeボタンを1つ押すだけなので格段にデプロイの手間が省けて安全(etc)

今回は0からのインフラ構築ということもあり、HashicorpのTerraformとAtlasを利用して上記を実現してみることにしました。それでは順を追って説明していきたいと思います。

Terraformって何?

TerraformとはHashicorpが提供している Infrastructure as Code を体現するためのツールです。今回は新規サービス開発ということもあり、ちょうど0から作るいいタイミングであった為、採用に踏み切りました。TerraformはAWSのほとんどの設定に対応しているため、コードベースでインスタンスをたてたり、ELBやS3の設定などのAWSのリソースを管理することができます。

Terraformの導入

インストール

ダウンロード

ダウンロードページから各プラットフォーム別のバイナリを取得します。プログラム自体はGoのバイナリなので好きなフォルダに展開後、PATHを通しておきましょう。
また、ruby gem経由でもインストールすることができます。

$gem install terraform $terraform -version Terraform v0.6.7

現時点(2015.11.27)での最新版は0.6.7です。

AWSクレデンシャルの設定

AWSのログインクレデンシャルを環境変数に設定しましょう。
bash/zshならexport、fish shellならsetenvを利用します。

export AWS_ACCESS_KEY_ID=XXXXX
export AWS_SECRET_ACCESS_KEY=XXXXX
 export AWS_DEFAULT_REGION=ap-northeast-1
setenv AWS_ACCESS_KEY_ID XXXXX
setenv AWS_SECRET_ACCESS_KEY XXXXX
setenv AWS_DEFAULT_REGION ap-northeast-1

以後、terraformコマンドを実行する際にこのアカウントの設定が利用されます。

Terraformを理解する

Terraformの構成

Terraformを理解するのに重要なファイルが2つ存在します。.tfファイルと.tfstateファイルです。

  • .tfファイル
    • terraformの設定を記述するファイルです。
  • .tfstateファイル
    • terraformコマンドを実行した後、現在のインフラの状態が記述されたファイルです。tfstateファイルはterraformを実行すると自動的に生成されるので通常は触る必要はありません。 また、terraformコマンドを実行すると自動的にtfstate.backupというバックアップファイルも生成されます。

Terraformを使ってEC2インスタンスを立ててみる

tfファイルの記述

Terraformを使って試しにインスタンスを立ててみます。
新しくインスタンスを立てる場合、例えばec2の設定は以下の様な感じになります。

ec2.tf
resource "aws_instance" "web01" {
    ami                         = "ami-6927d769"
    availability_zone           = "ap-northeast-1c"
    ebs_optimized               = false
    instance_type               = "t2.medium"
    monitoring                  = false
    key_name                    = "iqon"
    subnet_id                   = "subnet-4ee15817"
    vpc_security_group_ids      = ["sg-2f765a4a","sg-55755930"]
    associate_public_ip_address = true
    private_ip                  = "10.0.0.10"
    source_dest_check           = true

    root_block_device {
        volume_type           = "gp2"
        volume_size           = 20
        delete_on_termination = true
    }

    tags {
        "Name" = "web01"
    }
}

非常に直感的ですね。設定を記述できたらterraformコマンドを実行してみますが、その前に設定を確認してみましょう。

設定を確認してみる(Dry run)

terraformにはDry runの機能があります。
予め適用する内容を出力してくれるので、事前にどのような変更が起きるかを確認することができます。

$terraform plan

気をつけなければならないのですが、完璧なDry runというわけではなくplanでうまくいっても実際に実行すると失敗するということもありますので、あくまでも目安程度に捉えておいたほうが良いと思います。

コマンドを実行する

先ほど作ったec2.tfファイルを実行するためにはapplyコマンドを使います。

$terraform apply

これでしばらくするとEC2インスタンスが作成されます。
ec2のほかにもS3やRDS、ELBなどあらゆるAWSサービスに対応しています。

既存のAWSインフラの設定からtfファイルを生成する

完全に0からインフラを構築する機会はそんなにないと思うので、殆どの人は既存のインフラに少しずつ適用して試してみたいと思うのではないでしょうか。また既存のAWSの設定からtfファイルを生成したい場合はどうしたらいいでしょうか?公式ではそのようなexportの機能はサポートされていないので3rdParty製のツールを使います。
terraformingという便利なgemがあるので今回はこちらを使ってみます。

gem install terraforming

試しに現在のs3の設定を取得してみます。

$terraforming s3
resource "aws_s3_bucket" "iqonsample" {
    bucket = "iqonsample"
    acl    = "private"
}

resource "aws_s3_bucket" "iqonsample.test" {
    bucket = "iqonsample.test"
    acl    = "private"
}

このように$terraforming サービス名を指定すると、自動的に現在のAWSのリソース設定からtfファイルの形式で出力してくれます。また、同時にtfstateファイル形式を出力する機能も備えています。tfstate形式の場合はオプションで--tfstteを指定します。

$ terraforming s3 --tfstate
{
  "version": 1,
  "serial": 1,
  "modules": [
    {
      "path": [
        "root"
      ],
      "outputs": {
      },
      "resources": {
        "aws_s3_bucket.iqonapi": {
          "type": "aws_s3_bucket",
          "primary": {
            "id": "iqonapi",
            "attributes": {
              "acl": "private",
              "bucket": "iqonapi",
              "force_destroy": "false",
              "id": "iqonapi",
              "policy": ""
            }
          }
        },
        "aws_s3_bucket.iqonapi-test": {
          "type": "aws_s3_bucket",
          "primary": {
            "id": "iqonapi-test",
            "attributes": {
              "acl": "private",
              "bucket": "iqonapi-test",
              "force_destroy": "false",
              "id": "iqonapi-test",
              "policy": ""
            }
          }
        }
      }
    }
  ]
}

現在のAWSの状態からtfstateファイルの出力までしてくれます。
こちらを既存のtfstateファイルにmergeする場合は--merge オプションを利用してマージすることもできます。

AtlasとGithubでインフラを自動化する

Terraformで設定を記述した後は、次にAtlasとGithubを利用してインフラを自動化します。
ATLASはHashicorpが提供するクラウド型の統合プラットフォームであり、Vagrant,Packer,TerraformやConsulなどの機能を内包しています。ここではATLASとGithubを連携させてGithub上でPRを出した時点でterraform planの実行結果がPRに貼られ、mergeすると自動的にATLAS経由でterraform applyが実行されるのを目指します。

Atlasの設定

まずはAtlasにアカウントを作成します。
次にCreate and manage infrastructure with Terraformをクリックして設定に進みます。
画面に表示されるATLAS_TOKENを環境変数に設定します。

export ATLAS_TOKEN=XXXXXXXXXXX

TerraformプロジェクトをAtlasにpushする

次にローカルにあるterraformプロジェクトをAtlasにプッシュします。(チュートリアルに従うとexampleになる)

$ terraform remote config -backend-config "name=vasily/example"
Remote configuration updated
Remote state configured and pulled.
Configuration "vasily/example" uploaded! (v1)

設定がAtlasにプッシュされました。
この仕組みは、terraformのtfstateファイルをリモートで管理する機能/terraform remoteを利用したものです。
注意点ですが、この段階でローカルにあるtfstateファイルは削除されます。(Atlas上で管理されます)

自動デプロイ用の設定をする

Auto applyの設定

Github上でデフォルトブランチにmergeされた際に、terraform applyを自動実行するかどうかの設定ができます。ここでは自動化をするのでAuto applyにチェックをいれます。

Githubとの連携設定

次にgithubのイベントをフックできるようにAtlasの管理画面のサイドバーにあるIntegrationにてGithubの設定をします。

レポジトリのルートディレクトリにterraformの管理ファイルがない場合は、
サブディレクトリを指定することもできます。

環境変数の設定

次に管理画面のVariablesにて環境変数を設定します。
access_key,とsecret_keyを指定します。

GithubでPRを作成してみる

Atlas側で設定ができたらGithubに実際にPRを作成してみます。

PRのTESTの部分にTerraformでのterraform planの実行結果が表示されます。

PRをマージしてterraform applyが実行されるのを確認する。

Github側でPRをマージするとあとは自動的にAtlas側で
terraform applyが実行されます。もちろん管理画面からでも実行結果が確認できます。

これでGithubでPR作成->mergeからAWSへの反映まで自動化することができました。

terraform applyに失敗した場合


このようにterraform planが成功してもapplyが失敗する可能性があるので、手動でapplyする場合はコンソールからQueue Planを押すと再度実行されます。

まとめ

おすすめの導入方法

いきなりインフラ全てをTerraformで管理する、というのは0からインフラを作る時以外はあまりオススメできません。
TerraformはほとんどのAWSリソースに対応しています、よって全てをコードで管理できるわけではありますが、全てをTerraformで管理しようとすると個人的には結構事故の原因になりかねないなぁと運用してみて感じています。

例えばVPCのサブネットやインターネットゲートウェイの設定などの変更頻度のかなり低いものは場合に応じてTerraformで管理しない、という使い方もありだとは思います。

すでにAWSのインフラがある人は、まずは前述のterraformingを用いて既存のEC2インスタンスの設定などからtfファイルをつくり、同じ設定でインスタンスをたてたりするところから始めて見ると導入しやすいかなと思います。
また、Terraformへの完全移行の際には、tfstateファイルとの差分を排除するために、AWSのコンソールからポチポチというのは無くすというルールをつくって徹底する必要があります。(IAMとかをきちんと管理する)

導入後の感想

Terraformによって運用に必要な部分のインフラがコード化され、インフラの変更に対してもPRで確認してから適用できるようなったことで格段に属人性を排除することができました。また、変更の適用はGithubでPull Requestをmergeするだけで自動的にTerraformが実行されるようになり、安全性も確保することができました。

今日は紹介しませんでしたが、今後は弊社でのPackerやConsulまわりの取り組みもご紹介できればと思います。

VASILYではHashicorp製品をつかってインフラで新しいチャレンジをしてみたいエンジニアを募集しています。
ぜひとも一緒に取り組みましょう。興味があるインフラエンジニアはぜひこちらから応募してみてください。