DockerでCI/CD環境を構築してみます。かたりぃなです。
どうやってやるか?
今回はCI実行環境やアーキテクチャについて考えます。 まずCIといえばJenkinsやTravisとかいろいろありますが、次の機能さえ備えていれば良いと思います。
- 繰り返しテストを再現可能な状態で実行できること(CI)
- 任意のトリガでデプロイできる(CD)
というわけで、使い慣れているJenkinsでいきます。
Jenkins環境はプラグインとか入れ始めると汚れてくるので、立て直ししやすいようDockerコンテナとして実行します。
JenkinsコンテナからホストOSに接続して、ホスト上からテストコンテナを立ち上げてテスト実行するということをやってみます。
これができると何が良いのか?
主に次の点でメリットがあると考えます。
- テスト環境を常にクリーンな状態に保てる
- jenkins, テスト対象コンテナともに
- ローカルマシンでCIできる
- CDもできるはず
- コンテナのオーケストレーションのテストもできる
環境
環境ですが、Hyper-Vの有無で少々環境が異なります。 Windows10側は少々手こずっていて納得いっていない状態ですが、一旦良しとします。
Windows10 + Hyper-V
この環境ではdocker-desktopを稼働させます。 https://www.docker.com/products/docker-desktop ホストOSはHypser-V上のMobyLinuxとなるのですが、こいつへの接続がややこしいので、WindowsでOpenSSHを動かしてここからMobyLinuxのdockerを操作することにします。
いつの頃からかWindows10でもssh-serverが使えるようになっているので、設定からインストールして、設定しておきます。 Windows10のSSHのユーザーとパスワードはWindowsログイン時のものそのままです。
Windows7 + VirtualBox
こちらはいつもの環境です。 https://docs.docker.com/toolbox/toolbox_install_windows/ コンテナからホストへはNATのIPで接続できるので簡単でした。
詳細
ここからはdocker-toolboxでの説明です。
jenkinsに入って他の仮想マシンを立てるところまでやってみます。
Jenkinsコンテナを立てる
ssh接続のパス入力を自動でやりたいので、追加でパッケージを入れておきます。 docker-toolboxのdocker-engine側にはdocker-composeが入っていないので、いつでも追加できるようにここで保持させることにしました。
dockerfileはこうなりました。
FROM jenkinsci/jenkins:2.11 # install via apt USER root # install jenkins plugins COPY plugins.txt /usr/share/jenkins/plugins.txt RUN /usr/local/bin/plugins.sh /usr/share/jenkins/plugins.txt # install docker-compose RUN curl -L https://github.com/docker/compose/releases/download/1.24.0-rc1/docker-compose-Linux-x86_64 > /usr/local/bin/docker-compose RUN chmod +x /usr/local/bin/docker-compose # install ssh tools RUN apt-get update RUN apt-get -y install sshpass # drop back to the regular jenkins user - good practice USER jenkins
このdocker-fileを使うdocker-composeはこうなりました。
version: "3" services: jenkins-CI: build: . image: jenkins ports: - "12380:8080" - "12322:22" volumes: - "./jenkins_home:/var/jenkins_home" - "./test_container:/var/test_container"
立ち上げます
docker-compose up -d
これでhttp://localhost:12380に接続すればjenkinsの初期パスワード入力画面が表示されます。 接続できない場合はHyper-Vのネットワーク設定、VirtualBoxならNAT設定あたりを見直します。
ちなみに、この設定だとdockerコンテナを再起動かけるとジョブが動かなくなってしまいます。 理由はvolumesでしているのはホスト側のパス(Windowsのファイルシステム)なので、パーミッションがおかしくなるためです。 対策は後述。(volumeを使う)
docker-containerへのログイン
まずdockerのお約束的にjenkinsコンテナに入ります。 初期パスワードがjenkinsのホームディレクトリ以下に置かれているのでそれを見るために必要です。
docker exec -it コンテナ名 /bin/bash
cat /var/jenkins_home/secrets/initialAdminPassword
これがjenkinsの初期パスワードです。
jenkinsコンテナから、テスト用のコンテナを立ち上げる準備
jenkinsでジョブ作れるところまできたので、 jenkinsからDockerコンテナを立ち上げる設定をします。 とはいえJenkins自体がコンテナで動作しているので、そのままではコンテナ in コンテナになってしまいます。
ここではJenkinsコンテナが動作している仮想マシン(docker-engine)に接続して、docker-engine側からコンテナを立ち上げてもらうことにします。
docker-engineに接続する
dockerが動作しているVM(デフォルト設定だとdefaultというマシン名)に接続するため、engine側のIPを調べます。
docker-containerからホスト(docker-engine)のIPを調べるのは、NATの対向IPなのでこうすればよいです。
ip route | awk 'NR==1 {print $3}'
つまりdocker-engine(ホスト)にSSHするには
ssh docker@上記で調べたIP
となります。
sshのパスワードはtcuserです。
sshのパスワード自動入力とフィンガープリントチェックの(Y/N)なしも含めるとSSHコマンドはこうなります。
machine_ip=`ip route | awk 'NR==1 {print $3}'` sshpass -p tcuser ssh -oStrictHostKeyChecking=no docker@${machine_ip}
あとはdocker-composeをすればよいです。 他のコンテナを起動するスクリプトを書くとこうなります。
このスクリプトの前提条件は2つです
- docker-engineにdoker-composeがインストールしてあって、パスも通っている
- /home/docker/test_containerにdocker-compose.ymlが置かれている
#!/bin/bash # docker-engineへの接続情報 machine_ip=`ip route | awk 'NR==1 {print $3}'` sshuser="docker" sshpass="tcuser" # ホスト上でのテストスクリプト置き場 compose_path="/home/docker/test_container" function docker_compose(){ sshpass -p ${sshpass}\ ssh -oStrictHostKeyChecking=no ${sshuser}@${machine_ip}\ "cd ${compose_path}; docker-compose ${1}" } case "$1" in start) docker_compose "up -d" echo "Start" ;; stop) docker_compose "down" echo "Stop" ;; *) echo $"Usage: {start|stop}" esac
docker-engineにdocker-composeをインストールする
docker-engineとは、ざっくりいうとVirtualboxから見えるマシンに相当します。 このマシンにはdocker-composeが入っていないのでインストールする必要があります。
上記jenkinsのdockerfileで持ってくるようにしたので、jenkins起動時にscpで転送してもよさそうです。
念のためインストール手順をメモします。
ここを確認する
https://github.com/docker/compose/releases
ダウンロードURLをクリップボードにコピーしておきます。
docker-engine上でrootに昇格
ダウンロードしたファイルは/usr/local/binに実行ファイルを配置したいのでrootに昇格させておきます。
sudo -i
rootになった状態で次のコマンドを打ちます。
$ curl -L https://github.com/docker/compose/releases/download/1.24.0-rc1/docker-compose-Linux-x86_64 > /usr/local/bin/docker-compose $ chmod +x /usr/local/bin/docker-compose
これでDocker用の仮想マシン上でdocker-composeコマンドが使えるようになりました。
動作確認
$ docker-compose
使い方のメッセージが出てきたらOKです。
あとは
- jenkinsからdocker-engineにssh
- doker-engine上でdocker-composeしてコンテナを起動する
- 必要なテストをする
- docker-compose downして環境をきれいにする
というジョブを書くことでいつでもきれいな環境でテストが実行できます。
2回目以降Jenkinsが立ち上がらない対策
最初に書いたJenkinsのdocker-compose.ymlでは二回目以降docker-compose up -d
するとジョブが流せなくなります。
これはWindows側にJenkinsの各種ファイルを保存するので、パーミッションがおかしくなるためです。
具体的にはジョブの実行結果が書けないとか、ジョブ設定ファイルが読めないとか。
対策として、dockerにはデータを保存するボリュームという機能があるので、これを使うことになります。
データボリュームコンテナというのもあったらしいですが、現在はオススメじゃないらしいので今回はボリュームを使います。
データボリュームを使うdocker-compose.yml
これだけです。
version: "3" services: jenkins-CI: build: . image: jenkins ports: - "12380:8080" - "12322:22" volumes: - jenkins_data:/var/jenkins_home - "./test_container:/var/test_container" # 上記コンテナで使用するデータボリュームを指定 volumes: jenkins_data:
ポイントはservicesと同じトップレベルにあるvolumesで、docker-compose up -dすると自動的に作成されます。 名前を付けてボリュームが作られることから、名前付きボリュームというとのこと。
コンテナのvolumes指定ではこの名前を指定してマウントします。
ボリュームってどこにあるの?
上記設定でボリュームにデータが保存されます。
テストのログなんかはとっておきたいので、念のためたどこに置かれているか確認する必要があります。
ボリュームの場所は次のように確認します。
$ docker volume ls local jenkins-ci_jenkins_data $ docker volume inspect jenkins-ci_jenkins_data [ { "CreatedAt": "2019-02-05T23:58:11Z", "Driver": "local", "Labels": { "com.docker.compose.project": "jenkins-ci", "com.docker.compose.version": "1.24.0-rc1", "com.docker.compose.volume": "jenkins_data" }, "Mountpoint": "/mnt/sda1/var/lib/docker/volumes/jenkins-ci_jenkins_data/_data", "Name": "jenkins-ci_jenkins_data", "Options": {}, "Scope": "local" } ]
どうやら /mnt/sda1/var/lib/docker/volumes/jenkins-ci_jenkins_data/_data
にあるらしいですね。
rootじゃないと読み書きできないので、sudo -i
してから確認してみましょう。
前述のパスワードファイルとかよさそうです。
ls /mnt/sda1/var/lib/docker/volumes/jenkins-ci_jenkins_data/_data/ config.xml jobs secrets copy_reference_file.log logs updates hudson.model.UpdateCenter.xml nodeMonitors.xml userContent hudson.plugins.git.GitTool.xml nodes users identity.key.enc plugins war init.groovy.d secret.key workflow-libs jenkins.install.UpgradeWizard.state secret.key.not-so-secret
jenkinsのファイルが一通り入ってそうですね。
これでパスワードファイルも読めます。
cat /mnt/sda1/var/lib/docker/volumes/jenkins-ci_jenkins_data/_data/secrets/initialAdminPassword
最後に、あらかじめ以下ボリュームを作成しておくこともできるようです。$ docker volume create --name jenkins_data
感想と今後の展望
CI/CD環境が整ってきました。 これでサーバーサイドは好きなようにテスト流せますし、複数台の場合のオーケストレーションテストも実施できそうです。