無印吉澤

Site Reliability Engineering(SRE)、ソフトウェア開発、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

2015年12月に発表したスライド2件(Embulk 関連、Otto 関連)

年末は、この忙しい時に Ingress やりに沖縄行って美味しいもの食べてたせいでプレゼンの準備でバタバタしてました。

いずれのプレゼンもこのブログに書いたメモを発展させた内容なので、このブログしか見てない方向けにスライドを紹介しておきます。

あえてEmbulkを使ってログ監視すると幸せになれる状況、およびその導入事例

概要

Embulk Meetup Tokyo #2 の LT で発表しました。
意味のある情報を取り出せないログが大量にある場合に、Embulk を活用したログ監視システムを導入し、サービスの現状把握および改善活動に繋げた事例の紹介です。

紹介ページ

Embulk Meetup Tokyo #2 での LT 内容の補足と、イベントの感想 | GMOインターネット 次世代システム研究室

スライド

関連ページ

HashiCorp製品(Vagrant, Consul, Atlas, Otto)の活用による開発環境構築の自動化

概要

次世代システム研究室 2015 年 4Q の研究発表会にて発表しました。
HashiCorp の新ツール “Otto” に関する調査結果の紹介です。

紹介ページ

HashiCorp製品(Vagrant, Consul, Atlas, Otto)の活用による開発環境構築の自動化について発表しました | GMOインターネット 次世代システム研究室
(この記事は HashiCorp Advent Calendar 2015 の第22日目です)

スライド

関連ページ

HashiCorp のツールを動かすと自動的に作られる checkpoint_signature とは何か?

Otto の動作を調べていて気付いたのですが、ottoコマンドを実行すると ~/.otto.d ディレクトリ以下に checkpoint_signature というファイルが自動的に作られるようです。

例えば、一度 ~/.otto.d ディレクトリを削除してから otto version を実行すると、以下のようにファイルが作られます。

% rm -rf ~/.otto.d
% otto version
Otto v0.1.2

% tree -F ~/.otto.d
/Users/myoshiz/.otto.d
├── checkpoint_cache
└── checkpoint_signature

0 directories, 2 files

checkpoint_signature の中身は、以下のようなテキストファイルです。

8e7691ee-fa98-9ccb-260b-27031234fd81


This signature is a randomly generated UUID used to de-duplicate
alerts and version information. This signature is random, it is
not based on any personally identifiable information. To create
a new signature, you can simply delete this file at any time.
See the documentation for the software using Checkpoint for more
information on how to disable it.

手元の環境で見てみたら ~/.vagrant.d/data ディレクトリ以下にも checkpoint_signature と checkpoint_cache がありました。これって一体何だろう?と思って、少し調べてみました。

Checkpoint は HashiCorp 製品の最新情報を取得する機能

