Ansible などのプロビジョニングスクリプトを同じホストに何度も適用していると、そのスクリプトが正しく動くのか怪しくなってきます。
新規環境で流すと実は動かなかったりなんて。
この記事ではコンテナを使って高速に環境を再作成することで、いつも正しく動くプロビジョニングスクリプトを記述する方法を紹介します。
Vagrant 用が生成するssh_configを Ansible に渡すので、Ansible の inventory ファイルを編集せずにデプロイ先をコンテナに切り替えることができます。
Vagrantfileおよび Ansible playbooks は GitHub で公開しています。
この記事では、例として Ansible で Elasticsearch クラスタを構築します。
また高速化のために apt キャッシュサーバも配置します。
Vagrantfileによって以下のホストを作成します。
apt-cacher: apt キャッシュサーバelasticsearch-clientElasticsearch のクライアントノードで、クラスタのエンドポイントとなるelasticsearch-master-{n}Elasticsearch のマスターノードelasticsearch-data-{n}Elasticsearch のデータノード
apt-cacherの構築は Ansible の対象ではないので、Dockerfileで環境を作ります。
各 Elasticsearch ノードは、SSH ができる環境をDockerfileで作ります。
コンテナを定義する
ますはじめに、Docker のEmbedded DNS serverを使うために、ネットワークを定義します。
docker network create elasticsearchこのネットワークに接続したコンテナは、コンテナ名で他のコンテナの名前解決できるようになります。
Vagrantfile を記述
Vagrantfile の全貌は以下のとおりです。
# Vagrantfile
def vanilla_container(config, name, &proc)
config.vm.define name do |node|
node.vm.provider "docker" do |docker|
docker.name = name
docker.create_args = ["--hostname=#{name}", "--network=elasticsearch"]
docker.build_dir = "vanilla"
docker.has_ssh = true
proc.call(docker) if block_given?
end
end
end
Vagrant.configure("2") do |config|
config.ssh.username = "vagrant"
config.ssh.password = "vagrant"
config.vm.define "apt-cacher" do |node|
node.vm.provider "docker" do |docker|
docker.name = "apt-cacher"
docker.create_args = ["--hostname=apt-cacher", "--network=elasticsearch"]
docker.build_dir = "apt-cacher"
end
end
(1..4).each do |i|
vanilla_container config, "elasticsearch-master-#{i}"
end
(1..6).each do |i|
vanilla_container config, "elasticsearch-data-#{i}"
end
vanilla_container config, "elasticsearch-client" do |docker|
docker.expose = [9200]
end
endさきほど作成したネットワークにコンテナを接続するために、docker.create_argsに--network=elasticsearchを指定します。
そしてコンテナ名をdocker.nameで指定して、ホスト名をdocker.create_argsに--hostname=apt-cacherを追加することで設定します。
今回は Elasticsearch ノードのコンテナをヘルパメソッドでガッと定義します。
エンドポイントとなるelasticsearch-clientノードは、ポート 9200 を expose します。
vanillaコンテナの定義
vanillaの Dockerfile は、実際の Ansible のターゲットホストに近い状態を作るため、必要最低限の環境を構築します。
sudo や ssh サーバの設定については、Vagrant Docker provider で SSH ができるまでをどうぞ。
またapt-cacherを apt のキャッシュサーバとしてプロキシに設定します。
# vanilla/Dockerfile
FROM ubuntu:16.04
RUN apt update && apt install -y --no-install-recommends \
openssh-server \
sudo \
ca-certificates \
apt-transport-https \
python \
curl
# vagrantユーザを追加
RUN useradd --create-home --user-group vagrant && \
echo -n 'vagrant:vagrant' | chpasswd && \
echo 'vagrant ALL=NOPASSWD: ALL' >/etc/sudoers.d/vagrant
# apt-cacherをプロキシに設定
RUN echo 'Acquire::http::Proxy "http://apt-cacher:3142/";' >/etc/apt/apt.conf.d/02proxy
RUN mkdir -p /var/run/sshd
CMD /usr/sbin/sshd -Dapt-cacherコンテナの定義
apt-cacherコンテナは、apt キャッシュのみを行うので、ssh サーバや sudo すら必要ありません。
CMDでapt-cacher-ngを起動する、シンプルなコンテナです。
# apt-cacher/Dockerfile
FROM ubuntu:16.04
RUN apt update && apt install -y --no-install-recommends \
ca-certificates \
apt-cacher-ng
VOLUME "/var/cache/apt-cacher-ng"
RUN mkdir -p /var/run/apt-cacher-ng
CMD /usr/sbin/apt-cacher-ng -c /etc/apt-cacher-ng foreground=1コンテナを起動する
この状態でコンテナを立ち上げてみましょう
vagrant upvagrant sshで各ホストにログインできるので、他のホストの名前が引けるか、apt-cacher が正常に動作しているかを確認してみましょう。
vagrant ssh elasticsearch-client -- getent hosts elasticsearch-master-1
vagrant ssh elasticsearch-client -- sudo apt updateAnsible を流す
Ansible playbook は平凡な Elasticsearch クラスタを構築するのみです。 詳しくはをリポジトリを参照してください。
次に Ansible を流すために、ssh_configを作ります。
引数なしのvagrant ssh-configだと、apt-cacherの設定も作ろうとして失敗するため、apt-cacherを除いたホストを指定して ssh_config を作ります。
vagrant status | \
awk '/running/ { print $1 }' | \
grep -v 'apt-cacher' | \
xargs -n1 vagrant ssh-config >ssh_configそしてssh_configを Ansible に渡すために、ansible.cfgを作ります。
cat >ansible.cfg <<EOF
[ssh_connection]
ssh_args = -F ssh_config
EOFそして流します。
ansible-playbook --inventory-file=ansible/inventories/hosts --sudo ansible/site.yml出来上がったらクライアントノードからクラスタの状態を見てみましょう。number_of_nodesが 11、number_of_data_nodesが 6 となっていれば正常にクラスタが作成できています。
client_ip=$(docker inspect elasticsearch-client | \
jq -r '.[].NetworkSettings.Networks.elasticsearch.IPAddress')
curl http://${client_ip}:9200/_cluster/health | jq '.'