catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

DockerでJenkinsとテスト用のコンテナを動作させる

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です。

あとは

  1. jenkinsからdocker-engineにssh
  2. doker-engine上でdocker-composeしてコンテナを起動する
  3. 必要なテストをする
  4. 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環境が整ってきました。 これでサーバーサイドは好きなようにテスト流せますし、複数台の場合のオーケストレーションテストも実施できそうです。