"hashicorp checkpoint" で検索したところ、Checkpoint (https://checkpoint.hashicorp.com/) のページが見つかりました。

このサイトにある説明が、Checkpoint のほとんどすべてなのですが、日本語訳すると次の通りです。

  • Checkpoint は、OSSまたはプロプライエタリ製品の、バージョンおよびアラート情報を提供するために HashiCorp が動作させているサービス
  • HashiCorp 製品は、時々、Checkpoint API に対して、その製品が動作する環境の情報を送信する
  • この送信は必須ではなく、送信するかどうかは設定可能
  • Checkpoint は HashiCorp 製品に対して、動作中のバージョンが最新か、セキュリティ脆弱性があるか、などの情報を提供する

また、この Checkpoint サービスのクライアント側の(Ottoなどに組み込まれている)ソースコードは、GitHubで公開されていました。

hashicorp/go-checkpoint
https://github.com/hashicorp/go-checkpoint

クライアント側は checkpoint.go(約350行)のみ、Go に詳しくなくても簡単に読める内容で、主な動作は以下の通りでした。

  • 環境変数 CHECKPOINT_DISABLE が設定されていたら、動作しない
  • ランダムな16バイトの signature (ファイル:checkpoint_signature)を作成する
  • signature には以下の2通りの用途がある
    • キャッシュに含まれる signature と比較し、キャッシュが正しいか確認する
    • signature をサーバに送信し、同じアラート情報を何回も繰り返し受信することを防ぐ
  • HTTPリクエストの送信先は
    https://checkpoint-api.hashicorp.com/v1/check/{{製品名}}
  • HTTPリクエストのURLパラメータは、以下の情報を含む
    • version: バージョン番号
    • arch: runtime.GOARCH の値、または呼び出し元で指定した文字列
    • os: runtime.GOOS の値、または呼び出し元で指定した文字列
    • signature: signature の値
  • HTTPレスポンスは、以下の情報を含むJSON
    • product: 製品名
    • current_version: 最新バージョンのバージョン番号
    • current_release: 最新バージョンのリリース日(UNIX時間)
    • current_download: ダウンロードページのURL
    • current_changelog_url: 最新バージョンのChangeLogのURL
    • project_website: 製品のWebサイトのURL
    • outdated: 古いバージョンの場合は true
    • alerts: アラートメッセージの配列
  • 前回のリクエストから CacheDuration(デフォルトは48時間)以内なら、リクエストを送信せずにキャッシュ(checkpoint_cache)を使う
  • キャッシュには、signature、バージョン番号、レスポンスの内容が書き込まれている

Checkpoint API を直接叩いてみる

例えば、Otto 用のURLに対してHTTPリクエストを送ると、以下のようなレスポンスが得られます。

% curl https://checkpoint-api.hashicorp.com/v1/check/otto
{"product":"otto","current_version":"0.1.2","current_release":1446498717,"current_download_url":"https://ottoproject.io/downloads.html","current_changelog_url":"https://github.com/hashicorp/otto/blob/v0.1.2/CHANGELOG.md","project_website":"https://ottoproject.io","alerts":[]}

Otto の現時点での最新版は 0.1.2 です。ここで 0.1.0 というバージョン番号を与えて呼び出すと、レスポンスに "outdated":true が追加されているのがわかります。

% curl "https://checkpoint-api.hashicorp.com/v1/check/otto?version=0.1.0"
{"product":"otto","current_version":"0.1.2","current_release":1446498717,"current_download_url":"https://ottoproject.io/downloads.html","current_changelog_url":"https://github.com/hashicorp/otto/blob/v0.1.2/CHANGELOG.md","outdated":true,"project_website":"https://ottoproject.io","alerts":[]}

まとめ

Checkpoint は、ソフトウェアの最新バージョンを自動的にチェックして、重要なセキュリティ脆弱性があればユーザに通知するための機能でした。ソースコードを読んでも、それ以外に、特に変わった機能は見当たりませんでした。同じような機能を作っている会社はあちこちにあると思います。

ただ、ローカル環境の情報をサーバに自動アップロードする、という機能は不信感を持たれがちなところです。それを、Vagrantなどのツールの一部として公開するのではなく、たとえ1ファイルでも個別のリポジトリとして公開するのは、ユーザからの信頼を得るには良い方法だと思いました。

解説:Otto の基本概念、フォルダ構造、および Otto 0.1 では未実装の機能一覧

f:id:muziyoshiz:20151101162325p:plain

目次

はじめに

Otto は version 0.1 ながら、ドキュメントが充実しており、それらを読んでいくだけで基本概念や、将来の実装予定についてかなりの情報を得ることができます。

ただ、個人的には、ドキュメントが多い分、どの情報がどこにあるのかわかりにくい面もありました。そこで、チュートリアルのためのコマンド例などは省いて、基本概念、フォルダ構造、未実装機能に関する情報に焦点を絞ってまとめてみました。

この記事は Otto の概要をすでに知っている方を対象にしています。概要については、HashiCorp社のHashimoto氏自身による、以下のブログ記事が分かりやすいです。

Ottoの基本概念

Getting Started Guide および Otto Documentation を元にまとめた、基本概念の説明です。

Appfile

アプリケーション設定の大本(source)として使われるファイル。アプリケーションの開発からデプロイを管理するための十分な情報を、Ottoに渡すために使われる。アプリケーション自身の設定と、そのアプリケーションが依存する他のアプリケーション(dependency)の設定を記載する。

Otto 0.1 が対応している Application Type は以下の通り。Custom は、Vagrant、Terraform、Packerの設定ファイルを直接指定する形式なので、設定ファイルを作りこめば、一覧にない Java などのデプロイも可能(参考:Customization - Custom App Type)。

Dependencyについては、前回の解説記事を参照のこと。

muziyoshiz.hatenablog.com

Infrastructure

デプロイ先のクラウドプラットフォームと、アプリケーションを動作させるために最低限必要なリソースのことを、Otto では "infrastructure" と呼ぶ。Otto 0.1 が対応している Infrastructure Type は AWS のみ(参考:Infrastructure - Otto by HashiCorp)。

Flavor

クラウドプラットフォームのシステム構成、言い換えるとotto infraコマンドの実行時に行われる設定のパターンのこと。Otto 0.1 では、Infrastructure Type "AWS" について、Flavor "simple" と "vpc-public-private" の2種類が実装されている(参考:Infrastructure Type: AWS)。

  • "simple": 1個の public subnet、1台の Consul server
  • "vpc-public-private": 1個の public subnet、1個の private subnet、NATインスタンス、踏み台ホスト(bastion host)、3台構成の Consul cluster

ちなみにクラウド関係で言うと、OpenStackにも フレーバー(OpenStack 運用ガイド) という概念があるが、それとは意味がだいぶ違う。

Foundation

Ottoが、実際のインフラを構築するための基本的な構成要素と考えるものを "foundation" と呼ぶ。Ottoはfoundationを自動的にインストール、設定、およびスケールする。以下は、foundation の一例。

  • Service Discovery: Consul による名前解決(<appname>.service.consulでアクセス可能にする)
  • Security: Vault によるアプリケーションの認証(※Otto 0.1では未実装)
  • Scheduling: Nomad によるスケジューリング(※Otto 0.1では未実装)

Otto 0.1 では、foundation は flavor によって決まり、カスタマイズできない。

Directory

複数のアプリケーションで共有される情報が格納されるレイヤ。デフォルトでは、ディレクトリのためのデータベースが ~/.otto.d/directory/otto.db に作られる。

Otto 0.1にはディレクトリを共有する仕組みがないため、異なる開発者が同じ Appfile を使ってデプロイを行っても、AWS上には2個の異なるインスタンスができてしまう。かといって、ディレクトリをネットワークストレージ経由で共有することは禁止されている(参照:Directory - Otto by HashiCorp)。将来的には、後述の shared directory がサポートされる予定。

フォルダ構造

Otto の用語 "directory" とファイルシステムのディレクトリとの混同を避けるため、以下ではファイルシステムの方は「フォルダ」と表記します。Otto関係のフォルダ構造は、以下の通りです。

こちらの内容は Appfile Compilation にある説明と、実機での動作確認結果をもとにまとめました。まだ理解できてない点については "?" と記載しています。

<app folder>
├─ Appfile ... 人手で作成・編集する Appfile (※必須ではない)
├─ .ottoid ... アプリを一意に識別するためのUUIDが書かれたファイル
└─ .otto ... otto compile で自動生成されるファイル群
     ├─ appfile
     │   ├─ Appfile.compiled ... otto compile コマンドが自動生成する Appfile
     │   ├─ deps ... dependencyの指定があるときに作られる
     │   │   └─ このアプリが依存するアプリの .ottoid および Appfile
     │   └─ version ... バージョン番号(1, 2, ...)が書かれたファイル
     ├─ compiled
     │   ├─ app
     │   │   ├─ build
     │   │   │   └─ otto build のための設定ファイル(Packer)
     │   │   ├─ deploy
     │   │   │   └─ main.tf ... otto deploy のための設定ファイル(Terraform)
     │   │   ├─ dev
     │   │   │   └─ Vagrantfile ... otto dev のための設定ファイル(Vagrant)
     │   │   └─ foundation-consul
     │   │        ├─ app-build
     │   │        ├─ app-deploy
     │   │        ├─ app-dev ... provision のために Vagrantfile から呼び出されるスクリプトおよび設定ファイル
     │   │        ├─ app-dev-dep
     │   │        └─ deploy
     │   ├─ dep-<dependencyの.ottoid (UUID)> ... dependencyの指定があるときに作られる
     │   │   ├─ deploy
     │   │   │   └─ デプロイするための設定ファイル(Terraform)
     │   │   ├─ dev
     │   │   │   └─ ローカルに構築するための設定ファイル(Vagrant)
     │   │   ├─ dev-dep
     │   │   │   └─ dependencyに指定された場合の設定ファイル(Vagrant)
     │   │   └─ foundation-consul
     │   │        └─ VM/インスタンスに Consul インストールするための設定ファイル
     │   ├─ foundation-consul
     │   │   ├─ app-build
     │   │   │   └─ アプリケーションに依存しない設定のテンプレート?
     │   │   ├─ app-deploy
     │   │   │   └─ アプリケーションに依存しない設定のテンプレート?
     │   │   ├─ app-dev
     │   │   │   └─ アプリケーションに依存しない設定のテンプレート?
     │   │   ├─ app-dev-dep
     │   │   │   └─ アプリケーションに依存しない設定のテンプレート?
     │   │   └─ deploy
     │   │        └─ otto infra で Consul ノード設定するための設定ファイル
     │   └─ infra-<infra name>
     │        └─ Infrastructure 構築するための設定ファイル(Terraform)
     └─ data
          ├─ dev_ip ... 開発環境のVagrant VMのIPアドレス
          └─ vagrant
               └─ 通常 .vagrant フォルダ内に作成されるファイル群

~/.otto.d
├─ binaries
│   ├─ packer
│   │   └─ Packer関係のバイナリ群のインストール先
│   └─ terraform
│        └─ Terraform関係のバイナリ群のインストール先
├─ cache
│   └─ 暗号化されたcredential(例:AWSアクセスキー)などが格納されるフォルダ
├─ checkpoint_cache ... ?
├─ checkpoint_signature ... ?
├─ directory
│   └─ otto.db ... ディレクトリのためのデータベース
└─ ip.db ... IPアドレスのためのデータベース?

Appfile Compilation によると、上記ファイルのうち、バージョン管理システムで共有すべきファイルは Appfile と .ottoid のみ。

Otto 0.1では未実装の機能(ドキュメントの記載)

ドキュメントに記載されていた未実装機能です。リリース時期として具体的なバージョン番号が記載されていた機能については、以下の説明にもその旨を記載しました。

Application type および infrastructure type のプラグイン

Otto はプラグインアーキテクチャで設計されている。すべての App type および Infra type は、ビルトインのタイプも含めてプラグインである。しかし、Otto 0.1 ではプラグインは利用できず、0.2 から利用できるようになる予定。

参考:Plugins

Custom Foundations

Otto は foundation を設定および置き換えできるような方法で設計されている。しかし、この機能は Otto 0.1 では有効でなく、将来のバージョンで公開される予定。

参考:Foundations

Dependency versioning

依存先(Dependency)のバージョンを指定するための仕組みがない。将来的には重要機能(first class feature)としてサポートする。

参考:Dependencies

Docker対応

otto build コマンドで Docker container を作成できるようになる予定だが、Otto 0.1 ではできない(つまり、AWSへのコンテナのデプロイはできない)。近い将来に、コンテナを作成し、それを Nomad でデプロイできるようになる予定(補足:前回の解説記事で書いたように、ローカル環境へのコンテナのデプロイはできます)。

参考:Build

また、Otto を Docker image の開発環境として使うことはできない。将来そうする計画はある。

参考:Dev - Docker (External) App Type

Shared Directory

共同作業のために、将来的には shared directory がサポートされる。最初にサポートされる shared directory は Alras の予定。

参考:Collaboration

VaultとNomadの自動インストール

近い将来、Otto は Consul 以外の foundation もサポートする。その具体例として、Vault と Nomad がある。

参考:Foundations

Datadogのようなサービスとの依存関係の定義

Appfile のなかでは、アプリケーションに関するすべての依存関係を定義できる。この依存関係には、他のアプリケーションに加えて、キューのようなインフラストラクチャの構成要素、および Datadog のような外部サービスを含む。

参考:Dependencies

Safe Upgrade(Ottoのアップグレード時の後方互換性)

Otto 0.1 から 0.2 にアップグレードする際の後方互換性は保証されない。Otto 0.2 では、恐らく、Safe upgrade のための機能が導入される。

参考:Backwards Compatibility

Otto 0.1では未実装の機能(実機での動作からの推測)

以下は、ドキュメントには記載がなかったものの、実機での動作を確認できなかった機能です。

AWSのリージョン指定

インスタンスのデプロイ先は us-east-1 (米国東部、バージニア北部リージョン)。以下のIssueがOpen状態のため、Appfileでリージョンを指定する方法はまだ無いと思われる。

参考:Enable AWS custom region selection · Issue #210 · hashicorp/otto

Dependency による複数台のVM/インスタンスの起動

この点はドキュメントに記載がなく、実機での動作からの推測だが、Appfile に dependency を記載しても、アプリケーションについては(infrastructureの分を除くと)1台のVM/インスタンスしか起動されない。詳細は前回の解説記事を参照のこと。

muziyoshiz.hatenablog.com

解説:Otto 0.1で dependency (AppType = docker) を指定した場合の動作

はじめに

Otto とは、9月のHashiConfで発表された、Vagrantの後継となるソフトウェアです。9月に Version 0.1.0 がリリースされ、現在の最新版は 0.1.2 です。

Ottoの設定ファイル "Appfile" には、アプリケーション間の依存関係を定義することができます。以下の例は、Rubyで書かれたWebアプリ(otto-getting-started)がMongoDBに依存していることを示しています(Getting Started GuideApp Dependencies ページより抜粋)。

application {
  name = "otto-getting-started"
  type = "ruby"

  dependency {
    source = "github.com/hashicorp/otto/examples/mongodb"
  }
}

dependencyのsourceが示す先にはそのアプリケーション(上記の例ではMongoDB)のAppfileがあります。そのAppfileにもdependencyが書かれていたら、Ottoは芋づる式にそれらのAppfileをダウンロードします。

Otto の目標は、このようにアプリケーションの依存関係が定義された1個の Appfile を元に、複数のアプリケーションが動作するVM群(またはコンテナ群)を一括セットアップできるようにすることです。この機能があることをもって、HashiCorpは、Otto は "Built for Microservices" である と標榜しています。

この目標に魅力を感じて、僕も最近少しずつOttoを試しています。ただ、Otto はリリースされたばかりの version 0.1 ということで、この機能(Dependency)はまだ未実装な部分が多いようです。自分の理解のためもあり、現状をまとめてみました。

結論

f:id:muziyoshiz:20151030234318p:plain

AppType = docker のアプリケーションを Dependency に記載した場合、ローカル環境とクラウドサービス(0.1はAWSのみ対応)へのデプロイ結果は以下のようになりました。

  • ローカル環境の構築時(otto dev実行時)

    • ローカル環境で1台のVirtualBox VMが起動され、その上でWebアプリが動作する。
    • このVMにDockerが自動インストールされて、VM上でMongoDBのコンテナが動作する。
    • VMにはConsulもインストールされ、VMからは mongodb.service.consul という名前でMongoDBに接続できる。
  • AWSへのデプロイ時(otto deploy実行時)

    • AWS上で1台のインスタンスが起動され、その上でWebアプリが動作する(インフラ構成=Flavor が "simple" の場合)。
    • このインスタンスにはDockerがインストールされず、従ってMongoDBのコンテナも動作しない。dependencyの設定は単純に無視される?
    • Consulはインストールされる。しかし、現状ではdependencyに関しては意味がない。

動作確認の結果

Getting Started Guide にある例を用いて、動作確認を行いました。以下、動作確認を行った際の手順、および実行結果の詳細です。

動作環境

  • OS: OS X Yosemite バージョン 10.10.5(14F27)
  • Otto: Mac OS X版 Otto 0.1.2

サンプルアプリのダウンロード

サンプルアプリ(Rubyで書かれたWebアプリケーション)をダウンロードします。

happyturn% git clone https://github.com/hashicorp/otto-getting-started.git

ここにはAppfileが含まれないので、App Dependencies にある内容でAppfileを作成します。

application {
  name = "otto-getting-started"
  type = "ruby"

  dependency {
    source = "github.com/hashicorp/otto/examples/mongodb"
  }
}

このmongodbディレクトリには、以下の内容の Appfile と、アプリケーションのIDが入った .ottoid ファイルが置かれています。これは、"mongo:3.0"という名前のdocker imageをダウンロードしてきて、"mongodb"という名前のアプリケーションとして動作させる、という設定です。

application {
    name = "mongodb"
    type = "docker-external"
}

customization "docker" {
    image = "mongo:3.0"
    run_args = "-p 27017:27017"
}

otto/Appfile at master · hashicorp/otto

コンパイル

otto compile コマンドを実行すると、このAppfileをもとに、Vagrant、Terraform、Consulなどの設定ファイルが自動生成されます。生成されたファイルは .otto フォルダ以下に格納されています。この処理を Otto では「コンパイル」と呼んでいます。

Otto 0.1.0 には、source = "github.com/hashicorp/otto/examples/mongodb" を指定するとコンパイルに失敗するというバグがあったのですが、最新版では解決しています。

happyturn% otto compile
==> Loading Appfile...
==> Fetching all Appfile dependencies...
    Fetching dependency: git::https://github.com/hashicorp/otto.git//examples/mongodb
==> Compiling...
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)

    Compiling infra...
    Compiling foundation: consul
