Terraform + CircleCI 2.0 + Ansible で作る CD (Continuous Delivery) 環境

# 初めに

これは 第2のドワンゴ Advent Calendar 2017 24日目の記事です。

この記事では、Terraform + CircleCI 2.0 + Ansible を使ったCD(Continuous Delivery)  環境構築方法について解説します。

# CD(Continuous Delivery) とは

CDと略されますが、Continuous Delivery と Continuous Deployment と２つの意味で使われることが多いです。

https://puppet.com/blog/continuous-delivery-vs-continuous-deployment-what-s-diff

両者の違いについては、上記のURLで解説されているため、説明は割愛しますが、以下継続的デリバリーの略語としてCDを使用します。

# CD を行うために必要なモノ

- インフラストラクチャー
- CI/CDツール
- デプロイツール

以上のものが必要です。

今回の事例では、インフラストラクチャーは AWS上にTerraformで構築しました。

AWSの場合、他にもCloudFormationなどが選択肢としてありますが、
- HCLが CloudFormationのJson/Yamlに比べて読みやすい
- インフラの状態管理がしやすい
- 後々クラウドプロバイダーを変更した時にツールを変えてなくて良い

ということから今回は　Terraform  を採用しています。

CircleCI 2.0 では Workflow機能が使え、testの実行、ビルド、デプロイなどをワークフローとして定義できるため、今回はCircleCI 2.0 を使用しています。

デプロイについては、Ansibleを使用しました。

また対象となるアプリケーションは今回のサンプルでは Play 2.6(Scala)を使用しています。

# ワークフロー

- テストの実行
- アプリケーションのビルド
- S3へのビルド成果物アップロード
- ansibleスクリプトの実行
    - ビルド成果物の配置
    - アプリケーションサーバーの再起動

# Terraform でのプロビジョニング

```text
provider "aws" {
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
  region     = "${var.region}"
}

resource "aws_vpc" "backbeard_vpc" {
  cidr_block = "10.0.0.0/16"
  tags { Name = "backbeard" }
}
resource "aws_key_pair" "backbeard" {
  key_name   = "${var.key_name}"
  public_key = "${var.ssh_public_key}"
}

resource "aws_subnet" "backbeard_subnet" {
  vpc_id = "${aws_vpc.backbeard_vpc.id}"
  cidr_block = "10.0.0.0/24"
  availability_zone = "${var.region}a"
  tags {
    Name = "backbeard"
  }
}

resource "aws_network_interface" "backbeard" {
  subnet_id = "${aws_subnet.backbeard_subnet.id}"
  private_ips = ["10.0.0.100"]
  tags {
    Name = "backbeard"
  }
}

resource "aws_internet_gateway" "backbeard" {
  vpc_id = "${aws_vpc.backbeard_vpc.id}"
}

resource "aws_instance" "backbeard" {
  ami           = "ami-0a00ce72"
  instance_type = "t2.micro"
  key_name = "${var.key_name}"

  network_interface {
    network_interface_id = "${aws_network_interface.backbeard.id}"
    device_index = 0
  }
  tags {
    Name = "backbeard"
  }
}

resource "aws_eip" "lb" {
  instance = "${aws_instance.backbeard.id}"
  vpc      = true
}

resource "aws_security_group" "backbeard" {
  name        = "backbeard"
  description = "backbeard security group"
  vpc_id      = "${aws_vpc.backbeard_vpc.id}"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }
}

resource "aws_route_table" "backbeard" {
  vpc_id = "${aws_vpc.backbeard_vpc.id}"

  route {
    cidr_block = "10.0.0.0/0"
    gateway_id = "${aws_internet_gateway.backbeard.id}"
  }

  tags {
    Name = "backbeard"
  }
}
```

以上のテンプレートでインフラを作成しました。

backbeardという名前は、現在個人で作っているサービスのコードネームなので適当に無視してください。

# Ansibleでのデプロイ

今回Ansibleを以下の用途で使っています。
- Terraformで作成したEC2インスタンスについて必要なアプリケーションのインストール・設定ファイルの配置
- アプリケーションのデプロイ・再起動

Ansibleの設定ファイルは以下のとおりです。

https://github.com/ymizushi/cd-sample/tree/master/ansible


# CircleCI での ビルド成果物のアップロード、deployの実行

https://github.com/ymizushi/cd-sample/blob/master/.circleci/config.yml

`.circleci/config.yml` に定義しています。

# リポジトリ
https://github.com/ymizushi/cd-sample
今回作成した環境について、以上のリポジトリで公開しています。

# 最後に

今回は Terraform + CircleCI + ANsibleでの CD環境構築について説明させていただきました。

個人の開発においてCD環境構築は後回しになりがちな部分であると思いますが、このあたりを先に構築しておくと、リリースと確認に煩雑な手間をかけなくて良くなるため、アプリケーションの開発に集中しやすくなると感じました。