全国1000万人の大トロ好きのみなさんこんにちは。
Hashicorpから新たにOttoと呼ばれるプロダクトがリリースされました。
Otto
はVagrant
の後継となるもので、開発からデプロイまで一気通貫で行うことができるソリューションでマイクロサービスでの活用も考慮されて作られているということで早速試してみました。 軽く触った印象としては、Vagrant
、Packer
、Terraform
、Consul
などいままでHashicorpが提供してきたツールを組み合わせて一気通貫で操作できるようになった、と考えるとわかりやすそうです。
https://ottoproject.io/downloads.html にアクセスして自分の環境にあったバイナリをダウンロードして展開します。展開したら実行できるようにPATH
に追加します。
僕の場合はアーカイブを~/tools/otto/
に配置したので以下のようにします。設定が終わったら環境変数を読み込み直してください。
export PATH=PATH:~/tools/otto/
インストールが終わったら、otto
コマンドを実行します。以下のように表示されればインストールは正常に完了しています。
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
まずは開発環境を作ってみます。サンプルが用意されているのでその通りやってみます。
以下のようにしてサンプルを取得して、当該ディレクトリに移動します。
git clone https://github.com/hashicorp/otto-getting-started.git
cd otto-getting-started
以下のファイルがあるはずです。単純なSinatraを使ったRubyアプリケーションです。
.
├── ./Gemfile
├── ./Gemfile.lock
├── ./README.md
├── ./app.rb
├── ./config.ru
└── ./views
├── ./views/index.erb
└── ./views/name.erb
ここで、以下を実行します。これはこのアプリケーションを動作させるのに必要な依存関係を調査し設定を行うものです。
otto compile
以下のように表示されればOKです。ottoではAppfile
というファイルに環境の設定を記述することもできますが、今回はアプリディレクトリにAppfile
が存在しないので、otto自身がプロジェクトの種類を検出しています。
==> 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 infastructure, 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.
これで準備が出来たので開発環境をビルドします。
otto dev
以下のようなログが出力されます。順番に見れば分かる通り、Vagrant
を使って仮想環境を用意しています。
==> 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: Box 'hashicorp/precise64' could not be found. Attempting to find and install...
default: Box Provider: virtualbox
default: Box Version: >= 0
==> default: Loading metadata for box 'hashicorp/precise64'
default: URL: https://atlas.hashicorp.com/hashicorp/precise64
==> default: Adding box 'hashicorp/precise64' (v1.1.0) for provider: virtualbox
default: Downloading: https://atlas.hashicorp.com/hashicorp/boxes/precise64/versions/1.1.0/providers/virtualbox.box
==> default: Successfully added box 'hashicorp/precise64' (v1.1.0) for 'virtualbox'!
==> default: Importing base box 'hashicorp/precise64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'hashicorp/precise64' is up to date...
==> default: Setting the name of the VM: dev_default_1443486119730_73300
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
default: Adapter 2: hostonly
==> default: Forwarding ports...
default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2222
default: SSH username: vagrant
default: SSH auth method: private key
default: Warning: Connection timeout. Retrying...
default:
default: Vagrant insecure key detected. Vagrant will automatically replace
default: this with a newly generated keypair for better security.
default:
default: Inserting generated public key within guest...
default: Removing insecure key from the guest if it's present...
default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
default: The guest additions on this VM do not match the installed version of
default: VirtualBox! In most cases this is fine, but in rare cases it can
default: prevent things such as shared folders from working properly. If you see
default: shared folder errors, please make sure the guest additions within the
default: virtual machine match the version of VirtualBox you have installed on
default: your host and reload your VM.
default:
default: Guest Additions Version: 4.2.0
default: VirtualBox Version: 4.3
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
default: /vagrant => /Users/ryutaroy/dev/otto-getting-started
default: /otto/foundation-1 => /Users/ryutaroy/dev/otto-getting-started/.otto/compiled/app/foundation-consul/app-dev
==> 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] Adding apt repositories and updating...
==> default: [otto] Installing Ruby 2.2 and supporting packages...
==> default: [otto] Installing Bundler...
==> 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: 172.16.1.250
A development environment has been created for writing a generic
Ruby-based app.
Ruby is pre-installed. To work on your project, edit files locally on your
own machine. The file changes will be synced to the development environment.
When you're ready to build your project, run 'otto dev ssh' to enter
the development environment. You'll be placed directly into the working
directory where you can run 'bundle' and 'ruby' as you normally would.
You can access any running web application using the IP above.
早速この仮想マシンにログインします。内部的にはvagrant ssh dev
と同じですね。
otto dev ssh
アプリケーションを起動します。
bundle && rackup --host 0.0.0.0
開発環境のアプリケーションは9292
ポートで起動するので、端末のブラウザを使って、http://[先ほどの起動時のログで出力されたIPアドレス]:9292/
にアクセスしましょう。以下のような画面が出れば成功です!
アプリケーションを開発する際は、ローカル端末側のディレクトリのファイルを編集すれば、そのディレクトリがVagrant
と共有されているのですぐに反映されます。例えば、views/index.erb
を編集してブラウザをリロードすればOKです。
本番環境にはAWSをはじめとしてさまざまな環境を利用することができます。なお環境の構築には内部的にTerraformが使われています。
まず、以下を実行します。対話的に設定を行うことになります。
otto infra
最初にいくつか聞かれますので適宜設定します。Terraformがインストールされていない場合はローカル端末にインストールも自動で行われます。
==> 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.
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...
Would you like Otto to install Terraform?
Otto requires terraform to be installed, but it couldn't be found on your
system. Otto can install the latest version of terraform for you. Otto will
install this into its own private data directory so it doesn't conflict
with anything else on your system. Would you like Otto to install terraform
for you? Alternatively, you may install this on your own.
If you answer yes, Otto will install terraform version 0.6.3.
Please enter 'yes' to continue. Any other value will exit.
Enter a value: yes
以降、どんどん処理が進みます。
==> Downloading terraform v0.6.3...
URL: https://dl.bintray.com/mitchellh/terraform/terraform_0.6.3_darwin_amd64.zip
71.8 MB/71.8 MB
==> Unzipping downloaded package...
==> terraform installed successfully!
==> 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_key_pair.main: Creating...
fingerprint: "" => "<computed>"
key_name: "" => "otto-4d26f729"
public_key: "" => "ssh-rsa(略)"
aws_internet_gateway.public: Creating...
vpc_id: "" => "vpc-4d26f729"
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-4d26f729"
aws_key_pair.main: Creation complete
aws_internet_gateway.public: Creation complete
aws_route_table.public: Creating...
route.#: "" => "1"
route.1371386380.cidr_block: "" => "0.0.0.0/0"
route.1371386380.gateway_id: "" => "igw-54fdc231"
route.1371386380.instance_id: "" => ""
route.1371386380.network_interface_id: "" => ""
route.1371386380.vpc_peering_connection_id: "" => ""
tags.#: "" => "1"
tags.Name: "" => "public"
vpc_id: "" => "vpc-4d26f729"
aws_subnet.public: Creation complete
aws_route_table.public: Creation complete
aws_route_table_association.public: Creating...
route_table_id: "" => "rtb-0649cf62"
subnet_id: "" => "subnet-dccd97f7"
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/b5/446vyv6x0sb6w0p23w1yjy_9cmhhvg/T/otto-tf684230868/state
Outputs:
infra_id = 4d26f729
key_name = otto-4d26f729
region = us-east-1
subnet_public = subnet-dccd97f7
vpc_cidr = 10.0.0.0/16
vpc_id = vpc-4d26f729
==> Terraform execution complete. Saving results...
==> Building infrastructure for foundation: consul
Get: file:///Users/ryutaroy/dev/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-4d26f729"
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-4d26f729"
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-dccd97f7"
tags.#: "" => "1"
tags.Name: "" => "consul 1"
tenancy: "" => "<computed>"
vpc_security_group_ids.#: "" => "1"
vpc_security_group_ids.303664619: "" => "sg-e04baa86"
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.23.200.202
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 1355
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/b5/446vyv6x0sb6w0p23w1yjy_9cmhhvg/T/otto-tf467388744/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`.
次にアプリケーションをビルドしましょう。ここではAWSのAMI(Amazon Machine Image)を作ります。内部的にPacker
を利用しているのでインストールされていない場合は自動でインストールされます。
otto build
を実行します。以下のようなログが表示され、AMIが作成されたのがわかります。すなわちAWSへのデプロイは毎回AMIを作りなおして、そのAMIからインスタンスを起動して入れ替えるというアプローチです(ということは状態をどう管理するかを考えないといけないことになります。将来的にはこの部分をコンテナ対応することを考えているという記載が公式サイトにあります)。
==> 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:
Would you like Otto to install Packer?
An older version of packer was found installed (0.7.2). Otto requires
version 0.8.6 or higher. Otto can install the latest version of packer
for you (0.8.6). Would you like Otto to update packer for you?
Please enter 'yes' to continue. Any other value will exit.
Enter a value: yes
==> Downloading packer v0.8.6...
URL: https://dl.bintray.com/mitchellh/packer/packer_0.8.6_darwin_amd64.zip
132 MB/132 MB
==> Unzipping downloaded package...
==> packer installed successfully!
==> 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 5609e662-fbf7-2499-bfbf-09f464ce4d98
==> 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-f0d9db53
==> otto: Waiting for instance (i-f0d9db53) to become ready...
==> otto: Waiting for SSH to become available...
==> otto: Connected to SSH!
==> otto: Provisioning with shell script: /var/folders/b5/446vyv6x0sb6w0p23w1yjy_9cmhhvg/T/packer-shell530463319
==> otto: Uploading /Users/ryutaroy/dev/otto-getting-started/.otto/compiled/app/foundation-consul/app-build/ => /tmp/otto/foundation-1
==> otto: Provisioning with shell script: /var/folders/b5/446vyv6x0sb6w0p23w1yjy_9cmhhvg/T/packer-shell453867810
otto: [otto] Installing Consul...
otto: [otto] Installing dnsmasq for Consul...
otto: [otto] Configuring consul service: otto-getting-started
==> otto: Uploading /var/folders/b5/446vyv6x0sb6w0p23w1yjy_9cmhhvg/T/otto-slug-127174387 => /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 1443489378
otto: AMI: ami-8d1f5ae8
==> 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-8d1f5ae8
==> 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.
最後にデプロイしましょう。
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-4d26f729"
owner_id: "" => "<computed>"
vpc_id: "" => "vpc-4d26f729"
aws_security_group.app: Creation complete
aws_instance.app: Creating...
ami: "" => "ami-8d1f5ae8"
availability_zone: "" => "<computed>"
ebs_block_device.#: "" => "<computed>"
ephemeral_block_device.#: "" => "<computed>"
instance_type: "" => "t2.micro"
key_name: "" => "otto-4d26f729"
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-dccd97f7"
tags.#: "" => "1"
tags.Name: "" => "otto-getting-started"
tenancy: "" => "<computed>"
vpc_security_group_ids.#: "" => "1"
vpc_security_group_ids.2490306598: "" => "sg-c05fbea6"
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/b5/446vyv6x0sb6w0p23w1yjy_9cmhhvg/T/otto-tf876303855/state
Outputs:
url = http://[自動で割り当てられたホスト名].compute-1.amazonaws.com/
ここで最後にurlが表示されたので、アクセスしてみましょう。正常に表示されればデプロイ完了です!
ここまでとりあえずチュートリアルに書いてある通りにやってみましたが、非常に簡単に開発環境と本番環境を扱えるのは良いですね。 内部のツールには使い慣れているツールが多数使われているので勘所もつかみやすいと思います。 今後も注目のプロダクトといって間違いないでしょう。