==> Compiling dependency 'mongodb'...
==> 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.

ログから、Dependency 'mongodb' のダウンロードも行われたのが分かります。mongodbのAppfileは .otto/appfile/deps/b80064b1b081e385b5c0cf62e2b2b3f0/Appfile にダウンロードされています。このフォルダ名は、otto-getting-startedとmongodbのいずれのOtto IDとも一致しないのですが、何のIDなのかは分かりません……。

ローカル環境への構築

コンパイル後に otto dev コマンドを実行すると、ローカル環境にこれらのアプリケーションが構築されます。この処理の内部では Vagrant が使われています。以下のログの冒頭4〜5行目にあるように、OttoはVagrantの出力をミラーしているだけのようです。

このログをよく見ると、途中でdockerのインストールや、mongodbコンテナのダウンロードを行っているのがわかります。

happyturn% 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...
==> default: Setting the name of the VM: dev_default_1446212147870_20376
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> 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 => 2200 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
    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/myoshiz/Otto/otto-getting-started
    default: /otto/foundation-1 => /Users/myoshiz/Otto/otto-getting-started/.otto/compiled/app/foundation-consul/app-dev
    default: /otto/foundation-mongodb-1 => /Users/myoshiz/Otto/otto-getting-started/.otto/compiled/dep-24cbbebc-fc41-53b4-c844-60d642fb523c/foundation-consul/app-dev-dep
    default: /otto-deps/mongodb-24cbbebc-fc41-53b4-c844-60d642fb523c => /Users/myoshiz/Otto/otto-getting-started/.otto/appfile/deps/b80064b1b081e385b5c0cf62e2b2b3f0
