CircleCI + Docker で Infrastructure CI (Chef + Serverspec) 環境を構築した

近況

3月から DevOps 関連の技術的負債の解消に取り組んでいて,動かなくなった Chef を直したり,秘伝のタレ(手動)で構築されたサーバ設定を Chef にリバースエンジニアリングしたり,Serverspec を導入して稼働中のサーバの差異を確認したりしている.

他にもウェブサーバのパフォーマンスチューニングをしたり,Zabbix / Kibana / CloudWatch で可視化したり,不要なアラートを消したりもした.あと Vagrant 環境を自動構築できるようにしたり,Packer を使って Vagrant Box を改善したり,デプロイ手順を正常化したり,テストの品質向上の目的で Capybara を導入したりもした.

最近はキャッシュサーバをリプレイスしたり,AWS のネットワーク構成を変更するなど,とにかく様々な施策を試しているけど,全然まだまだという感じで,圧倒的成長が求められている.

Chef をテストする

今までローカル環境で Chef をテストする場合,Vagrant + Sahara Plugin で適用とロールバックを繰り返して,Serverspec を手動で流していた.ちなみに Vagrant 1.8 から公式で vagrant snapshot コマンドが追加されたらしく,もしかしたら Sahara Plugin の役目は終わったのかも?(未検証)

$ vagrant -v
Vagrant 1.7.4
$ vagrant sandbox on
$ vagrant provision
$ vagrant sandbox rollback

Infrastructure CI

とは言え,モダンに Infrastructure CI をしたいなと思っていて,既に事例も多く出ている CircleCI + Docker を活用した環境を構築してみた.今回構築した構成図はザッとこんな感じ.基本的に CircleCI からコンテナに ssh をしたくないと考えていて,全てコンテナ内で完結するように工夫した.

f:id:kakku22:20160422175417p:plain

Dockerfile

まず,コンテナイメージに関しては CentOS 6.x で,既に Ruby 2.x が入っている tcnksm/centos-ruby をベースにした.今後カスタマイズしてもっと軽量なイメージに置き換えていきたいと思っている.

Chef を管理しているリポジトリをコンテナの /chef にコピーして,circleci ノードに対して Chef を実行している.上記の通り,コンテナ内で完結するため knife solo cook ではなく chef-solo にした.

今はまだ RUN を呼び出しまくっているため,1回に抑えてイメージサイズを軽量化した方が良さそうだけど,そもそも Chef を実行した後のゴミファイルが残ってしまっていたり,イメージサイズに関してはもっと抜本的な策が必要だと思う.今回は実際に稼働させるわけではなく CI 専用だから多少は甘く考えている.

FROM tcnksm/centos-ruby

ENV CHEF_HOME /chef

# Docker で実行するときに Serverspec の backend を切り替えるため
ENV IS_DOCKER 1

# リポジトリをコンテナに展開する
# `solo.rb` だけはコンテナ用に上書きする
ADD . ${CHEF_HOME}
ADD ./solo_circleci.rb ${CHEF_HOME}/solo.rb

# epel リポジトリを削除する
RUN rm -f /etc/yum.repos.d/epel.repo

# Chef をインストールして chef-solo を実行する
RUN curl -L https://www.opscode.com/chef/install.sh | bash
RUN cd ${CHEF_HOME} && bundle install
RUN cd ${CHEF_HOME} && /usr/bin/chef-solo -c ${CHEF_HOME}/solo.rb -j ${CHEF_HOME}/nodes/circleci.json

circle.yml

circle.yml はシンプルに書いた.事前に docker build をして,最後に docker run で Serverspec を実行している.

machine:
  services:
    - docker

dependencies:
  override:
    - docker info
    - docker build -t xxxxx .

test:
  override:
    - docker run -it xxxxx sh /chef/spec_scripts/spec.sh

spec_scripts/spec.sh

CircleCI から Serverspec を実行するスクリプトを呼び出している.ただし nginx を起動している点は微妙すぎると思っていて,コンテナは chkconfig を設定しても docker run で自動起動されるわけではないため,スクリプトで起動することにした.むむむむむー!微妙!

#!/bin/sh

service nginx start

cd ${CHEF_HOME}
bundle exec rake spec:localhost

spec/spec_helper.rb

Serverspec で使っている spec_helper.rb を一部修正した.具体的には backend の設定で,通常は ssh で実行しているけど,CircleCI ではコンテナ内で実行するため,Dockerfile で環境変数 IS_DOCKER を定義して,環境変数が存在する場合に exec に切り替えるようにしている.

set :backend, :ssh

# 通常の実行と CircleCI with Docker の実行で backend を切り替える
set :backend, :exec if ENV['IS_DOCKER']

実行結果

イイ感じ!

f:id:kakku22:20160422175444p:plain

巨大イメージ問題

実際に Dockerfile から docker build したイメージサイズが 3GB を超えていて,ポータブルなコンテナと言えないのでは?と思えるほどのデカさになっている.ローカル環境で docker build を何度も繰り返していたら Mac のディスク容量が無くなってワロタwww

まとめ

1日でサクッと Infrastructure CI を実践してみた.CircleCI の手軽さは驚きだし,プライベートリポジトリでも無料で使える点は最高だと思う.今回は実行できるようにすることに注力したため,まだまだ改善すべき点があるし,もはや Docker をうまく活用できてない気もするため,アドバイス求む!って感じではある.DevOps もアジャイルでリーンな開発をするべきだと思うし,今後も小さな成功を積み上げていこうと思う.

軽さは正義!(昨日 Alpine Linux Meetup Tokyo #1 に参加して意識高まってる)