Tech Sketch Bucket of Technical Chips by TIS Inc.

AWS OpsWorksを使ってみた (技術編)

Pocket

AWS OpsWorksを使ってみた(概要編) では、AWS OpsWorksの概要について紹介しました。今回の記事ではそれに補足して、前回触れられなかったOpsWorksの機能の詳細や、OpsWorksの初期構築処理の仕組みに関して把握できた範囲で紹介します。


OpsWorksの各種機能

前回の記事 でも特徴の所で簡単に触れましたが、OpsWorksにはChefによる自動構築以外にも様々な機能が用意されています。まずは前回掘り下げられなかったこれらの機能について、簡単に紹介していきます。

Auto Healing (障害自動復旧)

Auto Healingは、インスタンスの障害を検知した際に代替となる新しいインスタンスを自動的に立ち上げる機能です。OpsWorksの各インスタンスではOpsWorks Agentと呼ばれるサービスが稼動しており、定期的にKeepaliveパケットを送信しています。それが途絶えた際にはOpsWorksが自動的に新しいEC2インスタンスを作成し、Chefにより同じ環境を再構築します。動作のイメージを表したのが以下の図1です。

opsworks_autohealing.png

図1. Auto Healing

従来のAWSでも、同じようなことはElastic Load Balancer(ELB)のヘルスチェックとAuto Scalingを組み合わせれば実現することができました。Auto Healingは、以下の点が従来のELB + Auto Scalingによる自動復旧と異なります。

  • 新たに構築された代替インスタンスは、障害インスタンスに割り当てられたElastic Block Store(EBS)を引き継ぐ
  • 障害検知のトリガーとなるのは、インスタンス上で稼動するOpsWorks AgentのKeepalive途絶
    (ELBのようにURLヘルスチェックを条件にはできない)

前者はELB + Auto Scalingでは実現できなかった大きなメリットと言えます。EBSを自動的に引き継ぐため、障害時のログなどのデータを失うことなく新インスタンスへの移行を行うことができます。

一方で後者は、その環境で動かしているサービスの稼動を保証できない点で物足りないと感じられる方もいるかもしれません。OpsWorks Agentのサービス自体は、別途Monitによる監視・自動再起動も行われているため厳重に守られていますが、その環境で動かすアプリケーションの動作はAuto Healingと連動しておらず、別途監視の仕組みが必要です。

Time/Load Based Instance

Time/Load Based Instanceは、指定した条件を満たした際に自動的に起動停止するインスタンスです。Time basedの場合は、指定した曜日の指定した時間帯になると起動し、その時間帯が終わると停止します。Load basedの場合は、そのレイヤ内の平均負荷が指定した値を超えると起動し、指定した値を下回ると停止します。

opsworks_scaling.png

図2. Time/Load Based Instance

これも従来のAWSでも、ELBとAuto Scalingを組み合わせれば同じようなことを実現することはできました。Time/Load Based Instanceは、以下の点が従来のELB + Auto Scalingによるスケーリングと異なります。

  • インスタンスを下限・上限台数の範囲で作成・廃棄するのではなく、予め用意しておいたインスタンスを条件を満たした際に起動停止する
  • Load basedで指定できる条件は、CPU, Memory, Loadの3種類の組み合わせ
  • Time basedで指定できる条件は、曜日と時刻による指定のみ

Auto Scalingでは新インスタンスを都度作成し、不要になったインスタンスはTerminateしていましたが、OpsWorksのこの機能では予めOpsWorksにインスタンスを登録しておき、その台数の範囲でStart/Stop (EBS backedの場合)を行います。Terminateでは無いので中のログ等が完全に失われることはありません(Instance storeのインスタンスを指定した場合はStopができないので、Auto Scalingと同様にTerminateされます)。またOpsWorksのChef処理と連携して起動するたびにConfigureに指定したレシピを実行させたり、停止するたびにShutdownに指定したレシピを実行させることでスケール時に適切に設定を変更することが出来ます。

一方で、Auto Scalingの場合はCloudWatchのアラームとして登録できるものであれば様々な条件を指定することが出来ましたが、OpsWorksのLoad based InstanceではCPU, Memory, Loadの3種の平均使用率をORで組み合わせた条件のみとなります。またAuto Scalingではas-put-scheduled-update-group-actionのコマンドでスケジュールを年月日でcronのように指定することが出来ましたが、OpsWorksのTime based Instanceでは曜日と時刻で指定することになります。このあたりは用途にもよりますが、気になる人もいるかもしれません。

Permissions

