グリーのインフラに Chef を導入した話
こんにちは。インフラストラクチャ本部の荒井 (@ryot_a_rai) です。グリーのインフラのサーバ管理周りをより良くする仕事をしています。
GREE Advent Calendar 2013 23日目はグリーで Chef を導入していく上でのプラクティスを紹介していきます。
Chef とは
まず、簡単に Chef についてご説明いたします。
Chef は Chef 社(旧Opscode社)が開発している、サーバの設定管理ツールです。Chef は OSS として開発が進められており、無料で利用することができます(有償でサポートを受けたり、Chef 社によってホストされたChef Serverを利用することもできます)。Chefではノードのあるべき姿を記述します。例えば以下の様なコードで、Apacheをインストールして、設定ファイルを置くなどのミドルウェアの設定を自動化することができます。
1 2 3 4 5 6 7 8 9 10 11 12 |
package "apache2" do action :install end service "apache2" do supports reload: true end template "/etc/apache2/apache.conf" do action :create notifies :reload, "service[apache2]" end |
類似のソフトウェアとして、Puppet や Ansible といったものもあります。こういったインフラ自動化まわりのソフトウェアについてはペパボの宮下さんの インフラ系技術の流れ が参考になります。
Chef in グリー
さて、グリーでのChefまわりの構成をご紹介します。下図が全体の構成です。
開発環境
開発は各個人のマシン上で仮想マシンを立ち上げて行なっています。クックブックの開発では、クックブックを開発する人が serverspec でテストを書くようにしていて、構築後のサーバが期待通り動くことをテストしています。一つのクックブックでも設定値などの条件によって動作が変わってくるため、test-kitchen を用いて複数の条件(ランリストやノードのアトリビュート(以下、「アトリビュート」)などの組み合わせ)でテストを行っています。
また、一部仮想マシンを使う必要がないテスト(attributes/*.rb のテストなど)を高速に行うため ChefSpec も併用しています。
これらのフレームワークを使うためのファイルがセットアップされたテンプレートが用意されており、新規でクックブックを作成する場合にはそれをコピーする流れになっています。
GitHub Enterprise -> Jenkins -> デプロイ
クックブック、ロール、構築対象ノードの設定値は GitHub Enterprise でバージョン管理されています。GitHub にプッシュすると Jenkins が Vagrant を使って Vagrant + serverspec や ChefSpec でテストした後、テストが通ったものをデプロイするようになっています。(余談ですが、Jenkins は keepalived, lsyncd で冗長化されています)
基本的に、クックブックの開発者は Pull Request を投げて、レビューを受けたあとマージされるようにしています。
アトリビュートのバリデーション
Chef ではレシピにしたがってサーバのプロビジョニングを行いますが、各ノードごとに変更する必要がある設定値はレシピ内でアトリビュートを参照することになります。もちろん、クックブックごとに必要なアトリビュートはレシピの作り方によるので、クックブックにより異なってきます。そのため、ノードが利用するクックブックに応じて、そのノードに適切なアトリビュートを設定する必要があります。クックブックが多くなってくると、どのアトリビュートを設定すべきかわからなくなったり、あるアトリビュートが不足していて、Chef がコケるということが起こってきます。
それを防ぐために、グリーではアトリビュートのバリデーションを行っています。JSONのバリデーションということで JSON Schema をまず検討しましたが、要件に対して不足があり、内製のバリデータを用意しています。このバリデータでは以下のようなルールを記述することでJSONのバリデーションが行えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ cat schema.rb object do object "apache" do string "dir" array("listen_ports") { integer(minimum: 0, maximum: 65535) } end end $ cat valid.json { "apache": { "dir": "/var/www", "listen_ports": [80] } } $ validator validate schema.rb valid.json $ cat invalid.json { "apache": { "listen_ports": [-1] } } $ validator validate schema.rb invalid.json [missing_property][at apache] Missing required property: dir [minimum][at apache/listen_ports] Value -1 is less than or equal to 0 |
クックブックの中にバリデーションルールを記述したファイルを入れておくと、そのノードが利用しているクックブックに応じて自動的にルールが読み込まれて、バリデーションが実行されます。この作業は前述の Jenkins によって自動化されています。
Chef Bridge
上記の構成図に chef-bridge という見慣れないものがあると思いますが、これは内製のChef用のサーバです。
Chef には Chef Server + Chef Client のサーバ・クライアント構成で利用する方法と、Chef Solo 単体で利用する方法が用意されています。グリーでは Chef Server を利用せず Chef Solo と内製の Chef Bridge を利用して、システムを構成しています。この理由としては、
- Chef Server にはサーバの情報・設定を管理する機能が備わっているが、グリーでは内製のシステムでサーバの情報・設定が一元管理されていて、データを二重に管理することになる
- 内製のサーバ管理システムの情報を Chef から透過的に扱いたい
- Chef Server の HA はオープンソース版ではサポートされていない
- 調査した結果、PostgreSQLのレプリケーションとLsyncdなどによるファイルの同期をすることによってスタンバイの Chef Server を構築することができることが分かったが、不整合が起こった場合などのトラブルを考えて避けたい
- Chef Server と Chef Client では実現できない(しにくい)機能を追加できるようにしておきたかった
- 例えば、クックブックのバージョンをアトリビュートに応じて動的に設定する機能が入っていたりします
- Chef Server はヘビーすぎる
といった点があります。
Chef Bridge はHTTPでリクエストを受けると指定されたノードの設定情報、クックブックなどを返すだけの、シンプルなサーバです。設定の変更などは Chef Bridge から行うことはできず、Gitから変更する必要があります。既存のサーバ管理システムとは Chef Bridge のみが会話するように構成していて、Chef 側から透過的に扱えます。
プロビジョニング
ノード側で gree-chef-client を実行することでプロビジョニングを開始することができます。gree-chef-client では、
- Chef Bridge に問い合わせて、サーバの設定、クックブックなどを取得する
- Chef Solo を実行するための準備
- Chef Solo を実行
- serverspec でテストを実行
を行っています。
まず、前述の Chef Bridge に問い合わせて、自分の設定や、必要なクックブックを取得します。それらを元に Chef::Config あたりを設定したり、ファイルを配置したりして、Chef Solo を実行できる状況をつくります。社内サーバ管理システムの情報はアトリビュートとして Chef Solo に渡されます。その後は、以下のように Chef Solo を起動して、プロビジョニングを行います。
1 2 3 |
solo = Chef::Application::Solo.new # 中略... solo.run_chef_client |
Chef Solo やクックブックから見ると社内サーバ管理システムの情報は単なアトリビュートとして見えるため、適切にアトリビュートを渡せば、Chef Bridge を利用せず Chef Solo 単体で実行することも可能になっています。
Chef でプロビジョニングが完了したあとは、serverspec でテストを走らせています。開発環境やJenkins上で仮想マシンを使ったテストを行っている上、構築したノードでテストを実行しているのは、クックブックがバージョンアップを重ねて何回も Chef が実行された場合に、適切にサーバがプロビジョニングされていることを確認したいからです。Immutable にサーバを扱う場合、Jenkins でテストされていれば十分だと考えられますが、グリーのように Mutable に Chef を走らせてサーバの状態を変更していく運用の場合、構築対象でテストを走らせるのは有用だと考えています。
まとめ
以上、簡単にではありますが、グリーでの Chef 導入におけるプラクティスを紹介してみました。実際の運用にのせるために不足した機能を補いつつ、導入を進めています。他社さんのプラクティスも気になるところです。
明日は @ichii386 さんです!