はじめまして!今年1月からジョインしましたtjinjinです。feedforceではアニメインフラを担当しています。ちなみに今季オススメアニメはSHIROBAKOです。

今回サーバCIにDockerを導入しましたので、実際の設定や工夫した点など投稿したいと思います。

feedforceのサーバCI

弊社ではこれまでCircleCIまたはJenkinsとAWSを組み合わせてサーバのCIを行って参りました。
参考: JenkinsでサーバのCIを始めました

しかし、CircleCIを利用するプロジェクトが増えるにつれ、CircleCIに実行待ちが発生するようになりました。CircleCIの実行ログを確認したところ、AWSのセットアップに時間がかかっていることがわかったので、環境のセットアップ時間を短縮できるもののはないかと探していました。そんな折、Dockerがいいのではという話を耳にしました。Docker はDocker社が開発しているコンテナ型の仮想化ソフトウェアで、ゲスト型のVMより軽量であり、起動が速いなどの特徴があります。そこで、今回サーバCIにDockerが使えそうか検証しました。

Dockerの設定

Dockerfileを準備する

DockerfileとはオリジナルのDocker imageを作成するための設定ファイルのようなものです。この設定に従いDocker imageが作成されます。主にやっていることとして3点あります。

  • 公式のDocker imageを元に既存で使っていたイメージと比較を行い、足りないパッケージを事前にインストール(ここは今後改善したい)
  • Dockerfileの中でChefを事前にインストールさせる
  • SSHログイン用するための設定

実際の設定は下記のようになります。(一部修正しています。)

FROM centos:6.6
MAINTAINER FF
RUN yum update -y
RUN yum -y install openssh-server sudo acl attr audit authconfig bzip2 cloog-ppl cronie-anacron \
(中略)
yum clean all && \

## Create user
    useradd docker && \
    passwd -f -u docker && \

## Set up SSH
    mkdir -p /home/docker/.ssh; chown docker /home/docker/.ssh; chmod 700 /home/docker/.ssh && \
echo "<public_key>" > /home/docker/.ssh/authorized_keys && \

    chown docker /home/docker/.ssh/authorized_keys && \
    chmod 600 /home/docker/.ssh/authorized_keys && \

## setup sudoers
    echo "docker ALL=(ALL) ALL" >> /etc/sudoers.d/docker && \

## Set up SSHD config
    sed -ri 's/#PermitRootLogin yes/PermitRootLogin yes/g' /etc/ssh/sshd_config && \
    sed -ri 's/#UsePAM no/UsePAM no/g' /etc/ssh/sshd_config && \
    sed -ri 's/^UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config && \

## Pam認証が有効でもログインするための設定
    sed -i -e 's/^\(session.*pam_loginuid.so\)/#\1/g' /etc/pam.d/sshd && \

## Chef-soloを事前にインストールする
    curl -L https://www.chef.io/chef/install.sh | bash && \

## Init SSHD
    /etc/init.d/sshd start && \
    /etc/init.d/sshd stop

CMD /usr/sbin/sshd -D

&& \という形でRUNの数をなるべく減らしています。Dockerはinstruction(RUNやCMDなど)毎にイメージを差分コミットしているため、例えばyumパッケージ毎にinstructionを書いてしまうとその分だけimageが分割されます。imageが増えるほど余分な情報が増え、結果的にサイズが大きくなってしまいます。少し強引なやり方にはなりますが、ひとつのコマンドの続きとすることでimageをまとめ、サイズを抑えることが可能です。

CircleCIのコンテナからDockerコンテナに対してsshできるようにする

弊社では現在Chef-soloとserverspecを使って構成管理しているため、コンテナに対しSSHで接続する必要があります。そこで、Docker起動時にポートを指定しポートフォワーディングさせています。~/.ssh/configでそのポートに対しsshするように設定することで、接続が可能になります。

Host test
  HostName 127.0.0.1
  User docker
  Port 40122
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile ~/.ssh/<secret_key_path>
  IdentitiesOnly yes
  LogLevel FATAL

CircleCIの設定

秘密鍵はCircleCI上に事前に設定しています。

circle.ymlについては以下のように設定しています。

machine:
  timezone:
    Asia/Tokyo
  services:
    - docker
checkout:
  post:
    - git submodule sync
    - git submodule update --init
dependencies:
  cache_directories:
    - "~/cache"
  pre:
    - cp ./.docker/ssh-config.circleci ~/.ssh/config
    - ./script/docker-build.sh
    - docker run -d --privileged -p 40122:22 docker/centos-sshd; sleep 2
test:
  pre:
    - bundle exec knife solo cook test:
        timeout: 600
  override:
    - bundle exec rake spec:test:
        timeout: 600

docker runの際には--privileged オプションをつけています。hostnameの変更をするレシピがあり、--privilegedオプションを付けないと、hostnameの変更がうまくいかなかったためです。

Docker imageのキャッシュ方法

docker-build.shというスクリプトの中ではキャッシュの有無によって、処理を分岐しています。通常時はキャッシュされたファイルをロードし、Dockerfileに変更がある場合のみビルドをさせることで処理の最適化をしています。

#!/bin/sh
set -xe

if [ -e ~/cache/centos-sshd.tar ] && [ $(md5sum Dockerfile | cut -d' ' -f1) = $(cat ~/cache/dockerfile.digest) ]
then
  docker load < ~/cache/centos-sshd.tar
else
  mkdir -p ~/cache
  docker build -t docker/centos-sshd .
  md5sum Dockerfile | cut -d' ' -f1 > ~/cache/dockerfile.digest
  docker save docker/centos-sshd > ~/cache/centos-sshd.tar
fi

実施した結果

結果としては下記のようになりました。

logo-title
CircleCI のカテゴリに基づいてグラフにしています

当初の想定ではdependencies(環境のセットアップをする部分)が短縮できる想定でした。cacheが効いていない赤いグラフでも処理時間が半減していますが、更にcacheを効かせることで約5分の1まで時間を短縮できました。また、想定外の結果でしたが、test工程(chef & serverspec)の時間が圧倒的に短くなりました。AWSはm3.mediumを使っていますが、CircleCIの方が圧倒的に速いみたいですね。全体として10分以上の短縮ができ、やる意味はあったかと思います。

最後に

今回は、DockerHub上においてあるimageを使いましたが、セキュリティ上の問題も考えると管理しておきたいですね。一度共通的なDocker imageを作ってしまえば、ビルドする手間が省けるのでさらなる高速化ができそうです。

  • このエントリーをはてなブックマークに追加
Feedforce Developer Blog

新しいブログへ移行しました!こちらもよろしくお願いします。