OpsWorksでは指定したIAMユーザに対し、各インスタンスにSSHログインしたり、sudoを行う権限を付与することが出来ます。具体的には、指定された内容に応じて自動的に各インスタンス内にユーザとauthorized_keysが作成・削除され、sudo権限が与えられた場合は/etc/sudoersも更新されます。これにより、管理者ユーザを一つ用意して担当者間で共用しなくても、ログイン用のユーザを必要な人数分全インスタンスに作成し、担当者の離任時には全インスタンスから該当ユーザを削除する処理が自動で行えるようになります。スケールアウト等で台数が増えた際も自動でユーザ作成が行われるので、台数の変動を気にする必要もありません。

OpsWorksによる初期構築処理の流れ

次に、OpsWorksによるインスタンス初期構築処理の流れを簡単に紹介します。OpsWorksではChefを実行するための環境構築などは全てOpsWorks側で自動的に行われるため、その部分を意識しなくてもサービスを利用することはできます。とは言え、どのような処理が行われているのかを把握しておけば、後から環境に手を加える際に役に立つでしょう。

全体の流れ

OpsWorksではインスタンスを追加して起動すると、大まかに分けて以下のプロセスで初期構築が行われます。

  1. AMIを基にEC2インスタンスを作成
  2. Cloud-initによるUserDataの実行
  3. UserDataによって取得されたOpsWorks Agentインストーラの実行
  4. OpsWorks Agentと各種子プロセスの起動
  5. OpsWorks Agentよる実行コマンド取得
  6. Built-in Chef recipes 及び Custom Chef recipes の実行

それぞれの処理の詳細を個別に見ていきましょう。

1. AMIを基にEC2インスタンスを作成

Management ConsoleからOpsWorksのインスタンスを起動すると、まず指定したOSに対応するAMIからEC2インスタンスが作成されます。この時点ではまだ普通のAmazon LinuxまたはUbuntuであり、特別な設定はされていません。Instance Type, Region, AZ, Key pair, Security Groups等は、Layer設定やInstance起動時の指定に従って割り当てられます。

2. Cloud-initによるUserDataの実行

作成されたインスタンスが起動されると、起動時に渡されたUserData内のスクリプトがcloud-initにより実行されます。この時の実行ログは /var/log/aws/opsworks/user-data.log に保存されています。Amazon Linuxの場合、主な処理内容は以下の通りです。

  • yum update
  • yum でRubyやRubyGem等をインストール
  • sshログイン時に表示されるMOTDを更新
  • OpsWorks Agentのインストーラをダウンロード
  • OpsWorks Agentのインストーラを実行
  • Kernelが更新された場合は再起動

より詳細に処理内容を知りたい場合は、Userdataを確認してみて下さい。インスタンスに与えられたUserdataはMeta-dataと同様に、OpsWorksのインスタンス上で以下のコマンドを実行することで確認できます。

3. UserDataによって取得されたOpsWorks Agentインストーラの実行

次に、UserDataのスクリプトからOpsWorks Agentのインストーラが実行されます。この時の実行ログは /var/log/aws/opsworks/installer.log に保存されています。主な処理内容は以下の通りです。

  • OpsWorks AgentとBuilt-in Chef recipesの設置 (/opt/aws/opsworks/currentの中身を作成)
  • Chefを含む必要なgem一式をbundle install
  • Chef-soloとBuilt-in Chef recipesを使ってMonitをインストール
  • その他細かい環境設定

4. OpsWorks Agentと各種子プロセスの起動

OpsWorks Agentのインストールが終わると、必要に応じて再起動が行われた後にOpsWorks Agentのサービスが起動し、OpsWorksと定期的に各種通信を行うようになります。この時のOpsWorks Agentの稼動ログは、 /var/log/aws/opsworks/opsworks-agent.log に保存されています。ログを見る限りでは、OpsWorks Agentは以下の3つの子プロセスを立ち上げ、それぞれの子プロセスは1分おきに通信を行っているようです。

keepalive AutoHealingの実行基準となるkeepaliveパケットの送信
statistics Management ConsoleのMonitoring画面に表示される各種統計データの送信
process_command 定期的にOpsWorksに対してpollingを行い、実行すべきコマンドが見つかればそれを実行して結果をOpsWorksにレポート

5. OpsWorks Agentによる実行コマンドの取得

このあたりの挙動は理解が不十分で誤りがあるかもしれませんが、OpsWorks Agentの子プロセスであるprocess_commandがOpsWorksに対してpollingを行った結果、実行すべきコマンド(ライフサイクルイベントのSetupやConfigure等)が見つかれば、コマンドに応じたChefの処理が実行されるのだと思われます。ここで例えばSetupがコマンドとして与えられた場合、Setupに対応したBuilt-in Chef recipesとCustom Chef recipesなどを含むJSONがパラメータとして渡されているようです。