==> 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: Running provisioner: docker...
    default: Installing Docker (latest) onto machine...
==> default: Starting Docker containers...
==> default: -- Container: mongodb
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: stdin: is not a tty
==> default: [otto] Configuring consul service: mongodb
==> 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: 100.115.125.105

    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.

ローカル環境への構築結果の確認

これで、1台のVMが起動し、そのうえでRubyアプリケーションと、MongoDBが動作するコンテナが動くようになりました。VMが1台しか動いていないことは、以下のコマンドで確認できます。ちなみに、otto dev vagrant [subcommand] で、Vagrantの任意のサブコマンドを実行できます。このように、ローカル環境に関しては、OttoはVagrantの単なるラッパーです。

happyturn% otto dev vagrant status
==> Executing: 'vagrant status'
Current machine states:

default                   running (virtualbox)

otto dev ssh コマンドで、いま起動したVMにログインできます。

happyturn% 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

dockerコマンドがすでにインストールされているので、以下のようにコンテナ上でMongoDBが動いていることを確認できます。

vagrant@precise64:/vagrant$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mongo               3.0                 c08c92f4cb13        6 days ago          261.6 MB

vagrant@precise64:/vagrant$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                      NAMES
a49699c16bfc        mongo:3.0           "/entrypoint.sh mong   9 minutes ago       Up 9 minutes        0.0.0.0:27017->27017/tcp   mongodb

