Ottoでサンプルを動かしてみた

HashiCorpから最近発表されたサービスの1つであるOttoですが、Vagrantの後継と位置づけられているらしく、遅かれ早かれ業務で使うことになりそうな気がしたので、まずはどんなものなのか、ということで触ってみました。

Ottoでは、開発環境だけではなく、本番環境まで含め、otto コマンドだけで扱えるようになっており、内部的には、今までのHashiCorpのプロダクトであるVagrant, Packer, Consul, Terraformを組み合わせて利用しているようです。

Vagrantを含めた他のHashiCorpプロダクトを組み合わせて開発環境・本番環境の構築からデプロイまですべて1つで出来るようにしたもの、というイメージのプロダクトで、Vagrantの後継という印象から来るイメージとは少し違いました。

# 環境
以下の環境で動作確認を行いました。
- OS X yosemite
- Otto v0.1.2

# インストール
以下からバイナリファイルをダウンロードし、適当なディレクトリへ配置します。
https://ottoproject.io/downloads.html

```bash
$ mv ~/Downloads/otto /usr/local/bin/
$ chmod +x /usr/local/bin/otto
```

オプションは以下となります。
ステータスとバージョンを確認するコマンドを除けば、5つしかオプションがなく、かなりシンプルなようです。

```bash
$ otto --help
usage: otto [--version] [--help] <command> [<args>]

Available commands are:
    build      Build the deployable artifact for the app
    compile    Prepares your project for being run.
    deploy     Deploy the application
    dev        Start and manage a development environment
    infra      Builds the infrastructure for the Appfile
    status     Status of the stages of this application
    version    Prints the Otto version
```