この時のChefの実行ログは、 /var/lib/aws/opsworks/chef/%Y-%m-%d-%H-%M-%S.log に保存されています。またChefに与えられたJSONも同じ場所に置かれています。最新のChef実行ログは、opsworks-agent-cli show_log のコマンドでも確認できます。

Chef-soloが実行されると、まずrun_listに指定された以下の二つのレシピが実行されます。

  • opsworks_custom_cookbooks::load
  • opsworks_custom_cookbooks::execute

前者のloadでは、Custom Chef Cookbooksを有効にした場合のみ、指定された外部URLからCustom Chef Cookbooksを取得する処理が行われます。後者のexecuteでは、与えられたJSONの中の、node[:opsworks_custom_cookbooks][:recipes]のAttributeで指定されたレシピに対してinclude_recipeが呼び出されます。node[:opsworks_custom_cookbooks][:recipes]にはLayer設定画面で確認できるBuilt-in Chef recipesやCustom Chef recipesのレシピ名が格納されており、このレシピを通して任意のレシピが実行できるようになっています。

6. Built-in Chef recipes 及び Custom Chef recipes の実行

最後に、各レイヤに対応したBuilt-in Chef recipes, 及びユーザが指定したCustom Chef recipesがinclude_recipeで呼び出されて実行されます。ここから先の処理は、通常のChef-soloによるレシピ実行と違いはありません。OpsWorksでは様々なレイヤが標準で用意され、それぞれのレイヤごとに異なるレシピが用意されていますが、ここでは各レイヤに共通して含まれているレシピがどのような処理を行っているのか、概要を簡単にまとめました。

Built-in Chef recipesの内、Setupのライフサイクルで呼び出されるレシピには以下のものがあります。

レシピ名 概要
opsworks_initial_setup::sysctl ネットワーク周りのカーネルパラメータをチューニング
opsworks_initial_setup::limits /etc/limits.confの設定
opsworks_initial_setup::bind_mounts autofsの設定
opsworks_initial_setup::vol_mount_point /vol ディレクトリの作成
opsworks_initial_setup::remove_landscape landscape-common, landscape-clientパッケージの削除
opsworks_initial_setup::ldconfig ライブラリパスに/usr/local/libを追加
opsworks_initial_setup::tweak_chef_yum_dump Amazon Linuxかつt1.microの場合、yumのlock timeoutを調整
opsworks_initial_setup::setup_rhel_repos yumのepelリポジトリを有効にする
opsworks_initial_setup::package_procps procpsパッケージのインストール
opsworks_initial_setup::package_ntpd ntpdパッケージのインストール
opsworks_initial_setup::package_vim vimパッケージのインストール
opsworks_initial_setup::package_sqlite sqliteパッケージのインストール
opsworks_initial_setup::package_screen screenパッケージのインストール
ssh_host_keys /etc/ssh/ssh_host_rsa_key等を作成
ssh_users Permissionsの画面で指定したIAM Userに対応するユーザを作成
mysql::client MySQL Clientのインストール
dependencies Layer設定画面のOS packagesで指定したパッケージをインストール
ebs Layer設定画面で追加したEBSボリュームのファイルシステム構築, マウント, RAID構築
opsworks_ganglia::client Ganglia Clientのインストール

またLayer設定画面のBuilt-in Chef recipesの中には記載がありませんでしたが、Chefの実行ログを見ると以下のレシピも実行されています。これらはライフサイクルの種別に関わらず、どのライフサイクルイベント時も実行されているようです。

レシピ名 概要
opsworks_stack_state_sync /etc/hostsにStack内の各インスタンスの情報を登録
test_suite node[:opsworks][:run_cookbook_tests]が有効な場合にテストを実行
opsworks_cleanup 一時ファイルと古いログファイルの削除

それぞれのレシピを確認すれば、OpsWorksの裏側でどのような処理が行われているのかを垣間見ることが出来ます。
AWSが提供している各レシピのソースコードは、https://github.com/aws/opsworks-cookbooks で公開されています。自分でレシピを追加する際の参考にもなりますので、興味のある方は一度目を通してみて下さい。

まとめ

OpsWorksでは裏で様々な処理を行っており、ユーザはそれらを意識することなくChefを用いた環境の構成管理を行うことができます。しかし実際にどのような処理が行われているのかを知っておくことは、OpsWorksの仕組みを理解し、自由に構成をカスタマイズする上で役に立つと思います。実際に動かされているコードはインスタンス内のUser Dataや/opt/aws/opsworks/current/lib以下のファイルから確認できますし、各種レシピは全てGithubで公開されています。もし興味があれば確認してみてはいかがでしょうか。

エンジニア採用中!私たちと一緒に働いてみませんか?