vagrant@precise64:/vagrant$ docker exec -it a496 bash
<8d0520a79fe446e0# ps -ef | grep mongod
vboxadd   5939  5667  2 13:37 ?        00:00:18 mongod
root     10144 10128  0 13:49 pts/2    00:00:00 grep --color=auto mongod

Consulの動作も、以下のように確認できます。Consulの設定が自動で行われており、mongodb.service.consul という名前でMongoDBに接続できるようになっています。

vagrant@precise64:/vagrant$ consul members
Node       Address         Status  Type    Build  Protocol  DC
precise64  10.0.2.15:8301  alive   server  0.5.2  2         dc1

vagrant@precise64:/vagrant$ curl http://mongodb.service.consul:27017
It looks like you are trying to access MongoDB over HTTP on the native driver port.

当然ではありますが、Install MongoDB on Ubuntu — MongoDB Manual 3.0 の手順に従ってVM上にmongo shellをインストールすれば、以下のコマンドで、コンテナ上で動作するMongoDBサーバに接続できました。

vagrant@precise64:/vagrant$ mongo mongodb.service.consul:27017/test
MongoDB shell version: 3.0.7
connecting to: mongodb.service.consul:27017/test
>

2015-11-24追記

consul membersの出力にあるように、Consul AgentはVM上の1台しか動作していません。コンテナ上にはConsul Agentはありません。

