OSSのカスタマイズと、実環境への反映をGitで行う運用について考えてみる | サイバーエージェント 公式エンジニアブログ

はじめまして、2011年9月に入社した 前田 です。アメーバのサービス向けのサーバや、開発環境向けサーバを提供するプライベートクラウドを運用・構築を行うクラウドチームで仕事をしています。このエンジニアブログにも何度か登場している”Maeda”さんと同じチームに所属しているので、社内では”こうへい”と呼ばれています。前田姓は珍しくありませんが、個人的には今まで身近なところで被ることはあまりなかったので、家族以外から日常的にファーストネームで呼ばれるのは結構新鮮な体験です。


さて、今回のブログは、技術ネタをと依頼されたので、入社してからやっている亊や入社前にやってきたことに絡めた話を紹介したいと思います。このエンジニアブログを読まれている皆さんは普段オープンソースソフトウェアを使って、バグがあったり機能が足りなかったら、パッチをモリモリ書いて、Upstream(開発元)にフィードバックされていることと思います。こういうケースでのGitの使用例は私も執筆に参加した書籍 [参考文献] に記載されています。一方で、実際に運用する時には利用環境独自のカスタマイズを行ったり、個別の設定も行っている場合もあると思います。自分で行った汎用的な変更や個別カスタマイズの変更を加えた環境を、Upstreamの変更に追従しながら、必要に応じて随時変更を加え、なおかつそれを本番環境に反映していくには、それぞれ工夫して運用されているのではないかと思います。今回はそういった場合にGitを活用してみよう、というお話です。




背景


クラウドチームでは、エンジニアの検証環境にOpenStackを使っています。現在は依頼ベースでクラウドチームでゲストOSを提供していますが、euca2oolsを使ってコマンド一発でOS起動も含めて10秒程度で用意できます。必要な時に必要な分だけサーバをすぐにガンガン立てられるのは便利ですね。


現在はOpenStackを検証環境だけでなく本番環境にも導入しようとしています。本番環境での運用を始めるときには、今までの依頼ベースでのゲストOSを作成するプロセスから、(クラウドチームにとってのユーザである)社内エンジニア自身で必要なときに必要な分だけサーバを用意できるようする予定です。ゲストOS自体の管理は現在もユーザ自身で行っていますが、ゲストOSのIPアドレスはOpenStackによって動的に割り当てられます。そこでユーザがゲストOS作成時に指定するホスト名と、動的に割り当てられるIPアドレスを紐付けられるようにするために、 PowerDNS をローカルDNSとして導入することにしました。


このローカルDNSの導入にはもう一つ別の目的があります。普段エンジニアがサーバにログインする場合には、IPアドレスもしくは個人ごとに管理しているhostsファイルで名前解決をしてサーバにアクセスします。そこで、この個人用のhostsファイルの代わりとしてもPowerDNSを兼用することにしました。最終的にはインフラエンジニア以外でも簡単にレコードの追加・変更ができるようにしたいこともあり、使いやすい管理ツールが必要です。そこで、PowerDNS用のWebアプリ管理ツールである、 PowerDNS GUI を使うことにしました。





PowerDNS GUIとは


PowerDNS GUI(以降、pdns-gui)は、GPL v2で公開されているPHPで書かれたWebアプリケーションで、PowerDNSのデータベースはMySQLであることが前提です。今回、導入したPowerDNSのデータベースにもMySQLにしていたこともありちょうど良い条件です。また、Upstreamの デモサイト を見ると分かりますが、見た目や使い勝手が結構良いツールです。


ただ、導入にあたりいくつかバグがあったり、検索機能がないという問題もあり、バグフィックスと機能拡張を行いました。汎用的な変更についてはパッチを作成し、Upstreamにフィードバックを行いました。一方、社内独自の設定やカスタマイズについては、独自に管理する必要があります。独自設定・カスタマイズ部分については、テスト環境を新しく作って再インストールする手間を減らす為に作成していた、PowerDNSとpdns-gui用の自動インストール用のスクリプトと一緒に管理するようにしました。 [1]





Upstreamから開発、本番環境への反映への流れ


pdns-gui自体のバグフィックスと拡張、独自設定およびインストールスクリプトの管理、本番環境へのパッチの反映は、基本的にGitで行いました。Upstreamから、パッチ作成、本番環境への反映の流れは図1のようになります。


図1. Upstreamから本番環境への反映までのフロー



サイバーエージェント 公式エンジニアブログ-Upstreamから本番環境への反映までのフロー


pdns-guiはGoogle Codeでホスティングされており、 tarballも公開されています が、バグフィックスや機能拡張を行うため、Subversionのtrunkからソースコードを取得しました。このリポジトリに対してはコミット権はありません。ですので、まず開発用PCにgit svn cloneで複製しました。


$ git svn clone http://pdns-gui.googlecode.com/svn/trunk/ pdns-gui



これをチームで使っている社内のGitリポジトリで管理します。 [2]


$ cd pdns-gui
$ git remote add internal-repos mkouhei@gitsrv:/repos/pdns-gui.git
$ git push --mirror internal-repos


バグフィックスや機能拡張は、masterブランチのHEADを基点にしてトピックブランチを作成し、変更を行います。


$ git branch
* master
$ git checkout -b fix_bug



コミット後、masterブランチとの差分をgit format-patchでパッチを作成します。


$ git format-patch -o ../paches/ master


このパッチを前述のインストールスクリプトのリポジトリで管理します [3] 。なお、汎用的な変更については、ここで生成したパッチを使って、UpstreamにメールしたりBTS(Bug Tracking System)などに登録するなどでフィードバックします。本番環境や検証環境へのインストールには、導入対象のサーバの中でインストールスクリプトのリポジトリをgit cloneし、インストールスクリプトを実行すると、pdns-guiのリポジトリも社内のリポジトリサーバからgit cloneされるという流れです。





