あいつの日誌β

働きながら旅しています。

Ansible での provisioning を継続的にインテグレーションしたいので circleci で Docker を使う

あらすじ

Provisioning を継続インテグレーションしたくなったのですが、そんな時は CircleCI で Docker を使えばいいんじゃないかと思いました。

コンテナ内で ServerSpec を実行する方法などがあると思いますが、ここではコンテナ内に SSHD を使って外部からプロビジョンします。

テストに関しては Serverspec などがあると思いますが、ここではシンプルに 外部から SSH でコマンドを実行して結果の確認をするだけにします。

ソースコード

ここで紹介しているソースコードは下記の置いてあります。

github.com

準備

mkdir circleci-docker-provisioning && cd $_
mkdir -p keys provisioning/inventory provisioning/roles/nodejs/tasks
touch .gitignore Makefile Dockerfile circle.yml 
touch provisioning/playbook.yml provisioning/inventory/docker provisioning/roles/nodejs/tasks/main.yml keys/.gitkeep

keys/* に公開鍵と秘密鍵を保存します。あくまで CI 用の鍵なので git で管理しても問題無いのですが、万が一これを見て秘密鍵github.com で公開している事を心配する人がいるかもしれないので一応このようにします。

create .gitignore

keys/*
!.gitkeep

とりあえず commit します。

git init && git add . && git commit -m "initial commit"

秘密鍵と公開鍵を作成

パスワード無しで作成します。keys/docker_id_rsakeys/docker_id_rsa.pub が生成されます。

ssh-keygen -P "" -f keys/docker_id_rsa

Dockerfile

通常は Docker コンテナーに SSHD を入れないのですがここでは SSHD でのプロビジョニングを行いたいのでこのようにしています。

Docker に ansible をマウントして Ansible を local connection してテストする方法もありますが今回はこのようにしています。

create Dockerfile

FROM ubuntu:16.04

RUN apt-get update && apt-get install -y sudo openssh-server python
RUN mkdir -p /var/run/sshd

RUN useradd -m -d /home/docker -s /bin/bash docker
RUN echo "docker:docker" | chpasswd
RUN mkdir /home/docker/.ssh
RUN chmod 700 /home/docker/.ssh
COPY ./keys/docker_id_rsa.pub /home/docker/.ssh/authorized_keys
RUN chmod 600 /home/docker/.ssh/authorized_keys
RUN chown -R docker:docker /home/docker/.ssh

RUN echo "docker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

Build

docker build -t provisioning_sshd .

Run

docker run -d -p 40122:22 --name test_sshd provisioning_sshd

ssh で接続確認をします。

ssh docker@localhost -p 40122 -i keys/docker_id_rsa 

Ansible

とりあえず Node.js をインストールしてみます。

create provisioning/inventory/docker

docker ansible_user=docker ansible_port=40122 ansible_host=localhost ansible_ssh_private_key_file=./keys/docker_id_rsa

create provisioning/roles/nodejs/tasks/main.yml

---
- name: install Node.js
  become: yes 
  apt: name={{ item }} state=installed
  with_items:
    - nodejs
    - npm

- name: nmp cache clean
  command: npm cache clean

- name: install n command
  become: yes 
  command: npm install n -g

- name: install node v6.10.0
  become: yes 
  command: n 6.10.0

create provisioning/playbook.yml

---
- hosts: docker
  become: true
  roles:
    - nodejs

provision 開始

ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker

動作確認

ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'
v6.10.0

これを以下のコマンドでテストします。status code を echo $? で確認します。 0であればOKです。

test `ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'` = "v6.10.0"
echo $? 

Makefile

以下のような Makefile を作っておきます。

docker.keygen:
    ssh-keygen -P "" -f keys/docker_id_rsa

docker.build:
    docker build -t provisioning_sshd .

docker.start:
    docker run -d -p 40122:22 --name test_sshd provisioning_sshd

docker.stop:
    ssh-keygen -R "[localhost]:40122"
    docker rm -f `docker ps -aq`

docker.provision:
    ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker

docker.provision.ci:
    ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker --private-key=./keys/docker_id_rsa

docker.test:
    test `ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'` = "v6.10.0"
    @echo "It looks like optimal"

CircleCI

CircleCI 上ではホストOS上で Docker を使います。Circle CI のホストOS上で Python と Ansible を install して、コンテナに対して SSH 接続を行います。

create circle.yml

machine:
  python:
    version: 2.7.10
  services:
    - docker

dependencies:
  pre:
    - pip install ansible
  override:
    - docker info
    - make docker.keygen
    - docker build -t provisioning_sshd .

test:
  override:
    - docker run -d -p 40122:22 provisioning_sshd
    - make docker.provision.ci
    - make docker.test

最終チェック

これらをコミットして実際に Circle Ci が動作する事を確認します。

まとめ

というわけで無事 CircleCI 上で Docker のコンテナに対して provisioning を行う事ができました。

サーバーサイドエンジニアがインフラエンジニアに代わってどれだけ作業すべきなのかは良く分からないのですが、インフラエンジニアにソースコード渡す場合はきちんと動作チェックをするのが礼儀だと思うのでこういうやり方があるという事は覚えておいたほうがいいなあと思いました。おしまい。