しかし、以下の2点により、結果的にmongodb.service.consul:27017でMongoDBに接続できるようになっています。

  • Appfileに書かれた設定により、ローカルホストの27017番への通信がコンテナに転送されること。この転送設定は、docker psの出力からも確認できる。
  • Ottoが自動生成したprovisionのスクリプトにより、VM上の/etc/consul.d/service.mongodb.jsonに「MongoDBはローカルホストにある」という設定が書き込まれること。以下に、このjsonファイルの内容を示す。
{
  "service": {
    "name": "mongodb",
    "tags": [],
    "port": 0
  }
}

AWSへのデプロイ

AWSへアプリケーションをデプロイする際は、事前にotto infraコマンドと、otto buildコマンドを実行する必要があります。内部的には前者はTerraform、後者はPackerが使われています。今回の本題ではないので、詳細は割愛。

  • otto infra: 複数アプリケーションの共通基盤となるインスタンスを、AWSにデプロイ
  • otto build: デプロイ対象のアプリを含むAMIを作成(将来的にはコンテナ作成なども対応予定とのこと)

以下は、otto buildeのログ出力の例です。よく見てみると、otto devのときと違い、ログにdockerやmongodb関係のメッセージが出ていないのがわかります。つまり、AMIの作成時点で、dependencyの情報が無視されてしまっているようです。