PowerDNSとpdns-guiのインストール


インストール時はまず実行ユーザのホームディレクトリなどでインストールスクリプトをgit cloneし、インストールスクリプトを実行すると、



  1. PowerDNSとMySQLのインストール

  2. 社内リポジトリのpdns-guiリポジトリをgit clone

  3. pdns-guiのmasterブランチを基点にしてdeployブランチを作成

  4. 汎用パッチをgit amで適用

  5. パッチ適用後のdeployブランチを基点して、マスターサーバ、スレーブサーバ毎のブランチ(dns_master, dns_slave)を作成

  6. マスターサーバ、スレーブサーバ毎のパッチを適用


という流れでリポジトリにパッチを適用し、ブランチを作成します。インストールスクリプトのワーキングツリーにgit cloneされたpdns-gui自体のリポジトリは図2のようなブランチの分岐の状態になります。


図2. 初回インストール時に作られるブランチ



サイバーエージェント 公式エンジニアブログ-初回インストール時に作られるブランチ


その後インストールスクリプトによって続きの処理が実行されます。



  1. pdns-guiのディレクトリが丸ごとApacheのDocumentRootのディレクトリにコピー(例:/var/www/pdns-gui)

  2. コピー先のpdns-guiのセットアップスクリプト(pdns-gui/batch/install.sh)を実行

  3. MySQL自体の設定と、pdns-guiとMySQLとの接続設定がされる

  4. 上記のMySQLとの接続設定をコミットする





運用中のサーバへのパッチ適用方法


すでにこのPowerDNSとpdns-guiは運用していますが、OpenStackとの連携を目的とした機能拡張を行っています。そこで新機能の追加やバグフィックスを行う場合の流れについて見てみます。


まず開発用PCのローカルリポジトリでトピックブランチを作ります(ここではnew_featureブランチ)。


$ cd pdns-setup/pdns-gui
$ git branch
* master
$ git checkout -b new_feature


変更を行ったらコミットします。コミット後の状態は汎用パッチなどは適用していない状態なので、図3のようになります。


図3 新機能のコミット後の状態



サイバーエージェント 公式エンジニアブログ-新機能のコミット後の状態


new_featureブランチとmasterブランチの差分(今回はコミット”h”のみ)をパッチファイルとして、インストールスクリプトのpatchesディレクトリにgit format-patchコマンドで出力します。


$ git format-patch -o ../patches master


パッチをインストール用スクリプトのリポジトリにコミットし、リモートリポジトリにpushします。


$ cd ..
$ git add patches/0001-new-feature.patch
$ git commit -sm "Add patch new feature."
$ git push origin master


これで、社内リポジトリに反映されました。


次に稼働中のサーバに反映させます。まず稼働中のサーバにログインし、ホームディレクトリ下にあるインストールスクリプトのリポジトリをgit pullでリモートリポジトリと同期します。そして先ほどのコミットで追加したパッチファイルをpatchesディレクトリから/tmpディレクトリにコピーします。


$ git pull
$ cp -i patches/0001-new-feature.patch /tmp


PowerDNS GUIのデプロイ先に移動し、deployブランチにpdnsユーザとしてチェックアウトします。 [4]


$ cd /var/www/pdns-gui
$ git branch
deploy
* dns_slave
master
$ sudo -u pdns git checkout deploy


pdnsユーザで/tmp下のパッチをgit amコマンドで適用すると、図4のようにdeployブランチにコミット”h”が取り込まれます。


$ sudo -u pdns git am /tmp/0001-new-feature.patch
Applying: new feature.


図4 deployブランチにコミット”h”を取り込む



サイバーエージェント 公式エンジニアブログ-deployブランチにコミット h を取り込む

最後に、実際に運用で使用しているブランチ(ここではdns_masterもしくはdns_slave)にチェックアウトし、コミット”h”を取り込みます。


$ sudo -u pdns git checkout dns_slave


dns_slaveブランチなどのサーバごとの独自の変更は設定ファイルなどに限定しているので、pdns-guiのロジック部分には直接関係ありません。ですので、汎用パッチを適用しているdeployブランチをベースにして、個別の変更を反映するようにしています。そのため、deployブランチからのmergeではなく、rebaseを行います。


$ sudo -u pdns git rebase deploy


rebase後のリポジトリの状態は、図5のようになります。


図5 git rebaseでdns_slaveブランチにコミット”h”を取り込む



サイバーエージェント 公式エンジニアブログ-git rebaseでコミット h を取り込み


以上でスレーブサーバへの反映は終わりです。マスターサーバでも同様の手順で反映しておきます。





まとめ


今回は、オープンソースソフトウェア自体の改変とフィードバック、そしてそれを実環境へ反映する運用をGitで行ってみる方法について紹介しました。Upstreamに追従しながら汎用的な変更用と、個別変更を管理し、本番環境に反映させる運用方法は、他のケースでのシステム運用にも考えられると思います。今回紹介した方法はベストでは無いと思いますので、もっといい方法があればぜひ フィードバック をお願いします。











脚注
[1]quiltなどのパッチ管理ツールは今回は使っていません。
[2]リポジトリサーバには、あらかじめベアリポジトリを作成しておきます。
[3]インストールスクリプト用のディレクトリの中に、pdns-gui/ディレクトリが存在する、という構成です。
[4]この作業はpdns-guiにパッチを当てるだけなので、運用中のブランチから切り替えるだけならPowerDNS自体の動きには影響ありません。
[参考文献]Gitによるバージョン管理(オーム社) 第5章,第7章