Docker Meetup Tokyo #2でLTしてきた:「Docker+serverspecで作るconfigspec CI」 #dockerjp
はじめに
2014年4月11日(金)に開催されたDocker Meetup Tokyo #2で、「Docker + serverspecで作る configspec CI」というタイトルでLTしてきました。
この内容について、少し詳細に落とし込んだのがこの記事です。拙いところも改善するべきところもいっぱいあるんですが、とりあえず動いたよ、というところで。
イベント中のtweetはTogetterにまとめました。
やりたいこと
結果としてやりたいこと、出来上がるものがこちらの図になります。
configspecとserverspecをRakefileとしてまとめておき、githubのリポジトリに配置しておきます。そのリポジトリにgit pushされると、webhookによってJenkinsに通知が入り、DockerでContainerを立ち上げ、rakeコマンドを実行し、configspecでプロビジョニングし、serverspecでテストします。
やったこと
Amazon Linux Docker Image
まず、Amazon Linux EC2を普通にLaunchし、そのままStopします。そのEC2のEBS VolumeをDettach後、DockerがセットアップされたEC2にAttachし、mountします。
Docker Imageにするには不要な情報がいくつかありますので、削除しました。
- /var/log/* (全てのファイルを編集し0byteに)
- /var/cache/yum/*
- /home/ec2-user/.ssh/authorized_keys
その後、以下コマンドでimageとしてimportします。
# tar --numeric-owner -cjp . | docker import - local/amzn
AttachしたEBS Volumeはumountの上Dettachしておきます。
Amazon Linux Docker Imageをsshd接続可能なImageにする
上記で作成したImageは素の状態のAmazon Linuxなので、このImageを元にsshで接続可能なImageを作成します。
以下のようなDockerfileを作成します。またDockerfileと同じフォルダ内に、ssh接続で使用したい公開鍵をauthorized_keysという名前で配置しておきます。
FROM local/amzn # PAM設定を変えておかないとsshがいきなり切れる RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config RUN sed -ri 's/#UsePAM no/UsePAM no/g' /etc/ssh/sshd_config RUN passwd -f -u ec2-user # start-stopしてhost_keyを作る RUN service sshd start RUN service sshd stop ADD ./authorized_keys /home/ec2-user/.ssh/authorized_keys EXPOSE 22
このDockerfileを使って、Imageをbuildします。
docker build -t local/amznssh .
spec-ciが動くようにする
今回、configspecとserverspecをまとめて動かすようにしたものは、smokeymonkey/spec-ciに置いてあります。
これを動かすためには以下のgemを入れておく必要があります。
$ sudo gem install rspec rake configspec serverspec docker-api
また、Dockerの実行はroot権限が必要なため、spec-ciのrakeの実行もrootで行います。/root/.ssh/configにSSH接続のための設定を書いておきます。ポート番号は、本当はDockerが動的にマッピングしたポートフォワードされるポート番号を使えば良いんだと思うんですが、うまくいかずに固定化してしまいました。
# vi /root/.ssh/config Host docker HostName 172.17.42.1 Port 54322 User ec2-user IdentityFile ~/.ssh/mykey.pem StrictHostKeyChecking no UserKnownHostsFile /dev/null
Rakefileの内容
Rakefileは以下のような内容になっています。
まず構成としてはserverspec のテストをホスト間で共有する方法 - Gosuke Miyashitaを参考にさせて頂いており、最初にhosts定義を作って、hosts毎にロールを指定する形にしています。そしてその中で使用するコンテナイメージも指定しています。
require 'rake' require 'rspec/core/rake_task' require 'docker' hosts = [ { :name => 'docker', :roles => %w( web ), :image => 'local/amznssh' } ]
このhosts定義をeachでループしている部分で、まず最初にdocker-apiを使ってコンテナを起動しています。
namespace :spec do task :all => hosts.map {|h| 'spec:' + h[:short_name] } hosts.each do |host| container = Docker::Container.create( 'Cmd' => ['/usr/sbin/sshd', '-D'], 'Image' => host[:image], 'PortSpecs' => '22' ) container.start( 'PortBindings' => { '22/tcp' => [ {'HostIp' => '0.0.0.0'}, {'HostPort' => '54322'} ] } ) sleep 1
その後にconfigspecを実行し、次にserverspecを実行し、最後にコンテナをstopしています。
desc "Run spec to #{host[:name]}" ENV['TARGET_HOST'] = host[:name] # configspec RSpec::Core::RakeTask.new(host[:short_name].to_sym) do |t| t.pattern = 'spec/{' + host[:roles].join(',') + '}_config/*_spec.rb' end # serverspec RSpec::Core::RakeTask.new(host[:short_name].to_sym) do |t| t.pattern = 'spec/{' + host[:roles].join(',') + '}_test/*_spec.rb' end container.stop
rakeしてみる
Rakefileが置いてあるフォルダでrakeを実行すると以下のような出力がされます。
$ sudo /usr/local/bin/rake /usr/bin/ruby2.0 -S rspec spec/web_config/nginx_spec.rb . Finished in 0.52104 seconds 1 example, 0 failures /usr/bin/ruby2.0 -S rspec spec/web_test/nginx_spec.rb . Finished in 0.09493 seconds 1 example, 0 failures
Jenkinsのセットアップ
Jenkinsをインストールします。今回はパッケージとしてインストールしました。
$ sudo -s # wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo # rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key # yum install jenkins # chkconfig jenkins on # service jenkins start
インストール後、http://EIP:8080/にアクセスしてJenkinsに接続します。このままだと何の認証も入らないので、[グローバルセキュリティの設定]でユーザ認証されるように設定変更します。
また、Jenkinsの実行ユーザ:jenkinsからgithubに接続出来るようにしておく必要があります。秘密鍵ファイルを/var/lib/jenkins/.ssh/に配置し、公開鍵をgithubリポジトリに登録し、git configを実行しました。
また、ユーザjenkinsからsudoを使えるようにしておく設定しておきます。
# visudo jenkins ALL=(ALL) NOPASSWD:ALL Defaults:jenkins !requiretty
Jenkinsとgithubの連携ではGit Pluginを使います。[プラグインの管理]から「Git Plugin」をインストールしておきます。
Githubのリポジトリを使ったジョブを作成します。git pluginを使って、ソースコード管理で[Git]を洗濯し、リポジトリのURLを登録します。
ビルド手順は[シェルの実行]として、以下1行を入れておくだけです。
github側の設定
github側のwebhooksの設定を行い、git pushがあった場合にJenkinsをキックするよう設定しておきます。設定場所は[Setting]-[Webhooks & Services]です。
実行
specファイルを追加したり編集したりして、git pushすると、GithubからJenkinsがキックされてビルドが実行されます。成功した場合はこんな感じです。
失敗した場合はこんな感じ。
最終的に完成したら、Amazon Linux EC2に実行してやれば、プロビジョニングとそのテストが完了です!
まとめ
今回やった事はただの実験ですが、Amazon Linux AMI 2014.03によってDockerが簡単に使えるようになった今、コンテナベースデプロイでアプリケーションを開発するというのが一般的になる未来はそう遠くないのかも知れません。とりあえずやってて楽しかったです。 僕の業務上ではDockerを触ることはほぼ無いのですが、今後も色々と試していきたいと思います!