dockerと継続的インテグレーション
DESCRIPTION
2014/02/12 Docker Meetup in Tokyo #1 での発表内容です。 デモコード: https://github.com/ydnjp/docker-continuous-integration-workflowTRANSCRIPT
Docker と継続的インテグレーション
14/02/12 Docker Meetup in Tokyo #1
Kazuki Suda! @superbrothers
" github.com/superbrothers
Docker での ビルド -> テスト -> プッシュの自動化
# 本日のお話
# アジェンダ
- CI ワークフロー 構成編
- Dockerfile のテスト
- ベースとなるイメージの実装
- テストの準備と実行
- CI ワークフロー ジョブスクリプト編
- デモ
- まとめ
# デモコードあります
https://github.com/ydnjp/ docker-continuous-integration-workflow
# CI ワークフロー 登場人物編 !
ビルド -> テスト -> プッシュの自動化
## 登場人物
#
$Git リポジトリ
Jenkins
Docker 入りスレーブ
Docker レジストリ
## ワークフロー
ビルド テスト プッシュ
docker build serverspec docker push
# Dockerfile のテスト
├──Gemfile ├──Gemfile.lock ├──Rakefile ├──dockerfiles │ ├──base │ │ ├──Dockerfile │ │ └──keys │ │ ├──id_rsa │ │ └──id_rsa.pub │ └──jenkins │ ├──Dockerfile │ └──start-jenkins.sh └──spec ├──base │ └──sshd_spec.rb ├──jenkins │ └──jenkins_spec.rb └──spec_helper.rb
## ディレクトリ構造
├──Gemfile ├──Gemfile.lock ├──Rakefile ├──dockerfiles │ ├──base │ │ ├──Dockerfile │ │ └──keys │ │ ├──id_rsa │ │ └──id_rsa.pub │ └──jenkins │ ├──Dockerfile │ └──start-jenkins.sh └──spec ├──base │ └──sshd_spec.rb ├──jenkins │ └──jenkins_spec.rb └──spec_helper.rb
dockerfiles/<image-name> で Dockerfile を配置する
base イメージ
jenkins イメージ
## Dockerfile の配置
├──Gemfile ├──Gemfile.lock ├──Rakefile ├──dockerfiles │ ├──base │ │ ├──Dockerfile │ │ └──keys │ │ ├──id_rsa │ │ └──id_rsa.pub │ └──jenkins │ ├──Dockerfile │ └──start-jenkins.sh └──spec ├──base │ └──sshd_spec.rb ├──jenkins │ └──jenkins_spec.rb └──spec_helper.rb
spec/<image-name> で スペックを配置する
dockerfiles 以下に 対応する形です
## spec の配置
├──Gemfile ├──Gemfile.lock ├──Rakefile ├──dockerfiles │ ├──base │ │ ├──Dockerfile │ │ └──keys │ │ ├──id_rsa │ │ └──id_rsa.pub │ └──jenkins │ ├──Dockerfile │ └──start-jenkins.sh └──spec ├──base │ └──sshd_spec.rb ├──jenkins │ └──jenkins_spec.rb └──spec_helper.rb
serverspec は ssh を通して コンテナとやりとりするので
sshd 入りのベースとする イメージを用意します
(base イメージ)
ssh ログインに使う ノーパスキーです
FROM ubuntu:13.10 !ENV DEBIAN_FRONTEND noninteractive !RUN apt-get -q update && apt-get -y upgrade !# Install openssh-server for serverspec RUN apt-get -q -y install openssh-server && apt-get clean RUN mkdir /var/run/sshd RUN mkdir /root/.ssh && chmod 600 /root/.ssh ADD keys/id_rsa.pub /root/.ssh/authorized_keys RUN chown root:root /root/.ssh/authorized_keys !# Ubuntu 13.10 additional steps for SSHD Service RUN sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd RUN echo LANG="en_US.UTF-8" > /etc/default/locale
dockerfiles/base/Dockerfile
ノーパスの 公開鍵を追加
sshd をインストール
## ベースイメージのビルドとコンテナの起動
% docker build -t base dockerfiles/base !% docker run -d -p 22 base /usr/sbin/sshd -D
22番ポートが bind されたポートを指定して ノーパスキーの秘密鍵を使うとログインできる
- ノーパスキーを利用しよう
- root にパスワードを設定しちゃダメ!
## ベースイメージのポイント
# serverspec を使ったテストの準備と実行
## serverspec を使ったテスト実行の流れ
1. イメージからコンテナを起動する
- CMD /usr/sbin/sshd -D
- 22番ポートを bind する
2. bind されたポートに対して SSH を使って spec を流し込む
- ノーパスの秘密鍵を使います
- bind されたポートは inspect で取得
- 1 -> 2 を最後まで繰り返す
3. 利用したコンテナを殺して削除する
c.before :all do (略)… host = File.basename(Pathname.new(file).dirname) ! if c.host != host ## Start container and retrieve port number of sshd container = Docker::Container.create( :Image => "#{host}", :Entrypoint => ['/usr/sbin/sshd'], :Cmd => ['-D'], :ExposedPorts => {'22/tcp' => {}}, :User => 'root' ).start( :PortBindings => { '22/tcp' => [{:HostIp =>‘127.0.0.1’}] } ) sleep 1 containers << container
spec/spec_helper.rb (前編)
Docker イメージごとにコンテナを起動
sshd を起動させる
使ったコンテナはあとで 停止、削除するのでとっておく
コンテナの22番ポートを 親のホストに bind
docker-api 使ってます
c.ssh.close if c.ssh c.host = host options = { :keys => [File.expand_path('../../dockerfiles/base/keys/id_rsa', __FILE__)], :port => container.json['HostConfig']['PortBindings']['22/tcp'][0]['HostPort'] } c.ssh = Net::SSH.start('0.0.0.0', 'root', options) end end ! c.after(:suite) do ## Kill and delete containers containers.each {|container| container.kill.delete } end
spec/spec_helper.rb (後編)
base イメージに追加した公開鍵と 対の秘密鍵を使う
利用したコンテナを 殺して削除する
HostConfig から bind されたポートを 取得する
接続先はローカルホストで root ユーザ
require 'spec_helper' !describe package('openssh-server') do it { should be_installed } end !describe file('/var/run/sshd') do it { should be_directory } end !describe file('/root/.ssh') do it { should be_directory } it { should be_mode 600 } end
spec/base/sshd_spec.rb
ディレクトリが存在するか
パッケージが正しく インストールされているか
ディレクトリが存在するかパーミッションが正しいか
spec の記述については serverspec 公式の ドキュメントを確認してください
## テストの実行
$ bundle exec rake spec /usr/bin/ruby1.9.1 -S rspec spec/base/sshd_spec.rb ......... !Finished in 2.21 seconds 9 examples, 0 failures
ベースとなるイメージの実装が終わったので 本来必要なイメージを
実装する準備が整いました(長い)
例として jenkins イメージを作ります(ここからは早送り
├──Gemfile ├──Gemfile.lock ├──Rakefile ├──dockerfiles │ ├──base │ │ ├──Dockerfile │ │ └──keys │ │ ├──id_rsa │ │ └──id_rsa.pub │ └──jenkins │ ├──Dockerfile │ └──start-jenkins.sh └──spec ├──base │ └──sshd_spec.rb ├──jenkins │ └──jenkins_spec.rb └──spec_helper.rb
## jenkins イメージ
Dockerfile はここ
spec はこちら
FROM base !RUN apt-get -q -y install openjdk-7-jre-headless && apt-get clean !# Install Jenkins RUN mkdir /opt/jenkins …
dockerfiles/jenkins/Dockerfile
jenkins のセットアップ(略)
base をベースとするので、sshd 入り
spec の実装は省略します
FROM base としているので、そのままで sshd を起動したコンテナを起動させることができます
!あとは spec を実装したら完了です
(spec を先に実装することで TDD もできちゃう)
# docker build, push の Rake タスク化 !
繰り返し行うことはタスクにまとめましょう
## docker build
% bundle exec rake docker:builddocker build -t base dockerfiles/base (略)...
## docker push
イメージが base に依存しているので、 base から順番にビルドする
% bundle exec rake docker:push docker tag base 0.0.0.0:5000/base docker push 0.0.0.0:5000/base (略)...
docker tag でプライベートレジストリの情報を付けたのち、 順番に push する
実装はデモコードの Rakefile を参照してください!
# CI ワークフロー ジョブスクリプト編 !
ビルド -> テスト -> プッシュの自動化
## ジョブスクリプト
ベースは出来ているのでこれだけです。
デモ
## まとめ
- ビルド、テスト、プッシュの自動化ができました
- 毎日自動でまわすことで常に最新のセキュリティアップデートを当て続けることもできます
!
次は継続的デリバリーに向けて?
# デモコードあります (再掲)
https://github.com/ydnjp/ docker-continuous-integration-workflow
ありがとうございました