[覚書]OttoでAWSに本番環境構築＆デプロイ

1. [[覚書]Ottoで開発環境を構築](http://qiita.com/neogym/items/c0c3ba87e8535dabe878)
2. [[覚書]OttoでAWSに本番環境構築＆デプロイ](http://qiita.com/neogym/items/e5e0bbf89df390aa9059) ← **ここ**
3. [[覚書]Ottoで開発環境を構築 (Appfileをカスタマイズ → Rubyのバージョン指定)](http://qiita.com/neogym/items/73ee4bee570963751153)
4. [[覚書]Ottoで開発環境を構築 (MongoDBのDockerコンテナを動かす)](http://qiita.com/neogym/items/0d7aa2e07ea1da477f2f)

# 検証環境

* OS : OS X 10.10.5
    * `Homebrew` : 0.9.5
    * `brew cask` : 0.56.0

# 事前準備

## `AWS`のAPIキーなど

下記は`AWS`の管理画面での作業です。

1. `IAM(Identity and Access Management)`から`IAM`ユーザを作成する。
    * ***この際、`認証情報のダウンロード`を忘れずに行ってください。***
2. パスワードやポリシーなどの設定を行う。

*[AWSアカウント作ったらこれだけはやっとけ！IAMユーザーとAuthyを使ったMFAで2段階認証 - Qiita:](http://qiita.com/yoshidashingo/items/cd206daca0596659b440)を参考にさせていただきました。*

## `Terraform`および`Packer`のインストール

***`otto infra`や`otto build`のコマンド実行時に、`Terraform`や`Packer`がインストールされるようなのですが、検証した環境ではインストールに失敗したので手動でインストールしました。***

`brew cask`でインストール。

```bash
$ brew install terraform
$ brew install packer
```

インストールされたか確認。

```bash
$ terraform --version
Terraform v0.6.3

Your version of Terraform is out of date! The latest version
is 0.6.7. You can update by downloading from www.terraform.io
$ packer --version
0.8.6
```

# インフラストラクチャー

## 起動

必要なもの。

* AWS Access Key
* AWS Secret Key
* SSH Public Key Path
* Password for Encrypting Credentials

***かなり時間がかかるようなので気長に待ちます。。。***

```bash
$ otto infra
==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Cached and encrypted infrastructure credentials found.
    Otto will now ask you for the password to decrypt these
    credentials.
    
AWS Access Key
  AWS access key used for API calls.

  Enter a value: AKIAXXXXXXXXXXXXXXXXX

AWS Secret Key
  AWS secret key used for API calls.

  Enter a value: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

SSH Public Key Path
  Path to an SSH public key that will be granted access to EC2 instances

  Default: ~/.ssh/id_rsa.pub
  Enter a value: 

Password for Encrypting Credentials
  This password will be used to encrypt and save the credentials so they
  don't need to be repeated multiple times.

  Enter a value: 
==> Building main infrastructure...
==> Executing Terraform to manage infrastructure...
    Raw Terraform output will begin streaming in below. Otto
    does not create this output. It is mirrored directly from
    Terraform while the infrastructure is being created.
    
    Terraform may ask for input. For infrastructure provider
    credentials, be sure to enter the same credentials
    consistently within the same Otto environment.
    
aws_vpc.main: Creating...
  cidr_block:                "" => "10.0.0.0/16"
  default_network_acl_id:    "" => "<computed>"
  default_security_group_id: "" => "<computed>"
  dhcp_options_id:           "" => "<computed>"
  enable_dns_hostnames:      "" => "1"
  enable_dns_support:        "" => "1"
  main_route_table_id:       "" => "<computed>"
  tags.#:                    "" => "1"
  tags.Name:                 "" => "otto"
aws_vpc.main: Creation complete
aws_subnet.public: Creating...
  availability_zone:       "" => "<computed>"
  cidr_block:              "" => "10.0.2.0/24"
  map_public_ip_on_launch: "" => "1"
  tags.#:                  "" => "1"
  tags.Name:               "" => "public"
  vpc_id:                  "" => "vpc-8d76fee9"
aws_key_pair.main: Creating...
  fingerprint: "" => "<computed>"
  key_name:    "" => "otto-8d76fee9"
  public_key:  "" => "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
aws_internet_gateway.public: Creating...
  vpc_id: "" => "vpc-8d76fee9"
aws_key_pair.main: Creation complete
aws_internet_gateway.public: Creation complete
aws_route_table.public: Creating...
  route.#:                                   "" => "1"
  route.559328850.cidr_block:                "" => "0.0.0.0/0"
  route.559328850.gateway_id:                "" => "igw-6f0aee0b"
  route.559328850.instance_id:               "" => ""
  route.559328850.network_interface_id:      "" => ""
  route.559328850.vpc_peering_connection_id: "" => ""
  tags.#:                                    "" => "1"
  tags.Name:                                 "" => "public"
  vpc_id:                                    "" => "vpc-8d76fee9"
aws_subnet.public: Creation complete
aws_route_table.public: Creation complete
aws_route_table_association.public: Creating...
  route_table_id: "" => "rtb-40256224"
  subnet_id:      "" => "subnet-c09098eb"
aws_route_table_association.public: Creation complete

Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: /var/folders/fn/4c83cb5979l0rw8mlz7nnvn00000gn/T/otto-tf808246809/state

Outputs:

  infra_id      = 8d76fee9
  key_name      = otto-8d76fee9
  region        = us-east-1
  subnet_public = subnet-c09098eb
  vpc_cidr      = 10.0.0.0/16
  vpc_id        = vpc-8d76fee9

==> Terraform execution complete. Saving results...
==> Building infrastructure for foundation: consul
Get: file:///Users/masaki/Documents/Development/github/hashicorp/otto-getting-started/.otto/compiled/foundation-consul/deploy/module-aws-simple

==> Terraform execution complete. Saving results...
==> Building infrastructure for foundation: consul
Get: file:///Users/masaki/Documents/Development/github/hashicorp/otto-getting-started/.otto/compiled/foundation-consul/deploy/module-aws-simple

==> Terraform execution complete. Saving results...
module.consul-1.aws_security_group.consul: Creating...
  description:                          "" => "Security group for Consul 1"
  egress.#:                             "" => "1"
  egress.482069346.cidr_blocks.#:       "" => "1"
  egress.482069346.cidr_blocks.0:       "" => "0.0.0.0/0"
  egress.482069346.from_port:           "" => "0"
  egress.482069346.protocol:            "" => "-1"
  egress.482069346.security_groups.#:   "" => "0"
  egress.482069346.self:                "" => "0"
  egress.482069346.to_port:             "" => "0"
  ingress.#:                            "" => "3"
  ingress.2541437006.cidr_blocks.#:     "" => "1"
  ingress.2541437006.cidr_blocks.0:     "" => "0.0.0.0/0"
  ingress.2541437006.from_port:         "" => "22"
  ingress.2541437006.protocol:          "" => "tcp"
  ingress.2541437006.security_groups.#: "" => "0"
  ingress.2541437006.self:              "" => "0"
  ingress.2541437006.to_port:           "" => "22"
  ingress.2547406835.cidr_blocks.#:     "" => "1"
  ingress.2547406835.cidr_blocks.0:     "" => "10.0.0.0/16"
  ingress.2547406835.from_port:         "" => "1"
  ingress.2547406835.protocol:          "" => "udp"
  ingress.2547406835.security_groups.#: "" => "0"
  ingress.2547406835.self:              "" => "0"
  ingress.2547406835.to_port:           "" => "65535"
  ingress.3910776171.cidr_blocks.#:     "" => "1"
  ingress.3910776171.cidr_blocks.0:     "" => "10.0.0.0/16"
  ingress.3910776171.from_port:         "" => "1"
  ingress.3910776171.protocol:          "" => "tcp"
  ingress.3910776171.security_groups.#: "" => "0"
  ingress.3910776171.self:              "" => "0"
  ingress.3910776171.to_port:           "" => "65535"
  name:                                 "" => "consul 1"
  owner_id:                             "" => "<computed>"
  vpc_id:                               "" => "vpc-8d76fee9"
module.consul-1.aws_security_group.consul: Creation complete
module.consul-1.aws_instance.consul: Creating...
  ami:                               "" => "ami-7f6a1f1a"
  availability_zone:                 "" => "<computed>"
  ebs_block_device.#:                "" => "<computed>"
  ephemeral_block_device.#:          "" => "<computed>"
  instance_type:                     "" => "t2.micro"
  key_name:                          "" => "otto-8d76fee9"
  placement_group:                   "" => "<computed>"
  private_dns:                       "" => "<computed>"
  private_ip:                        "" => "10.0.2.6"
  public_dns:                        "" => "<computed>"
  public_ip:                         "" => "<computed>"
  root_block_device.#:               "" => "<computed>"
  security_groups.#:                 "" => "<computed>"
  source_dest_check:                 "" => "1"
  subnet_id:                         "" => "subnet-c09098eb"
  tags.#:                            "" => "1"
  tags.Name:                         "" => "consul 1"
  tenancy:                           "" => "<computed>"
  vpc_security_group_ids.#:          "" => "1"
  vpc_security_group_ids.2305205259: "" => "sg-5fd08f39"
module.consul-1.aws_instance.consul: Provisioning with 'file'...
module.consul-1.aws_instance.consul: Provisioning with 'remote-exec'...
module.consul-1.aws_instance.consul (remote-exec): Connecting to remote host via SSH...
module.consul-1.aws_instance.consul (remote-exec):   Host: XXX.XXX.XXX.XXX
module.consul-1.aws_instance.consul (remote-exec):   User: ubuntu
module.consul-1.aws_instance.consul (remote-exec):   Password: false
module.consul-1.aws_instance.consul (remote-exec):   Private key: false
module.consul-1.aws_instance.consul (remote-exec):   SSH Agent: true
module.consul-1.aws_instance.consul (remote-exec): Connected!
module.consul-1.aws_instance.consul (remote-exec): consul stop/waiting
module.consul-1.aws_instance.consul (remote-exec): consul start/running, process 1350
module.consul-1.aws_instance.consul: Creation complete

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: /var/folders/fn/4c83cb5979l0rw8mlz7nnvn00000gn/T/otto-tf495026781/state

Outputs:

  consul_address = XXX.XXX.XXX.XXX

==> Terraform execution complete. Saving results...
==> Infrastructure successfully created!
    The infrastructure necessary to deploy this application
    is now available. You can now deploy using `otto deploy`.
```

## ステータス

ステータス確認。

```bash
$ otto status
==> App Info
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)
==> Component Status
    Dev environment: CREATED
    Infra:           READY
    Build:           NOT BUILT
    Deploy:          NOT DEPLOYED
```

*`Infra`が`READY`になっていることを確認します。*

# ビルド

`起動`の項目で入力した`Password for Encrypting Credentials`を使います。

```bash
$ otto build
==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Cached and encrypted infrastructure credentials found.
    Otto will now ask you for the password to decrypt these
    credentials.
    
Encrypted Credentials Password
  Infrastructure credentials are required for this operation. Otto found
  saved credentials that are password protected. Please enter the password
  to decrypt these credentials. You may also just hit <enter> and leave
  the password blank to force Otto to ask for the credentials again.

  Enter a value: 
==> Querying infrastructure data for build...
==> Building deployment archive...
==> Building deployment artifact with Packer...
    Raw Packer output will begin streaming in below. Otto
    does not create this output. It is mirrored directly from
    Packer while the build is being run.
    
otto output will be in this color.

==> otto: Prevalidating AMI Name...
==> otto: Inspecting the source AMI...
==> otto: Creating temporary keypair: packer 565a69ad-9a6e-7c4d-acaa-3852ae60a122
==> otto: Creating temporary security group for this instance...
==> otto: Authorizing access to port 22 the temporary security group...
==> otto: Launching a source AWS instance...
    otto: Instance ID: i-a30c501d
==> otto: Waiting for instance (i-a30c501d) to become ready...
==> otto: Waiting for SSH to become available...
==> otto: Connected to SSH!
==> otto: Provisioning with shell script: /var/folders/fn/4c83cb5979l0rw8mlz7nnvn00000gn/T/packer-shell278274437
==> otto: Uploading /Users/masaki/Documents/Development/github/hashicorp/otto-getting-started/.otto/compiled/app/foundation-consul/app-build/ => /tmp/otto/foundation-1
==> otto: Provisioning with shell script: /var/folders/fn/4c83cb5979l0rw8mlz7nnvn00000gn/T/packer-shell381431996
    otto: [otto] Installing Consul...
    otto: [otto] Installing dnsmasq for Consul...
    otto: [otto] Configuring consul service: otto-getting-started
==> otto: Uploading /var/folders/fn/4c83cb5979l0rw8mlz7nnvn00000gn/T/otto-slug-455814359 => /tmp/otto-app.tgz
==> otto: Provisioning with shell script: build-ruby.sh
    otto: [otto] Waiting for cloud-config to complete...
    otto: [otto] Adding apt repositories and updating...
    otto: [otto] Installing Ruby, Passenger, Nginx, and other packages...
    otto: [otto] Installing Bundler...
    otto: [otto] Extracting app...
    otto: [otto] Adding application user...
    otto: [otto] Setting permissions...
    otto: [otto] Configuring nginx...
    otto: [otto] Bundle installing the app...
    otto: Fetching gem metadata from https://rubygems.org/..........
    otto: Fetching version metadata from https://rubygems.org/..
    otto: Installing rack 1.6.4
    otto: Installing rack-protection 1.5.3
    otto: Installing tilt 2.0.1
    otto: Installing sinatra 1.4.6
    otto: Using bundler 1.10.6
    otto: Bundle complete! 1 Gemfile dependency, 5 gems now installed.
    otto: Gems in the groups development and test were not installed.
    otto: Bundled gems are installed into ./vendor/bundle.
    otto: [otto] ...done!
==> otto: Stopping the source instance...
==> otto: Waiting for the instance to stop...
==> otto: Creating the AMI: otto-getting-started 1448765868
    otto: AMI: ami-745a191e
==> otto: Waiting for AMI to become ready...
==> otto: Terminating the source AWS instance...
==> otto: Cleaning up any extra volumes...
==> otto: No volumes to clean up, skipping
==> otto: Deleting temporary security group...
==> otto: Deleting temporary keypair...
Build 'otto' finished.

==> Builds finished. The artifacts of successful builds are:
--> otto: AMIs were created:

us-east-1: ami-745a191e
==> Storing build data in directory...
==> Build success!
    The build was completed successfully and stored within
    the directory service, meaning other members of your team
    don't need to rebuild this same version and can deploy it
    immediately.
```

## ステータス

ステータス確認。

```bash
$ otto status
==> App Info
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)
==> Component Status
    Dev environment: CREATED
    Infra:           READY
    Build:           BUILD READY
    Deploy:          NOT DEPLOYED
```

`Build`が`BUILD READY`になっていることを確認します。

# デプロイ

`起動`の項目で入力した`Password for Encrypting Credentials`を使います。

```bash
$ otto deploy
==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Cached and encrypted infrastructure credentials found.
    Otto will now ask you for the password to decrypt these
    credentials.
    
Encrypted Credentials Password
  Infrastructure credentials are required for this operation. Otto found
  saved credentials that are password protected. Please enter the password
  to decrypt these credentials. You may also just hit <enter> and leave
  the password blank to force Otto to ask for the credentials again.

  Enter a value: 
aws_security_group.app: Creating...
  description:                         "" => "Managed by Terraform"
  egress.#:                            "" => "1"
  egress.482069346.cidr_blocks.#:      "" => "1"
  egress.482069346.cidr_blocks.0:      "" => "0.0.0.0/0"
  egress.482069346.from_port:          "" => "0"
  egress.482069346.protocol:           "" => "-1"
  egress.482069346.security_groups.#:  "" => "0"
  egress.482069346.self:               "" => "0"
  egress.482069346.to_port:            "" => "0"
  ingress.#:                           "" => "1"
  ingress.482069346.cidr_blocks.#:     "" => "1"
  ingress.482069346.cidr_blocks.0:     "" => "0.0.0.0/0"
  ingress.482069346.from_port:         "" => "0"
  ingress.482069346.protocol:          "" => "-1"
  ingress.482069346.security_groups.#: "" => "0"
  ingress.482069346.self:              "" => "0"
  ingress.482069346.to_port:           "" => "0"
  name:                                "" => "otto-getting-started-8d76fee9"
  owner_id:                            "" => "<computed>"
  vpc_id:                              "" => "vpc-8d76fee9"
aws_security_group.app: Creation complete
aws_instance.app: Creating...
  ami:                               "" => "ami-745a191e"
  availability_zone:                 "" => "<computed>"
  ebs_block_device.#:                "" => "<computed>"
  ephemeral_block_device.#:          "" => "<computed>"
  instance_type:                     "" => "t2.micro"
  key_name:                          "" => "otto-8d76fee9"
  placement_group:                   "" => "<computed>"
  private_dns:                       "" => "<computed>"
  private_ip:                        "" => "<computed>"
  public_dns:                        "" => "<computed>"
  public_ip:                         "" => "<computed>"
  root_block_device.#:               "" => "<computed>"
  security_groups.#:                 "" => "<computed>"
  source_dest_check:                 "" => "1"
  subnet_id:                         "" => "subnet-c09098eb"
  tags.#:                            "" => "1"
  tags.Name:                         "" => "otto-getting-started"
  tenancy:                           "" => "<computed>"
  vpc_security_group_ids.#:          "" => "1"
  vpc_security_group_ids.2371256730: "" => "sg-b72d73d1"
aws_instance.app: Creation complete

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: /var/folders/fn/4c83cb5979l0rw8mlz7nnvn00000gn/T/otto-tf608145036/state

Outputs:

  url = http://ec2-XX-XX-XX-XX.compute-1.amazonaws.com/

```

ブラウザで`Outputs`の`url`にアクセスし、`Who are you?`と表示されたら成功です。

## ステータス

ステータス確認。

```bash
$ otto status
==> App Info
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)
==> Component Status
    Dev environment: CREATED
    Infra:           READY
    Build:           BUILD READY
    Deploy:          DEPLOYED
```

`Deploy`が`DEPLOYED`になっていることを確認。

# 破棄

デプロイしたイメージなどを破棄する場合は、下記のコマンドを実行してください。

1. `otto deploy destroy`
2. `otto infra destroy`
3. `otto dev destroy`

## `otto deploy destroy`

```bash
$ otto deploy destroy
Do you really want to destroy?
  Otto will delete all resources associated with the deploy.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Cached and encrypted infrastructure credentials found.
    Otto will now ask you for the password to decrypt these
    credentials.
    
Encrypted Credentials Password
  Infrastructure credentials are required for this operation. Otto found
  saved credentials that are password protected. Please enter the password
  to decrypt these credentials. You may also just hit <enter> and leave
  the password blank to force Otto to ask for the credentials again.

  Enter a value: 
aws_security_group.app: Refreshing state... (ID: sg-b72d73d1)
aws_instance.app: Refreshing state... (ID: i-61bce0df)
aws_instance.app: Destroying...
aws_instance.app: Destruction complete
aws_security_group.app: Destroying...
aws_security_group.app: Destruction complete

Apply complete! Resources: 0 added, 0 changed, 2 destroyed.

```

## `otto infra destroy`

```bash
$ otto infra destroy
Do you really want to destroy?
  Otto will delete all your managed infrastructure.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Cached and encrypted infrastructure credentials found.
    Otto will now ask you for the password to decrypt these
    credentials.
    
Encrypted Credentials Password
  Infrastructure credentials are required for this operation. Otto found
  saved credentials that are password protected. Please enter the password
  to decrypt these credentials. You may also just hit <enter> and leave
  the password blank to force Otto to ask for the credentials again.

  Enter a value: 
==> Destroying infrastructure for foundation: consul
Get: file:///Users/masaki/Documents/Development/github/hashicorp/otto-getting-started/.otto/compiled/foundation-consul/deploy/module-aws-simple

==> Terraform execution complete. Saving results...
module.consul-1.aws_security_group.consul: Refreshing state... (ID: sg-5fd08f39)
module.consul-1.aws_instance.consul: Refreshing state... (ID: i-5be0bde5)
aws_instance.consul: Destroying...
aws_instance.consul: Destruction complete
aws_security_group.consul: Destroying...
aws_security_group.consul: Destruction complete

Apply complete! Resources: 0 added, 0 changed, 2 destroyed.

==> Terraform execution complete. Saving results...
==> Destroying main infrastructure...
==> Executing Terraform to manage infrastructure...
    Raw Terraform output will begin streaming in below. Otto
    does not create this output. It is mirrored directly from
    Terraform while the infrastructure is being created.
    
    Terraform may ask for input. For infrastructure provider
    credentials, be sure to enter the same credentials
    consistently within the same Otto environment.
    
aws_vpc.main: Refreshing state... (ID: vpc-8d76fee9)
aws_subnet.public: Refreshing state... (ID: subnet-c09098eb)
aws_key_pair.main: Refreshing state... (ID: otto-8d76fee9)
aws_internet_gateway.public: Refreshing state... (ID: igw-6f0aee0b)
aws_route_table.public: Refreshing state... (ID: rtb-40256224)
aws_route_table_association.public: Refreshing state... (ID: rtbassoc-b54babd2)
aws_route_table_association.public: Destroying...
aws_key_pair.main: Destroying...
aws_key_pair.main: Destruction complete
aws_route_table_association.public: Destruction complete
aws_subnet.public: Destroying...
aws_route_table.public: Destroying...
aws_route_table.public: Destruction complete
aws_internet_gateway.public: Destroying...
aws_subnet.public: Destruction complete
aws_internet_gateway.public: Destruction complete
aws_vpc.main: Destroying...
aws_vpc.main: Destruction complete

Apply complete! Resources: 0 added, 0 changed, 6 destroyed.

==> Terraform execution complete. Saving results...
==> Infrastructure successfully destroyed!
    The infrastructure necessary to run this application and
    all other applications in this project has been destroyed.
```

## `otto dev destroy`

```bash
$ otto dev destroy
==> Destroying the local development environment...
==> default: Destroying VM and associated drives...
==> default: Running cleanup tasks for 'shell' provisioner...
==> default: Running cleanup tasks for 'shell' provisioner...
==> default: Running cleanup tasks for 'shell' provisioner...


==> Deleting development environment metadata...
==> Development environment has been destroyed!
```

# 参考

* [Otto by HashiCorp:](https://ottoproject.io/)
* [AWSアカウント作ったらこれだけはやっとけ！IAMユーザーとAuthyを使ったMFAで2段階認証 - Qiita:](http://qiita.com/yoshidashingo/items/cd206daca0596659b440)

以上