133 Views
February 12, 14
スライド概要
2014/02/12 Docker Meetup in Tokyo #1 での発表内容です。
デモコード: https://github.com/ydnjp/docker-continuous-integration-workflow
2023年10月からSpeaker Deckに移行しました。最新情報はこちらをご覧ください。 https://speakerdeck.com/lycorptech_jp
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 ワークフロー 登場人物編 ! ビルド -> テスト -> プッシュの自動化
## 登場人物 Jenkins Docker レジストリ # $ Git リポジトリ 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
## Dockerfile の配置 ├──Gemfile dockerfiles/<image-name> で ├──Gemfile.lock Dockerfile を配置する ├──Rakefile ├──dockerfiles │ ├──base │ │ ├──Dockerfile │ │ └──keys │ │ ├──id_rsa │ │ └──id_rsa.pub base イメージ │ └──jenkins │ ├──Dockerfile │ └──start-jenkins.sh └──spec ├──base jenkins イメージ │ └──sshd_spec.rb ├──jenkins │ └──jenkins_spec.rb └──spec_helper.rb
## spec の配置 ├──Gemfile spec/<image-name> で ├──Gemfile.lock ├──Rakefile スペックを配置する ├──dockerfiles │ ├──base │ │ ├──Dockerfile │ │ └──keys │ │ ├──id_rsa │ │ └──id_rsa.pub │ └──jenkins │ ├──Dockerfile │ └──start-jenkins.sh dockerfiles 以下に └──spec ├──base 対応する形です │ └──sshd_spec.rb ├──jenkins │ └──jenkins_spec.rb └──spec_helper.rb
serverspec は ssh を通して コンテナとやりとりするので sshd 入りのベースとする イメージを用意します (base イメージ) ├──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 ssh ログインに使う ノーパスキーです
dockerfiles/base/Dockerfile
FROM ubuntu:13.10
!
ENV DEBIAN_FRONTEND noninteractive
!
RUN apt-get -q update && apt-get -y upgrade
!
# Install openssh-server for serverspec
sshd をインストール
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
## ベースイメージのビルドとコンテナの起動 % 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. 利用したコンテナを殺して削除する
spec/spec_helper.rb (前編)
c.before :all do
(略)…
host = File.basename(Pathname.new(file).dirname)
!
if c.host != host
Docker イメージごとにコンテナを起動
## Start container and retrieve port number of sshd
container = Docker::Container.create(
docker-api 使ってます
:Image => "#{host}",
:Entrypoint => ['/usr/sbin/sshd'],
:Cmd => ['-D'],
sshd を起動させる
:ExposedPorts => {'22/tcp' => {}},
:User => 'root'
).start(
:PortBindings => {
コンテナの22番ポートを
親のホストに bind
'22/tcp' => [{:HostIp =>‘127.0.0.1’}]
}
)
使ったコンテナはあとで
sleep 1
停止、削除するのでとっておく
containers << container
spec/spec_helper.rb (後編)
c.ssh.close if c.ssh
base イメージに追加した公開鍵と
c.host = host
対の秘密鍵を使う
options = {
:keys => [File.expand_path('../../dockerfiles/base/keys/id_rsa',
__FILE__)],
:port => container.json['HostConfig']['PortBindings']['22/tcp'][0]
['HostPort']
HostConfig から bind されたポートを
}
取得する
c.ssh = Net::SSH.start('0.0.0.0', 'root', options)
end
接続先はローカルホストで root ユーザ
end
!
c.after(:suite) do
利用したコンテナを
## Kill and delete containers
殺して削除する
containers.each {|container| container.kill.delete }
end
spec/base/sshd_spec.rb 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 の記述については 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 イメージを作ります (ここからは早送り
## 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 Dockerfile はここ spec はこちら
dockerfiles/jenkins/Dockerfile FROM base base をベースとするので、sshd 入り ! RUN apt-get -q -y install openjdk-7-jre-headless && apt-get clean ! # Install Jenkins jenkins のセットアップ(略) RUN mkdir /opt/jenkins … FROM base としているので、そのままで sshd を起動したコンテナを起動させることができます ! あとは spec を実装したら完了です (spec を先に実装することで TDD もできちゃう) spec の実装は省略します
# docker build, push の Rake タスク化 ! 繰り返し行うことはタスクにまとめましょう
## docker build イメージが base に依存しているので、 base から順番にビルドする % bundle exec rake docker:build docker build -t base dockerfiles/base (略)... ## docker push docker tag でプライベートレジストリの情報を付けたのち、 順番に push する % bundle exec rake docker:push docker tag base 0.0.0.0:5000/base docker push 0.0.0.0:5000/base (略)... 実装はデモコードの Rakefile を参照してください!
# CI ワークフロー ジョブスクリプト編 ! ビルド -> テスト -> プッシュの自動化
## ジョブスクリプト ベースは出来ているのでこれだけです。
デモ
## まとめ - ビルド、テスト、プッシュの自動化ができました - 毎日自動でまわすことで常に最新のセキュリティ アップデートを当て続けることもできます ! 次は継続的デリバリーに向けて?
# デモコードあります (再掲) https://github.com/ydnjp/ docker-continuous-integration-workflow
ありがとうございました