Terraform + Ansible + Rails

# はじめに
Railsのアプリケーションを複数管理しているので今後の手間も考え、
何かテンプレ的なものが出来たらいいなと思って作成してみました。 :hammer: 
今後の運用において改善点もまだまだあると思ってますのでご意見ご要望はどしどし頂けたらと思います。 :bow: 

# 今回のゴール
Terraformで構築したEC2インスタンスにAnsibleを使ってRailsアプリケーションをデプロイするまでをゴールとします。 :golf: 

# 事前準備
Docker, Terraform, aws-cli, Ansibleのインストールをお願いします。

```bash
brew install docker
brew install terraform
brew install awscli
brew install ansible
```

ちなみに私の環境は下記のようになっています。
macOS Mojave 10.14.6
Docker 18.09.2
Compose 1.23.2
Terraform 0.12.3
aws-cli 1.15.50
Ansible 2.6.0

# テンプレートリポジトリ
[Github](https://github.com/Caad-Fukushi/terraform-ansible-rails)

# DockerでRails環境構築
以前に記事書いたのでこちらを参考に構築してください。 :pencil: 
[RubyをインストールせずにDockerでRails環境を構築する](https://qiita.com/fukushi_yoshikazu/items/c9beba97914d89ab4022)

ここまで完了した方は先ほどのテンプレートリポジトリから
`terraform`と`ansible`フォルダをコピーし、個人のリポジトリを作成してpushしておいてください。

# TerraformでAWSにインフラ準備
EC2, RDS, LBの構成で進めます。

terraform.tfstate格納用のS3バケットとAWSクレデンシャルは各自で用意してください。
S3バケット名：tf-bucket
AWSクレデンシャルprofile名：tf-profile

```bash:~/.aws/credentials
[tf-profile]
aws_access_key_id = XXXXXXXXXXXXXXXXX
aws_secret_access_key = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
```


```terraform:terraform/security_group/main.tf
provider "aws" {
  region  = "ap-northeast-1"
  version = "2.12.0"
  profile = "tf-profile"
}

terraform {
  required_version = ">= 0.12"

  backend "s3" {
    bucket  = "tf-bucket"
    region  = "ap-northeast-1"
    key     = "sample/sg/terraform.tfstate"
    encrypt = true
    profile = "tf-profile"
  }
}

# EC2に設定するセキュリティグループ
resource "aws_security_group" "ec2_security_group" {
  name   = "${var.app_name}-sg"
  vpc_id = var.vpc_id

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

  ingress {
    from_port       = "80"
    to_port         = "80"
    protocol        = "tcp"
    security_groups = [aws_security_group.lb_security_group.id]
  }

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

  tags = {
    Name = "${var.app_name}-sg"
  }
}

output "ec2_sg_id" {
  value = aws_security_group.ec2_security_group.id
}
//...これより後ろの設定はそのまま残しておいてください。
```

```terraform:terraform/security_group/variable.tf
variable "vpc_id" {
  default = "your-vpc-id"
}

variable "app_name" {
  default = "sample-app"
}
```

ファイルの修正が完了したら下記コマンドを順番に実行してみてください。

```bash:terraform/security_group/
terraform init
terraform plan
terraform apply
```
最後まで実行出来たらセキュリティグループが作成されているはずです。 :tada:
`output` メソッドで最終的に作成されたセキュリティグループIDを出力していることを覚えて追いてください。これを後ほどEC2作成時に利用します。

次にEC2の作成です。
Keypairは事前に作成し、サブネットも各自のサブネットIDを指定してください。
サブネット：subnet-000111222、subnet-333444555
Keypair：tf-keypair

```terraform:terraform/ec2/main.tf
provider "aws" {
  region  = "ap-northeast-1"
  version = "2.12.0"
  profile = "tf-profile"
}

terraform {
  required_version = ">= 0.12"

  backend "s3" {
    bucket  = "tf-bucket"
    region  = "ap-northeast-1"
    key     = "sample/ec2/terraform.tfstate"
    encrypt = true
    profile = "tf-profile"
  }
}

data "terraform_remote_state" "security_group" {
  backend = "s3"

  config = {
    bucket  = "tf-bucket"
    region  = "ap-northeast-1"
    key     = "sample/sg/terraform.tfstate"
    profile = "tf-profile"
  }
}

resource "aws_instance" "ec2" {
  count                  = var.instance_count
  ami                    = var.ami_id
  instance_type          = var.instance_type
  key_name               = var.key_pair
  subnet_id              = lookup(var.subnets, count.index % 2)
  associate_public_ip_address = "true"
  vpc_security_group_ids = [data.terraform_remote_state.security_group.outputs.ec2_sg_id]

  tags = {
    Name = "${var.app_name}_${count.index + 1}"
  }
}

output "instance_ids" {
  value = {
    for instance in aws_instance.ec2 :
    instance.id => instance.private_ip
  }
}

output "instance_count" {
  value = var.instance_count
}
```

```terraform:terraform/ec2/variable.tf
variable "ami_id" {
  default = "ami-0b898040803850657"
}

variable "instance_count" {
  default = 1
}

variable "subnets" {
  default = {
    "0" = "subnet-000111222"
    "1" = "subnet-333444555"
  }
}

variable "instance_type" {
  default = "t3.micro"
}

variable "key_pair" {
  default = "tf-keypair"
}

variable "app_name" {
  default = "sample-app"
}
```

ファイルの修正が完了したら下記コマンドを順番に実行してみてください。

```bash:terraform/ec2/
terraform init
terraform plan
terraform apply
```
最後まで実行出来たらEC2が作成されているはずです。 :tada:
作成したKeypairにてssh接続出来るか各自確認をお願いします。

RDSとLBの手順は割愛しますが、似たような感じでテンプレとして用意していますのでご活用頂けたらと思います。 :bow: 

# AnsibleでRuby環境構築
以前に記事を書いています。→ [AnsibleでRuby環境構築](https://qiita.com/fukushi_yoshikazu/items/69debbdab621ee0a19c2)

今回はこれに+αとして実際にGitサーバからソースをCloneし、CapistranoでRailsアプリをデプロイするテンプレまで作成しました。

まずはansibleユーザーがssh接続するための設定をします。

```ansible:ansible/development
---
[server]
先ほどTerraformで作成したEC2のIP

[server:vars]
ansible_user=ec2-user
ansible_ssh_private_key_file=~/.ssh/tf-keypair.pem

[server_secret:children]
server

[development:children]
server
```

次にアプリ名やデプロイ先のフォルダなどを指定をします。
ここで先ほどcommitした個人のリポジトリを指定してください。
あと、sshでClone出来るように各自で公開鍵と秘密鍵を作成してGitサーバ側に公開鍵を設定してください。
[GitHubでssh接続する手順~公開鍵・秘密鍵の生成から](https://qiita.com/shizuma/items/2b2f873a0034839e47ce)

```ansible:ansible/group_vars/server.yml
---
ruby_version: 2.6.3
bundler_version: 2.0.2
git_url: git@github.com:hoge.git
app_user: sample-app
project_name: sample
deploy_directory: /opt/{{ project_name }}
```

先ほど作成した秘密鍵やその他暗号化が必要な情報は`ansible-vault`で暗号化してください。
*vault_pass_sample*を**vault_pass**へリネームして下記コマンドを実行してください。

```bash
ansible-vault edit group_vars/server_secret.yml
```

ここにrailsのmaster_keyや先ほど作成した秘密鍵や公開鍵などを設定してください。

```ansible:ansible/group_vars/server_secret.yml
rails_master_key: xxx
ssh_authorized_keys: 'ssh-rsa XXX'
ssh_private_key: |
  -----BEGIN RSA PRIVATE KEY-----
  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  ...
  -----END RSA PRIVATE KEY-----
```

最後にgitブランチやcapistrano実行時のenvを`group_vars/{env}.yml`に設定して、
下記コマンドを実行するとEC2インスタンス環境にRuby環境の構築からGitリポジトリのCloneまで一通り行ってくれます。

```bash
# 初回のみ実行
ansible-playbook init.yml -i development
# 2回目以降デプロイ用
ansible-playbook deploy.yml -i development
```

※*nginx.conf*や*logrotate.conf*などもテンプレとして配置してますので各プロジェクトの設定に応じてご利用ください。

# Capistrano
参考までにCapistrano設定ファイル群を`capistrano`フォルダに置いてますので興味のある方は覗いてみてください。