LoginSignup
7
7

More than 5 years have passed since last update.

Vagrantで複数の仮想マシンとansible_localを利用して構築する(ついでにansibleのhostsファイルも適用させる)

Posted at

やること

  • DBサーバとWEBサーバを別々のVMで起動させる
  • windows環境でansibleを実行できるようにする(ansible_local)
  • inventory配下のhostsファイルをyamlで書く
  • ansibleのinventory/hostsファイルを読み込んでローカル環境の設定を適用させる
  • playbookはもちろんBestPracticesを踏襲

環境

  • Host OS: Windows10 Pro
  • Guest OS: Centos6.7 x86_64
  • Ansible 2.5.2
  • Vagrant 1.9.8
  • VirtualBox 5.1.22

動機

Vagrantで複数のVMを立ち上げてansibleを導入しようとした時のことです。windows環境化でansibleを実行するには色々な方法がありますが、vagrantのansible_localが使えそうな雰囲気だったので実装してみましたが、公式の方法がしっくりこなかったので独自に実装してみた話です。

Ansible Parallel Execution

公式で並列処理時の方法があったのでこれを参考に書きました。

Vagrantfile
N = 3
(1..N).each do |machine_id|
  config.vm.define "machine#{machine_id}" do |machine|
    machine.vm.hostname = "machine#{machine_id}"
    machine.vm.network "private_network", ip: "192.168.77.#{20+machine_id}"

    # Only execute once the Ansible provisioner,
    # when all the machines are up and ready.
    if machine_id == N
      machine.vm.provision :ansible do |ansible|
        # Disable default limit to connect to all the machines
        ansible.limit = "all"
        ansible.playbook = "playbook.yml"
      end
    end
  end
end

s/:ansible/:ansible_local/gに書き換えて起動!
最後に立ち上げたVMにしかplaybookが作動せず、、、原因が良く分からない。
そもそも、ホストOSで呼び出す方法を真似たので良くなかったのかも。

Ansible Parallel Execution from a Guest

ansible_localはGuest側でansibleを起動するのでこっちの方法を参照するべきでした。
ansible専用のVM(controller)を作ろうと書いてあります。

Vagrantfile
Vagrant.configure("2") do |config|

  config.vm.box = "centos6.7x86_64"

  config.vm.define "web" do |machine|
    machine.vm.network "private_network", ip: "172.17.177.21"
  end

  config.vm.define "db" do |machine|
    machine.vm.network "private_network", ip: "172.17.177.22"
  end

  config.vm.define 'controller' do |machine|
    machine.vm.network "private_network", ip: "172.17.177.11"

    machine.vm.provision :ansible_local do |ansible|
      ansible.playbook       = "site.yml"
      ansible.verbose        = true
      ansible.install        = true
      ansible.limit          = "all" # or only "nodes" group, etc.
      ansible.inventory_path = "inventories/local/hosts"
    end
  end
end

「うーん、でもansible用のVMまでわざわざ作る必要あるのか?」と思い、もう少しシンプルな作りを目指してみました。

成果物

playbook_from_AlternativeDirectoryLayout
|-- Vagrantfile
|-- ansible.cfg
`-- playbook
    |-- inventories
    |   |-- production
    |   |   `-- hosts.yml
    |   |-- staging
    |   |   `-- hosts.yml
    |   |-- develop
    |   |   `-- hosts.yml
    |   `-- local
    |       `-- hosts.yml
    |-- roles
    |   |-- common
    |   |-- httpd
    |   `-- postgresql
    |-- site.yml
    |-- dbservers.yml
    `-- webservers.yml
Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

require 'yaml'

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.

VAGRANTFILE_API_VERSION = "2"
VAGRANTFILE_DIR = File.dirname(__FILE__)
ANSIBLE_INVENTORY_PATH = 'playbook/inventories/local/hosts.yml'

begin

conf = YAML.load_file(File.join(VAGRANTFILE_DIR, ANSIBLE_INVENTORY_PATH))
rescue Errno::ENOENT => ex
  STDERR.puts <<-EOT
    File does not exist. "#{ANSIBLE_INVENTORY_PATH}"
  EOT
  exit;
end

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  config.vm.box = "centos6.7x86_64"

  conf['all']['hosts'].each do |host, value|
    config.vm.define host do |machine|
      machine.vm.hostname = value['hostname']
      machine.vm.network "private_network", ip: value['ip']

      # virtualbox
      machine.vm.provider "virtualbox" do |vb|
        vb.name = value['hostname']
      end

      # synced_folder
      if value.has_key? 'synced_folder' then
        value['synced_folder'].each do |folder|
          machine.vm.synced_folder File.join(VAGRANTFILE_DIR, folder['host']), folder['guest']
        end
      end

      # ansible
      machine.vm.provision :ansible_local do |ansible|
        ansible.playbook = "playbook/#{host}.yml"
        ansible.inventory_path = ANSIBLE_INVENTORY_PATH
        ansible.limit = 'all'
      end
    end
  end
end
playbook/inventories/local/hosts.yml
---
# file: playbook/inventories/local/hosts.yml

all:
  hosts:
    webservers:
      hostname: carolsendai.co.jp
      ip: 192.168.39.90
      synced_folder:
        - { guest: /var/LOG, host: ./LOG }
        - { guest: /usr/local/lib, host: ./lib }
    dbservers:
      hostname: db.carolsendai.co.jp
      ip: 192.168.39.91
playbook/webservers.yml
---
# file: playbook/webservers.yml

- hosts: webservers
  connection: local
  become: yes
  roles:
    - common
    - httpd
playbook/dbservers.yml
---
# file: playbook/dbservers.yml

- hosts: dbservers
  connection: local
  become: yes
  roles:
    - common
    - postgresql

解説

hosts.yml

Vagrantfile
require 'yaml'
ANSIBLE_INVENTORY_PATH = 'playbook/inventories/local/hosts.yml'
conf = YAML.load_file(File.join(VAGRANTFILE_DIR, ANSIBLE_INVENTORY_PATH))

inventoryのhostsがyaml形式で書ける(ansible 2.1以上)ようになったことでロードが容易になりました。設定も色々記述できて自由度が上がったと思います。

multi-machine

Vagrantfile
VAGRANTFILE_DIR = File.dirname(__FILE__)
ANSIBLE_INVENTORY_PATH = 'playbook/inventories/local/hosts.yml'

conf['all']['hosts'].each do |host, value|
    config.vm.define host do |machine|
      machine.vm.hostname = value['hostname']
      machine.vm.network "private_network", ip: value['ip']

      # virtualbox
      machine.vm.provider "virtualbox" do |vb|
        vb.name = value['hostname']
      end

      # synced_folder
      if value.has_key? 'synced_folder' then
        value['synced_folder'].each do |folder|
          machine.vm.synced_folder File.join(VAGRANTFILE_DIR, folder['host']), folder['guest']
        end
      end

      # ansible
      machine.vm.provision :ansible_local do |ansible|
        ansible.playbook = "playbook/#{host}.yml"
        ansible.inventory_path = ANSIBLE_INVENTORY_PATH
        ansible.limit = 'all'
      end
    end

公式のようなN = 3など記述せず、hosts.ymlの配列分のVMを増やすこともできます。(そこまでする必要があるのかは置いといて)
site.ymlが読み込まれていないのがちょっと残念な作り。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7