カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

Dockerを使ってプロビジョニングツールのCI環境を作った話

新サービスのたちあげで、プロビジョニングツール(ChefとかAnsibleとか)のテストを継続的にやりたいなーと思っていたので、環境を作ってみたという話です。

やりたいことは以下。

  • ChefなりAnsbileでサーバプロビジョニングの内容をコード化
  • 特定のトピックブランチ上でそれらが開発され、リモートレポジトリにpush
  • そのレポジトリ上のプルリクをトリガーに、Jenkinsへフック
  • Jenkinsに作っておいたCI用のジョブが走り、docker build
  • build後serverspecが走り、テスト
  • 結果を通知

色々やりかたはあると思いますが、今回は↑のフローを想定して、設計をしました。今回はAnsbileを使ったので、Ansibleの説明となります。

Docker

まずCI環境用にDockerの環境を作ります。 Ubuntu用ドキュメントCentOS用ドキュメントMac用ドキュメントなどに従いインストールします。基本的にはDockerのテストを行うローカルにインストールと、CIを行うJenkinsサーバにインストールを行えば大丈夫でしょう。

次に、Dockerfileを整えていきます。

コツとしては、インストールに時間かかる処理をわけるという意味で、commonとinstalled_packages_for_jenkinsのような形で、コンテイナーをわけました。

こんな感じ。

#####
# common
#####
FROM ubuntu:14.04
MAINTAINER masuda-kenichi <masuda_kenichi@example.org>
ENV USER_NAME docker
ENV USER_PASSWORD docker
# serverspecインストールに必要なパッケージインストール
RUN apt-get install -y sudo passwd openssh-server ruby git 

RUN gem install serverspec rake

※ 本来はサービスで使っているAMIと同じベースイメージを元に作る必要がある。

次に時間がかかる処理をDockerfile化。

#####
# installed_packages_for_jenkins
#####
FROM common:1.2
MAINTAINER masuda-kenichi <masuda_kenichi@example.org>

# インストールに時間かかるものをインストール
RUN apt-get install -y openjdk-7-jdk unzip
RUN wget -qO- https://get.docker.com/ | sh

jdk, Docker等々のインストールを行うとそれだけで10分近くかかってしまい、毎回テストする際にかなりの時間がかかってしまいます。

そのため、docker build時に時間がかかりそうなものは先にいれてしまえということで、まずcommonを作り、そこからinstalled_packages_for_jenkinsを作るようにしました。

本来であれば、jdkのインストール等はプロビジョニングツールでやらなければいけないので、 テストするコンテイナーに事前に組み込んでしまうと、本当のテストにはならないのですが、仕方がなくそうした感じです。

そして、プロビジョニング・serverspec用。

#####
# Jenkins
#####
FROM installed_packages_for_jenkins:1.2
MAINTAINER masuda-kenichi <masuda_kenichi@example.org>

# Ansible実行
UN service ssh start && \
    cd /var/tmp/ansible/ && \
    git checkout master && \
    pip install -r requirements.txt && \
    ansible-playbook -i inventories/local jenkins.yml --limit jenkins --vault-password-file /var/tmp/vault_pass

# serverspec実行
RUN cd /var/tmp/serverspec/ && rake spec:common

とりあえず、これで必要なコンテイナーを作っていけるようになります。

プルリクビルド

次にリモートレポジトリにプルリクがあったら、ジョブが走るようにします。

ジョブには以下に従って、設定しておきましょう。

などを参考にしながら設定をすると、プルリクがあった時点でジョブが動き始めます。たとえば、sample_project_ansibleレポジトリにプルリクがあったら、ジョブを実行させるようにします。

また、ジョブには、Execute Shellで以下のような設定をして、プルリクを踏まえたジョブを実行させるようにします。

コツとしてはDockerfileに書かれた

git checkout master

というところを、

git checkout ${GIT_BRANCH}

という形でsedで置き換えることで、プルリクがあったブランチをプロビジョニングすることができます。 たとえば、sample_project_ansibleというレポジトリにプルリクがあったら、ここで設定したジョブが実行されますが、最初の設定のままではDockerコンテイナー内にmasterブランチのデータをプロビジョニングをしてしまいます。プロビジョニングしたいのはトピックブランチのものなので、sedで置き換える必要があるのです。

ジョブに設定された一連の処理はこんな感じ。

rm -rf docker
git clone repo.org:hoge/docker.git

cd docker/${ROLE}
sed -i "s#git checkout master#git checkout ${GIT_BRANCH}#g" Dockerfile
sudo time docker build --no-cache --rm -t hoge/${ROLE}:1.0 .

これでプロビジョニングとserverspecが実行されるようになります。あとはhipchatなりslackに通知すれば、設定は完了です。

現状Dockerfile内でserverspecを実行していますが、この辺もJenkinsのほうでdocker runとかで設定できるとよりよいかもしれません。

プロビジョニングツール

ここまでできたら、プロビジョニングツールでコード化します。ブランチきって開発をします。

git checkout -b fix/masuda/add_hogehoge_package
vim hogehoge
git add hogehoge
git commit
git push fix/masuda/add_hogehoge_package

おそらくこんな感じでリモートにpushするでしょう。 ブランチ名は「fix/masuda/add_hogehoge_package」です。

この「fix/masuda/add_hogehoge_package」のデータをDockerコンテイナーにプロビジョニングしたいので、Jenkinsのジョブでsedしていたわけです。

終わりに

環境を作って間もないため、まだどれほど問題が出るか把握はできていないですが、とりあえず動く環境までは持って行くことができました。

ただ、今回はテスト対象となるサービスが、仮想サーバが前提のプロジェクトだったので、そこまで問題にはなっていないですが、物理であると、かなり設定に差が出てきてしまうため、もう少し不確実なテストになることが想定されそうです。

まだまだ問題はあると思いますが、まだ僕が把握できていない問題もあると思いますので、是非是非ご意見頂ければと思います。

ではでは!