happyturn% 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 563377c3-5671-2d2f-a472-67471e05270c
==> 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-665669d9
==> otto: Waiting for instance (i-665669d9) to become ready...
==> otto: Waiting for SSH to become available...
==> otto: Connected to SSH!
==> otto: Provisioning with shell script: /var/folders/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/packer-shell148828306
==> otto: Uploading /Users/myoshiz/Otto/otto-getting-started/.otto/compiled/app/foundation-consul/app-build/ => /tmp/otto/foundation-1
==> otto: Provisioning with shell script: /var/folders/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/packer-shell567335967
    otto: [otto] Installing Consul...
    otto: [otto] Installing dnsmasq for Consul...
    otto: [otto] Configuring consul service: otto-getting-started
==> otto: Uploading /var/folders/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/otto-slug-758424784 => /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 1446213571
    otto: AMI: ami-c03e4daa
==> 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-c03e4daa
==> 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コマンドを実行すると、ここで作ったAMIをもとに、インスタンスが作成されます。

happyturn% 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-571aac33"
  owner_id:                            "" => "<computed>"
  vpc_id:                              "" => "vpc-571aac33"
aws_security_group.app: Creation complete
aws_instance.app: Creating...
  ami:                               "" => "ami-c03e4daa"
  availability_zone:                 "" => "<computed>"
  ebs_block_device.#:                "" => "<computed>"
  ephemeral_block_device.#:          "" => "<computed>"
  instance_type:                     "" => "t2.micro"
  key_name:                          "" => "otto-571aac33"
  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-9b6163c2"
  tags.#:                            "" => "1"
  tags.Name:                         "" => "otto-getting-started"
  tenancy:                           "" => "<computed>"
  vpc_security_group_ids.#:          "" => "1"
  vpc_security_group_ids.1716715879: "" => "sg-fd872d9b"
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/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/otto-tf083753006/state