以下、公式サイト( https://ottoproject.io/intro/getting-started/dev.html ) を参考にしました。
流れとしては、上記のオプションでいうと、compileで準備、devで開発環境構築
、infraで本番環境構築、buildでビルド実行、deployでデプロイ実行という形です。
また、今回は公式に提供されているサンプルアプリケーションを利用します。

# 設定ファイルの作成

まずは、otto compileを実行します。
アプリケーションの環境を自動で判別して、それらのデータを基にOttoの設定ファイルが作成されます。

ApplicationがRuby、Projectがサンプルアプリケーション名、Infrastructureがawsとなっていることが分かります。

```bash
$ git clone https://github.com/hashicorp/otto-getting-started.git
$ cd otto-getting-started/
$ otto compile
==> Loading Appfile...
==> No Appfile found! Detecting project information...
    No Appfile was found. If there is no Appfile, Otto will do its best
    to detect the type of application this is and set reasonable defaults.
    This is a good way to get started with Otto, but over time we recommend
    writing a real Appfile since this will allow more complex customizations,
    the ability to reference dependencies, versioning, and more.
==> Fetching all Appfile dependencies...
==> Compiling...
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)

    Compiling infra...
    Compiling foundation: consul
==> Compiling main application...
==> Compilation success!
    This means that Otto is now ready to start a development environment,
    deploy this application, build the supporting infrastructure, and
    more. See the help for more information.

    Supporting files to enable Otto to manage your application from
    development to deployment have been placed in the output directory.
    These files can be manually inspected to determine what Otto will do.
```

上記で判別したアプリケーション等の情報は以下のAppfileに記載されているようです。
(参考：https://ottoproject.io/intro/getting-started/appfile.html)

```bash
$ cat .otto/appfile/Appfile.compiled
(snip)
                "Application": {
                    "Name": "otto-getting-started",
                    "Type": "ruby",
                    "Dependencies": null
                },
                "Project": {
                    "Name": "otto-getting-started",
                    "Infrastructure": "otto-getting-started"
                },
                "Infrastructure": [
                    {
                        "Name": "otto-getting-started",
                        "Type": "aws",
                        "Flavor": "simple",
                        "Foundations": [
                            {
                                "Name": "consul",
                                "Config": null
                            }
(snip)
```

このうち、Flavorというのが、自動で構築されるAWS環境のパターンを指します。
Flavorの種類については以下のページに記載されています。デフォルトではsimpleが指定されています。
https://ottoproject.io/docs/infra/aws.html

また、この時点で、.ottoidというファイルと、.ottoというディレクトリが新たに出来ていました。
ファイル名等から見ると、中では、Vagrant, Consul, Terraformといった他のHashicorpのプロダクトが使われているようです。

```bash
$ cat .ottoid
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

DO NOT MODIFY OR DELETE THIS FILE!

This file should be checked in to version control. Do not ignore this file.

The first line is a unique UUID that represents the Appfile in this directory.
This UUID is used globally across your projects to identify this specific
Appfile. This UUID allows you to modify the name of an application, or have
duplicate application names without conflicting.

If you delete this file, then deploys may duplicate this application since
Otto will be unable to tell that the application is deployed.

$ tree .otto
.otto
├── appfile
│   ├── Appfile.compiled
│   └── version
├── compiled
│   ├── app
│   │   ├── build
│   │   │   ├── build-ruby.sh
│   │   │   └── template.json
│   │   ├── deploy
│   │   │   └── main.tf
│   │   ├── dev
│   │   │   └── Vagrantfile
│   │   └── foundation-consul
│   │       ├── app-build
│   │       │   ├── main.sh
│   │       │   └── upstart.conf
│   │       ├── app-deploy
│   │       │   └── main.sh
│   │       ├── app-dev
│   │       │   ├── main.sh
│   │       │   └── upstart.conf
│   │       ├── app-dev-dep
│   │       │   └── main.sh
│   │       └── deploy
│   │           ├── main.tf
│   │           ├── module-aws
│   │           │   ├── join.sh
│   │           │   ├── main.tf
│   │           │   ├── outputs.tf
│   │           │   └── variables.tf
│   │           ├── module-aws-simple
│   │           │   ├── main.tf
│   │           │   ├── outputs.tf
│   │           │   ├── setup.sh
│   │           │   └── variables.tf
│   │           └── variables.tf
│   ├── foundation-consul
│   │   ├── app-build
│   │   │   ├── main.sh
│   │   │   └── upstart.conf
│   │   ├── app-deploy
│   │   │   └── main.sh
│   │   ├── app-dev
│   │   │   ├── main.sh
│   │   │   └── upstart.conf
│   │   ├── app-dev-dep
│   │   │   └── main.sh
│   │   └── deploy
│   │       ├── main.tf
│   │       ├── module-aws
│   │       │   ├── join.sh
│   │       │   ├── main.tf
│   │       │   ├── outputs.tf
│   │       │   └── variables.tf
│   │       ├── module-aws-simple
│   │       │   ├── main.tf
│   │       │   ├── outputs.tf
│   │       │   ├── setup.sh
│   │       │   └── variables.tf
│   │       └── variables.tf
│   └── infra-otto-getting-started
│       ├── main.tf
│       └── outputs.tf
└── data
    └── dev_ip

24 directories, 41 files
```

内部では、Terraformを利用して、本番環境向けのAWS環境の構築が自動で行われます。
例えば、Terraformの一部のファイルを見てみると、以下のようになっています。

```bash
$ cat .otto/compiled/app/foundation-consul/deploy/main.tf
provider "aws" {
    access_key = "${var.aws_access_key}"
    secret_key = "${var.aws_secret_key}"
    region = "${var.region}"
}

module "consul-1" {
    source = "./module-aws-simple"

    index = "1"
    private-ip = "10.0.2.6"
    ami = "${var.ami}"
    key-name = "${var.key_name}"
    subnet-id = "${var.subnet_public}"
    vpc-id = "${var.vpc_id}"
    vpc-cidr = "${var.vpc_cidr}"
}

output "consul_address" {
    value = "${module.consul-1.address}"
}
```

開発環境はVagrantで構築されます。
Vagrantfileを見てみると、Virtualboxのboxファイルを指定していることや、private network内に独自のIPが振られていることなどが分かります。
(なぜ100.〜のIPなんだろう…)

```bash
$ cat .otto/compiled/app/dev/Vagrantfile
(snip)
Vagrant.configure("2") do |config|
  config.vm.box = "hashicorp/precise64"

  # Host only network
  config.vm.network "private_network", ip: "100.xxx.xxx.xxx"

  # Setup a synced folder from our working directory to /vagrant
  config.vm.synced_folder '/Users/hoge/otto-getting-started', "/vagrant",
(snip)
  dir = "/otto/foundation-1"
  config.vm.synced_folder '/Users/hoge/otto-getting-started/.otto/compiled/app/foundation-consul/app-dev', dir
  config.vm.provision "shell", inline: "cd #{dir} && bash #{dir}/main.sh"
(snip)
```

また、上記で呼ばれているshell scriptを見てみると、apt-getと書かれています…
ということはDebian系のディストリビューションの利用が想定されているようです。

```bash
$ cat .otto/compiled/app/foundation-consul/app-dev/main.sh
(snip)
    oe sudo apt-get update -y
    oe sudo apt-get install -y unzip
    cd /tmp
    oe wget https://dl.bintray.com/mitchellh/consul/0.5.2_linux_amd64.zip -O consul.zip
    oe unzip consul.zip
    oe sudo chmod +x consul
    oe sudo mv consul /usr/local/bin/consul
    oe sudo mkdir -p /etc/consul.d
    oe sudo mkdir -p /mnt/consul
    oe sudo mkdir -p /etc/service
(snip)
```

ひとまず、自動で振られるPrivate IPを、自分の環境に合わせたかったので、以下のように変更しました。

```bash
$ sed -i.bak -e 's/100.xxx.xxx.xxx/192.168.33.100/g' .otto/compiled/app/dev/Vagrantfile
$ sed -i.bak -e 's/100.xxx.xxx.xxx/192.168.33.100/g' .otto/data/dev_ip
```

# 開発環境の構築

では、otto devを実行して開発環境を構築してみます。
hashicorpのboxファイルを取ってきて、それを用いて構築しているようです。

```
$ otto dev
==> Creating local development environment with Vagrant if it doesn't exist...
    Raw Vagrant output will begin streaming in below. Otto does
    not create this output. It is mirrored directly from Vagrant
    while the development environment is being created.

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'hashicorp/precise64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'hashicorp/precise64' is up to date...
(snip)
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: stdin: is not a tty
==> default: [otto] Installing Consul...
==> default: [otto] Installing dnsmasq for Consul...
==> default: [otto] Configuring consul service: otto-getting-started
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: stdin: is not a tty
==> default: [otto] Setting locale to en_US.UTF-8...
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: [otto] Adding apt repositories and updating...
==> default: [otto] Installing Ruby 2.2 and supporting packages...
==> default: [otto] Configuring Ruby environment...
==> default: [otto] Installing Bundler...
==> default: [otto] Bundling gem dependencies...
==> default: [otto] Configuring Git to use SSH instead of HTTP so we can agent-forward private repo auth...

==> Caching SSH credentials from Vagrant...
==> Development environment successfully created!
    IP address: 192.168.33.100
(snip)
```

作成した環境にログインしてみます。Ubuntuが起動してきたことが分かります。

```bash
$ otto dev ssh
==> Executing SSH. This may take a few seconds...
Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic x86_64)

 * Documentation:  https://help.ubuntu.com/
New release '14.04.3 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Welcome to your Vagrant-built virtual machine.
Last login: Fri Sep 14 06:23:18 2012 from 10.0.2.2
vagrant@precise64:/vagrant$
```

続いてサンプルアプリケーションを起動してみます。

```bash
$ bundle && rackup --host 0.0.0.0
Using rack 1.6.4
Using rack-protection 1.5.3
Using tilt 2.0.1
Using sinatra 1.4.6
Using bundler 1.10.6
Bundle complete! 1 Gemfile dependency, 5 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.
[2015-YY-MM 13:59:13] INFO  WEBrick 1.3.1
[2015-YY-MM 13:59:13] INFO  ruby 2.2.3 (2015-08-18) [x86_64-linux-gnu]
[2015-YY-MM 13:59:13] INFO  WEBrick::HTTPServer#start: pid=6874 port=9292
```

その後、ブラウザから http://指定したIPアドレス:9292/ へアクセスし、サンプルアプリケーションが閲覧できることが確認できました。

現時点でのステータスは以下のようになっています。
開発環境だけがCREATEされた状態です。

```bash
$ otto status
==> Loading status...
    Depending on your configured directory backend, this may require
    network operations and can take some time. On a typical broadband
    connection, this shouldn't take more than a few seconds.
==> App Info
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)
==> Component Status
    Dev environment: CREATED
    Infra:           NOT CREATED
    Build:           NOT BUILT
    Deploy:          NOT DEPLOYED
```

# 本番環境構築

続いて、本番環境の構築を行います。
デフォルトでは、AWS環境のバージニア北部リージョン(us-east-1)にVPCが作成されます。

が、存在しないterraformのバージョンをダウンロードしようとして(status code 404)エラーになりました…

```bash
$ otto infra
==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Existing infrastructure credentials were not found! Otto will
    now ask you for infrastructure credentials. These will be encrypted
    and saved on disk so this doesn't need to be repeated.

    IMPORTANT: If you're re-entering new credentials, make sure the
    credentials are for the same account, otherwise you may lose
    access to your existing infrastructure Otto set up.

AWS Access Key
  AWS access key used for API calls.

  Enter a value: xxxxxxxxxxxxxxxxxx

AWS Secret Key
  AWS secret key used for API calls.

  Enter a value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

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:

(snip)

==> Downloading terraform v0.6.7...
    URL: https://dl.bintray.com/mitchellh/terraform/terraform_0.6.7_darwin_amd64.zip

Error occurred: Error downloading, status code 404
```

そのため、今回は手動で事前にインストールしました。

```bash
$ brew install terraform
```

その後、再度実行してみます。

```bash
$ otto infra
(snip)
==> Detecting infrastructure credentials for: otto-getting-started
==> 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_internet_gateway.public: Creating...
  vpc_id: "" => "vpc-xxxxxxxx"
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-xxxxxxxx"
aws_key_pair.main: Creating...
  fingerprint: "" => "<computed>"
  key_name:    "" => "otto-xxxxxxxx"
  public_key:  "" => "ssh-rsa xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
aws_key_pair.main: Creation complete
aws_internet_gateway.public: Creation complete
aws_route_table.public: Creating...
  route.#:                                    "" => "1"
  route.3593146892.cidr_block:                "" => "0.0.0.0/0"
  route.3593146892.gateway_id:                "" => "igw-xxxxxxxx"
  route.3593146892.instance_id:               "" => ""
  route.3593146892.network_interface_id:      "" => ""
  route.3593146892.vpc_peering_connection_id: "" => ""
  tags.#:                                     "" => "1"
  tags.Name:                                  "" => "public"
  vpc_id:                                     "" => "vpc-xxxxxxxx"
aws_subnet.public: Creation complete
aws_route_table.public: Creation complete
aws_route_table_association.public: Creating...
  route_table_id: "" => "rtb-xxxxxxxx"
  subnet_id:      "" => "subnet-xxxxxxxx"
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/xn/xxxxxxxxxxxxxxxxxxxxxxxx/T/otto-tf xxxxxxxx/state

Outputs:

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

==> Terraform execution complete. Saving results...
==> Building infrastructure for foundation: consul
Get: file:///Users/hoge/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-xxxxxxxx"
module.consul-1.aws_security_group.consul: Creation complete
module.consul-1.aws_instance.consul: Creating...
  ami:                               "" => "ami-xxxxxxxx"
  availability_zone:                 "" => "<computed>"
  ebs_block_device.#:                "" => "<computed>"
  ephemeral_block_device.#:          "" => "<computed>"
  instance_type:                     "" => "t2.micro"
  key_name:                          "" => "otto-xxxxxxxx"
  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-xxxxxxxx"
  tags.#:                            "" => "1"
  tags.Name:                         "" => "consul 1"
  tenancy:                           "" => "<computed>"
  vpc_security_group_ids.#:          "" => "1"
  vpc_security_group_ids.3442394215: "" => "sg-xxxxxxxx"
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: 52.91.xxx.xxx
module.consul-1.aws_instance.consul (remote-exec):   User: hoge
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 1349
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/xn/xxxxxxxxxxxxxxxxxxxxxxxx/T/otto-tf xxxxxxxx/state

Outputs:

  consul_address = 10.0.2.6

==> 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`.
```

VPCとConsul Serverが作成されたことが確認出来ました。

```bash
$ aws ec2 describe-vpcs
{
    "Vpcs": [
        {
            "VpcId": "vpc-xxxxxxxx",
            "InstanceTenancy": "default",
            "Tags": [
                {
                    "Value": "otto",
                    "Key": "Name"
                }
            ],
(snip)

$ aws ec2 describe-instances | jq -r '.Reservations[].Instances[]|{PrivateIpAddress}'
{
  "PrivateIpAddress": "10.0.2.6"
}
```

ステータスを確認してみます。
InfraがREADYになっています。

```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
```

# 本番環境をビルド

otto buildを実行すると、PackerによってAWSのAMIが作成されます。
アプリケーションのデプロイはAWSのAMIからEC2インスタンスを作成し、それを入れ替える、という形で行われるようです。

尚、最初にotto infraを実行した際に指定したパスフレーズが聞かれますので、入力します。

```bash
$ otto build
==> Detecting infrastructure credentials for: otto-getting-started (aws)
(snip)
Encrypted Credentials Password
(snip)
==> otto: Prevalidating AMI Name...
==> otto: Inspecting the source AMI...
==> otto: Creating temporary keypair: packer xxx
==> 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-xxxxxxxx
==> otto: Waiting for instance (i-xxxxxxxx) to become ready...
==> otto: Waiting for SSH to become available...
==> otto: Connected to SSH!
==> otto: Provisioning with shell script: /var/folders/xn/xxxxxxxx/T/packer-shellxxxxxxxx
==> otto: Uploading /Users/hoge/otto-getting-started/.otto/compiled/app/foundation-consul/app-build/ => /tmp/otto/foundation-1
==> otto: Provisioning with shell script: /var/folders/xn/xxxxxxxx/T/packer-shellxxxxxxxx
    otto: [otto] Installing Consul...
    otto: [otto] Installing dnsmasq for Consul...
    otto: [otto] Configuring consul service: otto-getting-started
==> otto: Uploading /var/folders/xn/xxxxxxxx/T/otto-slug-xxxxxxxx
=> /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 xxxxxxxx
    otto: AMI: ami-xxxxxxxx
==> otto: Waiting for AMI to become ready...
==> otto: Terminating the source AWS instance...
==> otto: Cleaning up any extra volumes...
==> 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-xxxxxxxx
==> 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.
```

作成されたAMIを確認してみます。
AMIが作成されたことが確認できます。

```bash
$ aws ec2 describe-images --owners self | jq '.Images[] | {Name, ImageId}'
{
  "Name": "otto-getting-started xxxxxxxx",
  "ImageId": "ami-xxxxxxxx"
}
```

# 本番環境へデプロイ

続いて上記で作成したAMIを用いて、本番環境にデプロイします。
otto deployを実行します。
EC2インスタンスが作成され、パブリックDNSが割り当てられたことが確認できます。

```bash
$ otto deploy
(snip)
  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-xxxxxxxx"
  owner_id:                            "" => "<computed>"
  vpc_id:                              "" => "vpc-xxxxxxxx"
aws_security_group.app: Creation complete
aws_instance.app: Creating...
  ami:                               "" => "ami-xxxxxxxx"
  availability_zone:                 "" => "<computed>"
  ebs_block_device.#:                "" => "<computed>"
  ephemeral_block_device.#:          "" => "<computed>"
  instance_type:                     "" => "t2.micro"
  key_name:                          "" => "otto-xxxxxxxx"
  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-xxxxxxxx"
  tags.#:                            "" => "1"
  tags.Name:                         "" => "otto-getting-started"
  tenancy:                           "" => "<computed>"
  vpc_security_group_ids.#:          "" => "1"
  vpc_security_group_ids.1219882143: "" => "sg-xxxxxxxx"
aws_instance.app: Creation complete

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

(snip)

Outputs:

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

成功したので、ブラウザより
http://ec2-52-91-XX-XX.compute-1.amazonaws.com/
へアクセスしてみます。
開発環境と同じアプリケーションが閲覧出来ることが確認できました。

尚、この時のステータスは以下のようになっています。

```bash
$ otto status
==> Loading status...
    Depending on your configured directory backend, this may require
    network operations and can take some time. On a typical broadband
    connection, this shouldn't take more than a few seconds.
==> 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
```

これで、一通りの流れを実行したことになります。

# まとめ

今回、チュートリアル通りにコマンドを実行すれば、とりあえずサンプルは簡単に動かすことが出来ました。

HashiCorpのプロダクトは、多くのものがリリースされており、それらを1つ1つ使いこなすのは割りと大変(と思っている)ですが、
Ottoを使えば、それらのすべてを詳しく理解していなくても動かすことができそうです。
ただ、中で何が行われているかの詳細を知りたい場合、使われているプロダクトが多い分、大変な印象を受けました。

また、上記で見た限りではRedHat系ディストリビューションへの対応が分からなかったので(Vagrantfileで指定するboxを変更してapt-getの部分を書き換えて動かしたケースもあるようですが、今回は未検証)、
例えばCentOSを使いたい、等の場合はどうなるのか、というところが気になります。

なんにせよ、まだ始まったばかりのプロダクトのため、今後も注目して見ていきたいと思います。