Outputs:

  url = http://ec2-52-91-29-204.compute-1.amazonaws.com/

AWSへのデプロイ結果の確認

起動したインスタンスへは、ubuntuユーザでSSH接続できます。AWSへの最初の接続時に、Ottoが、SSH接続に使う証明書を尋ねてくるので、そこで正しく設定していれば以下のコマンドで接続できるはずです。

happyturn% ssh ubuntu@52.91.29.204
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-63-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

  System information as of Fri Oct 30 14:15:28 UTC 2015

  System load: 0.39              Memory usage: 5%   Processes:       80
  Usage of /:  15.9% of 7.74GB   Swap usage:   0%   Users logged in: 0

  Graph this data and manage this system at:
    https://landscape.canonical.com/

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

ローカル環境の場合と違い、dockerコマンドはインストールされていません。

ubuntu@ip-10-0-2-222:~$ which docker
ubuntu@ip-10-0-2-222:~$

Consulはインストールされています。ただし、依存関係にあるアプリケーションが動作していないので、現時点ではあまり意味はありません。

ubuntu@ip-10-0-2-222:~$ consul members
Node           Address          Status  Type    Build  Protocol  DC
ip-10-0-2-222  10.0.2.222:8301  alive   client  0.5.2  2         dc1
ip-10-0-2-6    10.0.2.6:8301    alive   server  0.5.2  2         dc1

余談ですが、Rubyアプリケーションは /srv/otto-app 以下にインストールされていました。

ubuntu@ip-10-0-2-222:~$ ls -la /srv/otto-app/
total 40
drwxr-xr-x 5 otto-app otto-app 4096 Oct 30 14:03 .
drwxr-xr-x 3 root     root     4096 Oct 30 14:03 ..
-rw-r--r-- 1 otto-app otto-app  307 Oct 30 13:24 app.rb
drwxr-xr-x 2 otto-app otto-app 4096 Oct 30 14:03 .bundle
-rw-r--r-- 1 otto-app otto-app   42 Oct 30 13:24 config.ru
-rw-r--r-- 1 otto-app otto-app   57 Oct 30 13:24 Gemfile
-rw-r--r-- 1 otto-app otto-app  267 Oct 30 13:24 Gemfile.lock
-rw-r--r-- 1 otto-app otto-app  160 Oct 30 13:24 README.md
drwxr-xr-x 3 otto-app otto-app 4096 Oct 30 14:03 vendor
drwxr-xr-x 2 otto-app otto-app 4096 Oct 30 13:24 views

最後に

OttoのDependencyの機能はまだまだ開発途上であることが、今回の解説でわかって頂けたかと思います。ただ、Ottoの概念自体は、一度理解してしまえばわかりやすいですし、開発環境と本番環境を一元管理できれば確かに理想的ではあります。個人的には、今後しばらくOttoに注目していきたいと思っています。