ラベル Ansible の投稿を表示しています。 すべての投稿を表示
ラベル Ansible の投稿を表示しています。 すべての投稿を表示

2024年7月4日木曜日

ansible で git credentials helper を設定する方法

ansible で git credentials helper を設定する方法

概要

git_config というビルトインモジュールがあるのでそれを使った方法を紹介します
今回は ansible を実行したユーザではなく指定のユーザにしてい git config credentials helper を設定する方法を紹介します

環境

  • Ubuntu 22.04
  • ansible 2.16.7

playbook サンプル

- name: Set config credential.helper
  git_config:
    name: credential.helper
    scope: file
    value: store
    file: /home/user01/.gitconfig

- name: Set permissions for /home/user01/.gitconfig
  file:
    path: /home/user01/.gitconfig
    group: user01
    owner: group01
    mode: '644'

ポイント

ポイントは git_config モジュールの scope に file を指定する点です
また file を使って .gitconfig を保存する場所も指定します
こうすることで ansible を実行しているユーザに対して credentials helper を設定するのではなく指定のユーザに対して設定することができます
また .gitconfig を作成したあとでユーザや権限を正しく設定してあげます
そうしないと ansible を実行したユーザの権限で .gitconfig が作成されてしまうので指定のユーザが扱えなくなってしまうためです

最後に

git_config を使って credentials helper を設定する方法を紹介しました
特定のユーザに対して設定したい場合は file を使います

直接 command などを使う方法もありますがこちらを使うほうが良いかなと思います

参考サイト

2020年11月18日水曜日

Ansible で環境変数を使って特定の環境ごとにタスクを切り替える方法

概要

例えばデプロイするファイルを動的に切り替えたい場合に毎回 playbook を変更するのは面倒です
また group_vars などで変数で定義してもいいですがそれも毎回変更するのは面倒です
そんなときは環境変数で playbook を実行するたびに別の値を設定しそれを参照するようにできます

環境

  • macOS 10.15.7
  • ansible 2.9.10

サンプルコード

HOGE という環境変数を設定することを想定しています
copy のファイル名や when の条件にも使えます
環境変数の設定を必須にしたい場合は playbook の冒頭で変数のチェックをして空であれば fail にするような処理を入れたほうが良いかなと思います

---
- hosts: localhost
  gather_facts: no
  vars_files:
    - vault.yml
  tasks:
    - name: Check HOGE env
      fail: 
        msg: The HOGE variable is an empty or None.
      when: lookup('env', 'HOGE') == ""

    - name: Print HOGE env
      debug:
        msg: "HOGE = {{ lookup('env', 'HOGE') }}"

    - name: Put a file
      copy:
        src: files/test_{{ lookup('env', 'HOGE') }}.txt
        dest: ~/Downloads/test.txt
        owner: hawksnowlog
        group: staff
        mode: '0644'

実行する場合はコマンドの前に環境変数をセットしてから実行しましょう

  • HOGE=fuga ansible-playbook -i hosts site.yml

特定の値が環境変数に設定されているか確認する場合は

サンプルコードは空の場合にエラーにしました
特定の値が入っていない場合はエラーにしたい場合は when を以下のように変更しましょう

when: lookup('env', 'HOGE') not in ["hoge", "fuga", "foo"]

参考サイト

2020年11月6日金曜日

Ansible で Docker Swarm 環境を構築してみた

概要

community.general.docker_swarm なるロールがあったのでこれを使って docker swarm 環境を構築してみました
なお作業するマシンは Swarm の Master 側の Ubuntu 上で行います

環境

  • Ubuntu18.04 x 2
  • docker 19.03.13
  • ansible 2.9.15

docker の 2376 ポートの開放

こちらを参考に開放してください

お互いのマシンから 192.168.100.10:2376/info にアクセスして JSON が返ってくれば OK です

お互い SSH できるようにする

  • ssh-keygen -t rsa

をお互いのマシンで実行し id_rsa.pub を作成します
そしてそれぞれのマシンの authorized_keys に記載しましょう

ロールのインストール

  • ansible-galaxy collection install community.general

playbook の作成

では playbook を作成していきます
完成形は以下のような構成にします

.
├── group_vars
│   └── test
│       └── vars.yml
├── inventory
│   └── test
│       └── hosts.yml
├── roles
│   └── swarm
│       └── tasks
│           └── main.yml
└── site.yml

inventory ファイルの作成

  • mkdir -p inventory/test
  • vim inventory/test/hosts.yml
---

test:
  hosts:
    ubuntu:
      ansible_host: "{{ worker_ip }}"
    ubuntu2:
      ansible_host: "{{ manager_ip }}"

変数ファイルの作成

  • mkdir -p group_vars/test
  • vim group_vars/test/vars.yml
---

ansible_python_interpreter: /usr/bin/python3

cluster_interface: enp0s8

manager_ip: 192.168.100.11
worker_ip: 192.168.100.10
manager_host_name: ubuntu2

タスクファイルの作成

  • mkdir -p roles/swarm/tasks
  • vim roles/swarm/tasks/main.yml
---

- name: Install docker-py
  pip:
    name: docker-py

- name: Init a new swarm with default parameters
  community.general.docker_swarm:
    state: present
    advertise_addr: "{{ manager_ip }}"
  register: rezult
  when: lookup('vars', 'ansible_' + cluster_interface).ipv4.address == manager_ip                                                

- name: Add nodes
  community.general.docker_swarm:
    state: join
    advertise_addr: "{{ worker_ip }}"
    join_token: "{{ hostvars[manager_host_name]['rezult'].swarm_facts.JoinTokens.Worker }}"                                      
    remote_addrs: [ "{{ manager_ip }}:2377" ]
  when: lookup('vars', 'ansible_' + cluster_interface).ipv4.address == worker_ip

ちょっと長いので説明します

まず docker-py をインストールしています
今回使用する community.general.docker_swarm は内部的に docker-py を使って docker の操作をしているためです

次に swarm init しています
初期化するノードは manager ノードのみなので IP で実行するノードを制限しています
ここは IP ではなく inventory や group で制御しても問題ないです

最後に swarm join しています
これは worker ノードで実行する必要があるのでこれも IP で制御しています
更に join するためのトークンは manager ノード側の hostvars に格納されているのでそれを参照するようにしています
remote_addrs は docker swarm 用の管理ポートになるので開放した 2367 ではなく swarm manager が起動した際の 2377 を指定します

実行メインファイルの作成

ロールを指定して実行するだけです

  • vim site.yml
---

- name: Build swarm cluster
  hosts: test
  become: true
  roles:
    - role: "swarm"
      tags: ["swarm"]

動作確認

実行してみましょう

  • ansible-playbook -i inventory/test site.yml

成功すると docker swarm クラスタが構築されています

  • docker node ls

あたりで確認すると良いと思います

最後に

Ansible で community.general.docker_swarm を使って docker swarm を構築してみました
今回は 2 台だったのでイマイチありがたみが出てきませんでしたがこれが 10 台くらいの規模になると楽な運用ができるようになると思います

今回は単純な join だけを紹介しましたが leave や update も実行できるのでコマンドのオペレーションは一通り Ansible にすることはできそうです

参考サイト

2020年11月5日木曜日

ansible で nickjj.docker を使ってみた

概要

nickjj.docker は ansible を使って docker 環境を構築できるロールです
今回は Ubuntu 18.04 上で試してみました

環境

  • Ubuntu 18.04 (on Vagrant)
  • ansible 2.9.15

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.network "private_network", ip: "192.168.100.11"
  config.vm.provider "virtualbox" do |vb|
    vb.memory = 4096
    vb.cpus = 2
  end
end

ansible インストール

Vagrant で構築して Ubuntu 上で作業していきます

  • sudo apt -y update
  • sudo apt -y install software-properties-common
  • sudo apt-add-repository --yes --update ppa:ansible/ansible
  • sudo apt -y install ansible

ノンパスログイン

  • ssh-keygen -t rsa
  • cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
  • chmod og-wx ~/.ssh/authorized_keys

ロールインストール

  • ansible-galaxy install nickjj.docker

試す

  • vim hosts
[localhost]
localhost
  • vim site.yml
---
# site.yml
- name: Example
  hosts: localhost
  become: true
  roles:
    - role: "nickjj.docker"
      tags: ["docker"]
  • ansible-playbook -i hosts site.yml

これで docker の最新版がインストールされています
ログを見ているとわかりますが公式の手順と同様にリポジトリの追加など行っています
dockerd の設定なども playbook から変更できそうです

  • docker -v

=> 19.03.13

  • docker-compose -v

=> 1.26.2

デフォルトで docker-compose もインストールしてくれます
バージョンの指定やレジストリの URL の指定もレシピからできます

最後に

nickjj.docker を使って docker のインストールと docker-compose のインストールをしてみました
ansible 環境がすでにある場合はこれを使うと簡単にインストールできそうです

Swarm の構築までは行ってくれなさそうです

参考サイト

2020年11月3日火曜日

ansible vault 超入門

概要

触ってみないことにはわからないのでこちらのチュートリアルっぽい記事を参考に自分でも試してみました

環境

  • macOS 10.15.7
  • ansible 2.9.10

とりあえず動作する playbook の作成

まずはパスワードを暗号化していない状態で正常に動作する playbook を作成します

  • vim hosts
localhost ansible_connection=local
  • vim site.yml
---
- hosts: localhost
  gather_facts: no
  vars_files:
    - vault.yml
  tasks:
    - debug: msg="password = {{ password }}"
  • vim vault.yml
---
password: 'hogehoge'

これで実況すると vault.yml のパスワードの内容がそのまま表示されるのが確認できると思います

  • ansible-playbook -i hosts site.yml

ansible vault を使って vault.yml に書かれているパスワードを暗号化する

ansible-vault encrypt コマンドを使って先程の暗号化されていない vault.yml ファイルを指定します
この際に暗号化するためのパスワードが要求されるので適当に入力しましょう

  • ansible-vault encrypt vault.yml
New Vault password: Confirm New Vault password: Encryption successful

暗号化に成功すると上記のようになります
暗号化に使用したパスワードは別途ちゃんと覚えておきましょう

ファイルの内容を確認すると今度は暗号化された内容になっているが確認できると思います

  • cat vault.yml
$ANSIBLE_VAULT;1.1;AES256 63353464303138643235653836613236623637613733626562663234363866393836653064323337 3230346232373261353165616561313831323134633338390a396666303637323336346566343836 30306662343835303261386235313064636162373064626539386539633334396530383766326438 6230386530393864330a643162386461613066653537666431636162353361303934336361626131 32646165653533346639343137356465396362613237643562363030393966663565

この状態で再度 playbook を実行しようとすると今度はエラーになります

  • ansible-playbook -i hosts site.yml
ERROR! Attempting to decrypt but no vault secrets found

暗号化されたファイルを使って playbook を流してみる

暗号化する際に入力したパスワードを使うことで暗号化されたファイルを使って playbook を実行することができます
--ask-vault-pass というオプションを使えば OK です

  • ansible-playbook -i hosts site.yml --ask-vault-pass

これで Vault password の入力が求められるので暗号化したときのパスワードを入力すれば OK です

Vault password を入力しない場合は

毎回 Vault password を入力するのが面倒という場合は ansible.cfg を使うことで解決できます

  • vim ansible.cfg
[defaults]
vault_password_file = ~/.vault_password
  • vim ~/.vaule_password

でファイルを開いて Vault password を書いておくことで --ask-vault-pass が不要になります

  • ansible-playbook -i hosts site.yml

暗号化したファイルを復号化するには

単純に復号化してファイルの内容を確認することができます
その場合は ansible-vault decrypt を使います

  • ansible-vault decrypt vault.yml

ansible.cfg に vault_password_file が記載されていてすでに復号化用のパスワードが記載されている場合は Vault password を入力せずに復号化できます

再度暗号化する場合は ansible-vault encrypt を使います

  • ansible-vault decrypt vault.yml

最後に

ansible-vault を使ってみました
やろうと思えばこれですべての playbook を暗号化できますがそこまでやる必要はないと思います

参考サイト

2019年9月29日日曜日

Ansible で Inventory ファイルに記載されている IP 情報を取得する方法

概要

Inventory ファイルに記載されている IP アドレスを playbook 内で使いたい場合はあると思います
そんな場合には Magic Variables を使いましょう

環境

  • CentOS 7.7.1908
  • ansible-playbook 2.8.5

サンプルコード

debug_hosts グループ内で定義されているホストの IP アドレスを取得してみたいと思います

  • vim main.yml
- hosts: target
  tasks:
    - name: debug inventry
      vars:
        msg: |
          {% for item in groups['debug_hosts'] %}
          {{ hostvars[item].ansible_host }}
          {% endfor %}
      debug:
        msg: "{{ msg.split('\n') }}"
#      debug:
#        msg: "{{ groups }}"

インベントリファイルは以下の通りです

  • vim inv
target:
  hosts:
    localhost:
      ansible_host: 127.0.0.1
debug_hosts:
  hosts:
    host1:
      ansible_host: 192.168.200.10
    host2:
      ansible_host: 192.168.200.11

ansible 自体は localhost に実行します

実行

  • ansible-playbook -i inv main.yml

結果

ok: [localhost] => { "msg": [ "192.168.200.10", "192.168.200.11", "" ] }

解説

まず groups 変数にインベントリファイルに定義したグループの情報が含まれています
ただ groups にはホスト名までの情報しかなく IP アドレスの情報は含まれていません
そこで登場するのが hostvars です
これもマップで管理されておりホスト名をキーにするとそのホストの情報が取得できます
その中に ansible_host というフィールドがありここで IP アドレスを管理しています

サンプルでは debug + template を使っていますが playbook ないで使う場合は好きなリソースを使ってください
また見やすくするように一旦 vars を使っていますがこれも playbook 内では不要かなと思います

参考サイト

2019年6月19日水曜日

Ruby で Dynamic Inventory 入門

概要

DynamicInventory は Ansible がスクリプトを実行しその実行結果のインベントリ情報を使って playbook を流すことができる機能です
スクリプトは出力結果が決められたフォーマットになっていればどんな言語で書いても OK です
今回は Ruby でやってみました

環境

  • macOS 10.14.5
  • ansible 2.5.5
  • Vagrant 2.1.1
  • Ruby 2.6.2p47

テストマシン

今回は Vagrant で構築していますが SSH で接続できれば何でも OK です

  • vim Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.network "private_network", type: "dhcp"
  config.vm.network "private_network", ip: "192.168.99.200"
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "4096"
  end
end
  • vagrant up

SSH できるようにするがめ ssh-config を書き出します

  • vagrant ssh-config >> ~/.ssh/config

playbook

とりあえずテストマシンに playbook が流せる環境を作ります

  • vim example.yml
---
- hosts: role_web1
  tasks:
    - name: Install nginx
      shell: "amazon-linux-extras install nginx1.12"
      become: yes
    - name: Start nginx
      systemd:
        name: nginx.service
        state: started
      become: yes

インベントリファイルもとりあえずベタ書きします

  • vim inventory
[web1]
default

nginx をインストールして起動する playbook です
これでとりあえず実行できることを確認しましょう

  • ansible-playbook -i inventory example.yml -e 'ansible_python_interpreter=/usr/bin/python3'

Dynamic Inventory

さてここから Ruby を使った Dynamic Inventory を作成していきます
今回は特に動的に生成してる感じはないですが結果として「こんな感じの JSON が出力されれば良い」ということが理解できることを目的にしています

まずは単純なインベントリ情報を出力するスクリプトを書いてみます

  • vim inventory.rb
#! /usr/local/opt/ruby/bin/ruby
require 'json'

i = {
  :web1 => {
    :hosts => [
      'default'
    ]
  }
}
puts i.to_json
  • chmod +x inventory.rb

ファイルを指定するだけで実行できるようにシェバングを指定しています
シェバングのパスは環境によって異なると思うので適宜変更してください

このスクリプトを実行するとわかりますが単純な JSON が返ってきます

  • ./inventory.rb

=> {"web1":{"hosts":["default"]}}

このスクリプトを使って ansible-playbook を実行することができます

  • ansible-playbook -i ./inventory.rb example2.yml -e 'ansible_python_interpreter=/usr/bin/python3'

_meta

インベントリファイルには host_varsgroup_vars を書くことができます
Dynamic Inventory を使っても書くことができるので試してみました
スクリプトを以下のように書き換えましょう

  • vim inventory.rb
#! /usr/local/opt/ruby/bin/ruby
require 'json'

i = {
  :web1 => {
    :hosts => [
      'default'
    ],
    :vars => {
      :gvar => "gvalue"
    }
  },
  :_meta => {
    :hostvars => {
      :default => {
        :hvar => "hvalue"
      },
    }
  }
}
puts i.to_json

group_vars はホストグループの定義配下に vars を使って定義します
host_vars_meta を使って定義します
_meta 配下に hostvars を定義しその配下に key/value を定義することで参照することができるようになります

確認のための playbook も書き換えます

  • vim example.yml
---
- hosts: web1
  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present
      become: yes
    - name: Start nginx
      systemd:
        name: nginx.service
        state: started
      become: yes
    - name: Debug host_vars
      debug:
        msg: _meta host_vars is {{hvar}}
    - name: Debug group_vars
      debug:
        msg: _meta group_vars is {{gvar}}

debug を使って変数の内容を表示しているだけです
これで先程同様に実行すると変数が問題なく参照できるのが確認できると思います

  • ansible-playbook -i ./inventory.rb example2.yml -e 'ansible_python_interpreter=/usr/bin/python3'

最後に

Dynamic Inventory を Ruby で試してみました
当然ですが ansible-playbook コマンドを実行するマシンに ruby がインストールされている必要があります
少し調べた感じだと --list--host オプションを実装したほうが良さそうだったのですが実装しないでも動作しました
スクリプトがインベントリファイルの形式で出力できたほうが良い場合などは実装すると良いかなと思います

実際は API や Terraform の .tfstate ファイルなどをパースしてインベントリ情報の JSON を生成することになると思います

参考サイト

2019年6月18日火曜日

terraform-inventory 最速入門

概要

terraform-inventory の挙動をとりあえず動かしてサクっと確認したい人向けの記事です
ただ執筆時点での結論から言うと terraform の最新バージョン 0.12 には対応していないため対応までは採用見送りかなと思っています

環境

  • macOS 10.14.5
  • ansible 2.5.5
  • terraform 0.12.1 -> 0.11.14

terraform インストール

  • brew install terraform@0.11

0.12 にはまだ対応していないので 0.11 をインストールする

example.tf

  • vim example.tf
provider "aws" {
  region = "us-east-2"
}

resource "aws_instance" "web1" {
  ami = "ami-0ebbf2179e615c338"
  instance_type = "t2.nano"
  key_name = "sshkey"
  tags = {
    Role = "web1"
  }
}

key_name = "sshkey" は事前に作成済みを想定しています

AWS にインスタンス作成

  • terraform init
  • terraform plan
  • terraform apply

ansible インストール

  • brew install ansible

terraform-inventory インストール

  • brew install terraform-inventory
  • terraform-inventory -inventory terraform.tfstate

Inventory ファイルの情報が出力されることを確認します

playbook 作成

  • vim example.yml
---
- hosts: role_web1
  tasks:
    - name: Install nginx
      shell: "amazon-linux-extras install nginx1.12"
      become: yes
    - name: Start nginx
      systemd:
        name: nginx.service
        state: started
      become: yes

playbook 適用

  • ansible-playbook -i $(which terraform-inventory) example.yml -u ec2-user --private-key /path/to/sshkey.pem

動作確認

  • curl ec2-3-14-84-102.us-east-2.compute.amazonaws.com

パブリック DNS の部分は適宜変更してください

解説

terraform

provider aws を使って AWS 上にインスタンスを作成します
ami = "ami-0ebbf2179e615c338" は Amazon Linux2 になります
ポイントは tags でこれを付与することで ansible の hosts としてインスタンスを指定することができます

ansible

terraform.tfstate というファイルを元に terraform-inventory というコマンドが自動的にインベントリ情報を生成してくれます
このようにプログラムが動的にインベントリ情報を生成する仕組みを Dynamic Inventory といいます
ec2 に生成したインスタンスに ssh ログインするには ec2-user ユーザの指定と鍵の指定が必要になります

また今回の動作確認は nginx をインストールして起動しています
ec2 に適用されるセキュリティブループは default を想定しているおり 80 でのアクセスができるようにしておいてください

最後に

terraform-inventory を試してみました
.tfstate ファイルを元にインベントリ情報を動的に生成してくれるツールです
最新版に対応するのは時間の問題かなと思いますが、今後も terraform のバージョンアップにより .tfstate のフォーマットが変更されることを考えると Dynamic Inventory 用のスクリプトを自作するほうが良いかもしれません

参考サイト

2018年6月28日木曜日

Ansible から docker-compose を使ってみた

概要

docker_service というモジュールを使うことで docker-compose.yml を制御することができます

環境

クライアント

  • macOS 10.13.5
  • Ansible 2.5.5

ターゲットホスト

  • Ubuntu 16.04
  • docker 1.13.1, build 092cba3

レシピ作成

必要なファイルを作成していきます

site.yml

実行メインです

- hosts: all
  roles:
    - role: git
    - role: docker

production

インベントリファイルです

[all]
172.28.128.3

[all:vars]
ansible_user = root
ansible_ssh_pass = xxxxxxxxx
ansible_connection = paramiko

git

docker-compose をターゲットのサーバに配置します
今回は Github で公開されている docker-compose を使うので clone するレシピを作成します
ローカルからコピーなどしても OK です

  • mkdir -p roles/git/tasks
  • vim roles/git/tasks/main.yml
- include: zabbix.yml
  • touch roles/git/tasks/zabbix.yml
- include: zabbix.ymlyoshi1:try_zabbix_docker kakakikikeke$ cat roles/git/tasks/zabbix.yml 
- name: Clone
  git:
    repo: 'https://github.com/zabbix/zabbix-docker.git'
    dest: /root/zabbix-docker

zabbix

今回は Zabbix の docker-compose を使ってみます

  • mkdir -p roles/git/tasks
  • vim roles/docker/tasks/main.yml
- include: zabbix.yml
  • vim roles/docker/tasks/zabbix.yml
- name: Run zabbix
  docker_service:
    project_src: /root/zabbix-docker
    files:
      - docker-compose_v2_ubuntu_mysql_latest.yaml
    state: present

git clone したディレクトリを project_src で指定します
もし docker-compose.yaml or docker-compose.yml という名前のファイルがある場合は自動的にそれを読み込んで up してくれます
しかしファイル名が違う場合は明示的に指定する必要があります
そのために files パラメータを使って docker-compose の YAML ファイルを指定する必要があります

state は present と absent がありそれぞれ up と down に対応しています

動作確認

  • ansible-playbook -i production site.yml

で実行できます
成功するとターゲットのホストに 9 つのコンテナが起動しているのが確認できると思います

  • docker ps
CONTAINER ID        IMAGE                                              COMMAND                  CREATED              STATUS    
         PORTS                                         NAMES
452c148e3d8b        zabbix/zabbix-proxy-mysql:ubuntu-3.4-latest        "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10071->10051/tcp                      zabbixdocker_zabbix-proxy-mysql_1                                       
29646c521ab7        zabbix/zabbix-proxy-sqlite3:ubuntu-3.4-latest      "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10061->10051/tcp                      zabbixdocker_zabbix-proxy-sqlite3_1                                     
b020130836c3        zabbix/zabbix-web-apache-mysql:ubuntu-3.4-latest   "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp      zabbixdocker_zabbix-web-apache-mysql_1                                  
daedb397f768        zabbix/zabbix-agent:ubuntu-3.4-latest              "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10050->10050/tcp                      zabbixdocker_zabbix-agent_1                                             
c610d76ee1ea        zabbix/zabbix-web-nginx-mysql:ubuntu-3.4-latest    "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:8081->80/tcp, 0.0.0.0:8443->443/tcp   zabbixdocker_zabbix-web-nginx-mysql_1                                   
585bb7622693        zabbix/zabbix-server-mysql:ubuntu-3.4-latest       "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10051->10051/tcp                      zabbixdocker_zabbix-server_1                                            
f856fba5aac2        mysql:5.7                                          "docker-entrypoint..."   About a minute ago   Up About a
minute   3306/tcp                                      zabbixdocker_mysql-server_1                                             
89a7a3bdc46c        zabbix/zabbix-snmptraps:ubuntu-3.4-latest          "/usr/bin/supervis..."   About a minute ago   Up About a
minute   0.0.0.0:162->162/udp                          zabbixdocker_zabbix-snmptraps_1                                         
8b5dafc3a00a        zabbix/zabbix-java-gateway:ubuntu-3.4-latest       "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10052->10052/tcp                      zabbixdocker_zabbix-java-gateway_1

http://172.28.128.3 にアクセスすると Zabbix の画面が表示されます
Admin/zabbix でダッシュボードにログインすることができます

最後に

Ansible から docker-compose を使ってみました
今回は既存の docker-compose ファイルを使用しました
docker_service モジュールはレシピの YAML ファイルに直接 docker-compose の内容を記載することもできるようです

docker_service はまだ v3 には対応しておらず v2 形式の YAML ファイルを使用する必要があります

Ansible 経由で定義することで変数機能などを使うことができるのがメリットかなと思います

zabbix_server.conf など主要な設定ファイルはホストでマウントしている感じではないのでコンテンからコピーするかコンテナのファイルを直接編集する必要があります

参考サイト

2018年6月27日水曜日

Ansible から docker を制御してみた

概要

docker_container というモジュールがありこれを使うと docker の制御することができます
今回はコンテナの起動など行ってみました

環境

クライアント

  • macOS 10.13.5
  • Ansible 2.5.5

ターゲットホスト

  • Ubuntu 16.04
  • docker 1.13.1, build 092cba3

レシピ作成

必要なファイルを作成していきます

site.yml

メインのレシピです

- hosts: all
  roles:
    - role: docker

production

インベントリファイルです

[all]
172.28.128.3

[all:vars]
ansible_user = root
ansible_ssh_pass = xxxxxx
ansible_connection = paramiko

対象のホストは Vagrant で作成した Ubuntu16 ですでに docker を apt でインストール済みです

nginx.yml

メインとなるレシピを作成します

  • mkdir -p roles/docker/tasks/
  • vim roles/docker/tasks/main.yml
- include: zabbix.yml
  • vim roles/docker/tasks/nginx/yml
- name: Run nginx
  docker_container:
    name: web
    image: nginx:latest
    ports:
      - "81:80"

おそらく何を設定しているか予想できると思います
nginx コンテナを 80 番ポートで起動して 81 番ポートで EXPOSE しています
またコンテナの名前を web にしています

name は必須パラメータになっています

実行

  • ansible-playbook -i production site.yml

パスワード等はすべてインベントリファイルに記載したので上記で実行できるはずです

動作確認としては curl でも叩いて見ましょう

  • curl 172.28.128.3:81

これで nginx のデフォルト画面が返ってくれば OK です

それ以外の動作

コンテナを停止する

- name: Stop
  docker_container:
    name: web
    state: stopped

コンテナを削除する

- name: Remove
  docker_container:
    name: web
    state: absent

最後に

Ansible から docker_container モジュールを使ってコンテナ制御をしてみました
当然ですがターゲットとなるホストには docker がインストールされている必要があります

Ansible の 2.4 から docker というモジュールが DEPRECATED になりました
代わりに docker_containerdocker_image というモジュールを使うようになったのでご注意ください
今回はコンテナ操作だけだったので docker_container を使いました

最近だとコンテナスケジューラが充実しているので運用はそっちを使えばいいと思います
コンテナホストで初回のコンテナ起動などにこれを使う感じかなと思います

ドキュメントは以下で紹介している公式のドキュメントが一番参考になります

参考サイト

2018年6月25日月曜日

ansible-galaxy を使って Ubuntu に docker をインストールしてみた

概要

ansible galaxy は簡単に言えば自分が作成した playbook (ロール) を公開するための機能です
すでに公開されている playbook がある場合簡単にそれを使うことができます
今回は docker のインストールを ansible galaxy で公開されているロールを使ってやってみました

環境

  • macOS 10.13.5
  • Ansible 2.5.5

ansible-galaxy のインストール

  • pip install ansible

でインストールできます
Mac であれば Homebrew でもインストールできます

ロールのインストール

ansible galaxy で公開されている docker をインストールするためのロールは結構あります
今回はこれを使います

  • ansible-galaxy install angstwad.docker_ubuntu

で OK です
インストールは ~/.ansible/roles/angstwad.docker_ubuntu にされます

Tips

カレントディレクトリに .netrc ファイルがあるとインストールに失敗することがあるようです
一旦 rename などをして再度 install コマンドを実行してみてください

 [WARNING]: - angstwad.docker_ubuntu was NOT installed successfully: Failed to get data from the API server                    
(https://galaxy.ansible.com/api/): bad follower token 'method' (/Users/hawksnowlog/.netrc, line 4)

レシピ作成

  • vim site.yml
- hosts: all
  roles:
    - role: angstwad.docker_ubuntu

今回は特にカスタマイズせずデフォルトのロールを使っています
カスタマイズできる変数の一覧はこちらで定義されているので必要に応じて site.yml 内で上書きしてください

  • vim production
[worker]
192.168.100.10

[worker:vars]
ansible_user=root
ansible_connection=paramiko
ansible_ssh_pass=pass
ansible_ssh_private_key_file=./ssh_secret_key.pem

今回は SSH の鍵認証を使っています
認証情報は Inventory ファイルにすべて記載しました

また ansible-connection=paramiko にしないと you must install the sshpass program のエラーになるので明示的に指定します

ファイルはこれだけです
あとは実行してみましょう

実行

  • ansible-playbook -i production site.yml

で OK です
docker と docker-compose のインストールが始まります
ok=23, changed=9 で終了しました

インストールされていたバージョンはかなり最新のものがインストールされていました

root@ubuntu:~# docker -v
Docker version 18.03.1-ce, build 9ee9f40
root@ubuntu:~# docker-compose -v
docker-compose version 1.21.2, build a133471

冪等性を確認するために再度流してみましたが特にエラーになることはありませんでした

最後に

ansible-galaxy を使って公開済みのロールを使ってみました
自分で作り直す必要がないので便利です
が、公開されているロールが何をしているかは不明なので詳細はコードを読むしかありません

今回のロールはかなり簡単なものだったので良いのですが複雑なものになると読むのに一苦労なので自分作ったほうが良いケースもあるかもしれません

参考サイト

2018年6月23日土曜日

ansible-playbook を Python からコールしてみた

概要

よくあるケースとしてはダイナミックにサーバをプロビジョニングしたい場合かなと思います
簡単な playbook を作成した後でプログラマブルに Ansible を実行してみます

環境

  • macOS 10.13.5
  • Python 2.7.15
  • Ansible 2.5.5

事前準備

今回は Mac で行っています
pip からインストールしました

  • pip install ansible

Homebrew でもインストールできるのでそれでも問題ないです

簡単な playbook の作成

非常に簡単なやつを作成します
ターゲットのサーバに対してファイルを touch するだけです
全体は以下の通りです

  • tree -a
.
├── production
├── roles
│   └── file
│       └── tasks
│           ├── main.yml
│           └── new.yml
└── site.yml
  • vim production
[ubuntu]
172.28.128.3

この Ubuntu サーバは vagrant を使ってローカルに立てています
サーバは何でも OK なので適当に用意してください

  • vim roles/file/tasks/main.yml
- include: new.yml
  • vim roles/file/tasks/new.yml
- file:
    path: /tmp/hoge
    state: touch

main.yml は参照するだけで本体は new.yml を作成してそっちに書いています
Ansible 側の内容はこれだけです

実行は

  • ansible-playbook -k -i production site.yml

です
-k オプションは SSH のパスワードを対話的に指定するためのオプションです

これで問題なく動作すれば playbook の準備は OK です

Tips

少し自分がはまった点を紹介します

fatal: [172.28.128.3]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "/bin/sh: 1: /usr/bin/python: n
ot found\r\n", "msg": "MODULE FAILURE", "rc": 127}

で実行できない状態がありました
ターゲットのマシンである Ubuntu に Python をインストールしてあげることで解決しました
Vagrant の xenial を使っていると Python がデフォルトでインストールされていないようです
Ansible は基本的に 2.7 以上の Python が /usr/bin/python にインストールされている想定だそうです
https://docs.ansible.com/ansible/2.4/faq.html

一応 Python なしでも実行する方法はあるらしいのですが基本はいるようです

Ansible を Python からキックするスクリプトの作成

ここからが本番です
先ほど作成した playbook と同じことを Python から実行してみます
結構長いのであとで詳細を説明します
改行部分がまとまりとなっています

  • vim site.py
import json
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
import ansible.constants as C

class ResultCallback(CallbackBase):
    def v2_runner_on_ok(self, result, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_failed(self, result, *args, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_unreachable(self, result):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff', 'remote_user'])
options = Options(connection='paramiko', module_path=['/usr/share/ansible'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False, remote_user='root')

loader = DataLoader()
results_callback = ResultCallback()
host_list = ['./production']
sources = ','.join(host_list)
inventory = InventoryManager(loader=loader, sources=host_list)
variable_manager = VariableManager(loader=loader, inventory=inventory)

play_source =  dict(
    name = "Ansible Play",
    hosts = ['ubuntu'],
    # gather_facts = 'yes',
    gather_facts = 'no',
    tasks = [
        dict(action=dict(module='file', args=dict(path='/tmp/hoge', state='touch')))
    ]
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

tqm = None
passwords = dict()
try:
    tqm = TaskQueueManager(
              inventory=inventory,
              variable_manager=variable_manager,
              loader=loader,
              options=options,
              passwords=passwords,
              stdout_callback=results_callback,
          )
    result = tqm.run(play)
finally:
    if tqm is not None:
        tqm.cleanup()
    shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

まず全体的な流れとして

  • 必要なライブラリの読み込み -> コールバック処理の定義 -> オプションの設定 -> インベントリ情報の生成 -> playbook の定義 -> 実行

という感じになります
それぞれ順を追って説明します

詳細説明

まず import 系ですがこれはほぼすべて必須かなと思います

import json
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
import ansible.constants as C

各流れの中で使用するものになります
次にコールバック用のクラスを先に作成します

class ResultCallback(CallbackBase):
    def v2_runner_on_ok(self, result, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_failed(self, result, *args, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_unreachable(self, result):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

results_callback = ResultCallback()

コールバッククラスは何に使うかというと単純に playbook が終了した際に何をするか定義することができます
今回は「成功」「失敗」「アクセスできない」の 3 つのイベントをハンドリングして内容を表示しているだけです
ここで定義したコールバッククラスはあとで使用します

次にオプションの設定です
これは CLI を実行する際のオプションと同じですが Python からコールする際には必須のオプションがあります

Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff', 'remote_user'])
options = Options(connection='paramiko', module_path=['/usr/share/ansible'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False, remote_user='root')

最後の remote_user だけ追加しました
それ以外は公式で紹介していた部分になるので必須かなと思います
例えば ansible-playbook コマンドでオプションを指定する必要がある場合はここで追加します
今回の場合であれば -k-i ですがこれらは InventroyManager で定義することになります (ややこしい)

次に InventoryManager になります

loader = DataLoader()
host_list = ['./production']
sources = ','.join(host_list)
inventory = InventoryManager(loader=loader, sources=host_list)
variable_manager = VariableManager(loader=loader, inventory=inventory)

ポイントは定義したインベントリファイル (production) を読み込んでいる部分です
実はファイルを使わないでも直接ここでインベントリ情報を定義することができます
が実はファイルを使ったほうが理由があります
それは SSH 時のパスワードを定義するためです
先ほどの production から実は以下のように変更しています

  • vim production
[ubuntu]
172.28.128.3

[ubuntu:vars]
ansible_ssh_user='root'
ansible_ssh_pass='xxxxxxxxxx'

Python から実行する際に -k を使って対話的に実行することはできません
なので、パスワード情報を事前にインベントリファイルに定義しておく必要があります
このパスワード情報を InventoryManager に食わせる方法がファイルからしかやり方がわからなかったため、既存のインベントリファイルを使うようにしています

次に playbook の定義です

play_source =  dict(
    name = "Ansible Play",
    hosts = ['ubuntu'],
    # gather_facts = 'yes',
    gather_facts = 'no',
    tasks = [
        dict(action=dict(module='file', args=dict(path='/tmp/hoge', state='touch')))
    ]
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

ポイントは hosts と tasks になります
hosts ではインベントリファイルに書かれているホスト情報のうちどれを実行するかを配列で定義できます
今回は 1 台だけなのでインベントリファイルに書かれているホストを 1 台定義しています
tasks ですが、ここにメインとなるプロビジョニング処理を定義する必要があります
tasks は先ほどのインベントリファイルとは違いファイルを流用することができません (今回の方法だとないだけで一応ファイルから実行する方法もあるっぽいです PlaybookExecutor ?)
なので、ここで同じ内容を定義し直す必要があります
今回であれば file モジュールを使って新規でファイルを touch しているだけなのでそこまでコードにするのは難しくありません
あとは Play().load() でこれまでに定義した情報を食わせれば playbook が出来上がります

最後に実行部分です

tqm = None
passwords = dict()
try:
    tqm = TaskQueueManager(
              inventory=inventory,
              variable_manager=variable_manager,
              loader=loader,
              options=options,
              passwords=passwords,
              stdout_callback=results_callback,
          )
    result = tqm.run(play)
finally:
    if tqm is not None:
        tqm.cleanup()
    shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

実行には TaskQueueManager を使います
これを使うことで実行結果をコールバックとして設定することができます
基本は定義してものを食わせていけば OK です
あとは run() するだけです

動作確認

  • python site.py

と実行すれば OK です
結果は以下のような感じで JSON で表示されます

{
    "172.28.128.3": {
        "_ansible_parsed": true, 
        "group": "root", 
        "uid": 0, 
        "dest": "/tmp/hoge", 
        "changed": true, 
        "state": "file", 
        "gid": 0, 
        "mode": "0644", 
        "invocation": {
            "module_args": {
                "directory_mode": null, 
                "force": false, 
                "remote_src": null, 
                "path": "/tmp/hoge", 
                "owner": null, 
                "follow": true, 
                "group": null, 
                "unsafe_writes": null, 
                "state": "touch", 
                "content": null, 
                "serole": null, 
                "diff_peek": null, 
                "setype": null, 
                "selevel": null, 
                "original_basename": null, 
                "regexp": null, 
                "validate": null, 
                "src": null, 
                "seuser": null, 
                "recurse": false, 
                "delimiter": null, 
                "mode": null, 
                "attributes": null, 
                "backup": null
            }
        }, 
        "owner": "root", 
        "diff": {
            "after": {
                "path": "/tmp/hoge", 
                "state": "touch"
            }, 
            "before": {
                "path": "/tmp/hoge", 
                "state": "absent"
            }
        }, 
        "size": 0, 
        "_ansible_no_log": false
    }
}

ターゲットのサーバに入ってファイルができているかも確認すると良いかもしれません
一応これで Python から同じ内容の playbook を実行することはできました

最後に

ansible-playbook を Python から実行してみました
正直辛い感じを覚えました
今回のやり方は公式に書いてあるサンプルを元にしているので、おそらく王道だと思います

一番辛いのは情報が少ないことです
タスクの定義の仕方などは各モジュールごとにいろいろと違うと思いますが、それをコードに落とす方法は Try&Error を繰り返すしかないかと思います
これをやりたいケースとしては既存の playbook をプログラミングから実行したい場合が一番多いかなと思います
その場合に今回の方法だと既存の playbook が使い回せないのが辛い点かなと思います

playbook を作る際にはじめからこの方法を取るのであれば何とかなるかもしれません
ただ今回紹介した構成はかなりミニマムな構成なのでここから group_vars やタスクの templates などが絡んでくると思うので、その場合にどうするかも調査しないとダメだと思います

他にやり方がないかもう少し見てみようと思います

参考サイト

2017年11月2日木曜日

Windows 2016 に ansible を使ってみる

概要

Ansible を WindowsServer 2016 に使ってみました
WindowsServer の構築はこちらを参考にしてください
ansible コマンドを実行する環境は CentOS になります

環境

  • Windows 2016
  • CentOS 7.4.1708
    • ansible 2.4.0.0
    • pip 9.0.1
    • python 2.7.5

ansible 環境の準備

ansible は yum コマンドでインストールしています

  • yum -y install ansible

必要な python モジュール等があるので準備します

  • wget 'https://bootstrap.pypa.io/get-pip.py'
  • python get-pip.py
  • pip install xmltodict
  • pip install pywinrm

2 つのライブラリを追加しました

とりあえず ping を送ってみる

では ansible の playbook を作成していきます
とりあえず ansible 経由で ping を送信してみましょう

  • vim inventory.yml
[windows]
192.168.56.101

ホストの情報を設定します

  • mkdir group_vars
  • vim group_vars/windows.yml
ansible_user: winuser1
ansible_password: winpass123
ansible_port: 5985
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore

WindowsServer に接続する認証情報を記載します
ansible が WindowsServer に接続するのは packer 時と同様で winrm を使っています

では実行してみましょう

  • ansible windows -i inventory.yml -m win_ping

で以下の結果が返ってくれば OK です

192.168.56.103 | SUCCESS => {
    "changed": false, 
    "failed": false, 
    "ping": "pong"
}

winrm の有効化について

今回 WindowsServer 2016 の構築は packer を使用しました
packer を使って構築する場合 winrm の有効が必須になります
なので、packer 経由で WindowsServer を構築すると必然的に winrm が有効になっています

もし有効になっていない場合はコンソール画面を使って WindowsServer を操作します
Powershell のプロンプトを管理者権限で開いて以下のコマンドを実行することで有効にすることができます

  • winrm qc

で y/n を求められるので y を入力すれば有効にできます

他のプロビジョニングをしてみる

接続できたらいろいろと playbook を作成して実行してみましょう
ディレクトリ構成はベストプラクティスに沿って作成します

コマンドを実行して結果を出力する

  • mkdir -p roles/common/tasks/
  • vim roles/common/tasks/main.yml
- include_tasks: test.yml
  • vim roles/common/tasks/test.yml
- name: run ipconfig
  raw: ipconfig
  register: ip_info
- debug: var=ip_info

- name: test stat module on file
  win_stat: path="C:/Windows/win.ini"
  register: stat_file
- debug: var=stat_file

raw はコマンドを直接実行するモジュールです
register は実行した結果を指定した変数に格納することができるモジュールです
debug は指定した情報を出力するモジュールです
var= オプションを使うことで register で定義した変数を参照することができます

  • vim site.yml
---
- hosts: all
  roles: 
    - common
  • ansible-playbook -i inventory.yml site.yml

で実行すると IP アドレスの表示とファイルのチェックを行います
本当は stat_file を assert: を使って中身を確認するところまでやったほうが良いのですが今回はサンプルなので省略しています

Powershell を実行する

  • mkdir -p roles/common/files/
  • vim roles/common/files/test.ps1
Write-Host "test `n"
  • vim roles/common/tasks/powershell.yml
- name: execute powershell
  script: files/test.ps1
  register: echo_ret
- debug: var=echo_ret
  • vim roles/common/tasks/main.yml
- include_tasks: test.yml
- include_tasks: powershell.yml
  • ansible-playbook -i inventory.yml site.yml

こんな感じです
先ほどの test.yml も実行していますが不要であれば include_tasks から削除すれば OK です

WinRAR をインストールする

  • vim roles/common/files/winrar.ps1
$workdir = "c:\installer\"

If (Test-Path -Path $workdir -PathType Container)
{ Write-Host "$workdir already exists" -ForegroundColor Red}
ELSE
{ New-Item -Path $workdir  -ItemType directory }

$source = "http://rarlab.com/rar/winrar-x64-540.exe"
$destination = "$workdir\winrar.exe"
Invoke-WebRequest $source -OutFile $destination
Start-Process -FilePath "$workdir\winrar.exe" -ArgumentList "/S"

Start-Sleep -s 35
rm -Force $workdir\w*
  • vim roles/common/tasks/winrar.yml
- name: execute powershell
  script: files/winrar.ps1
  register: install_ret
- debug: var=install_ret
  • vim roles/common/tasks/main.yml
- include_tasks: test.yml
- include_tasks: powershell.yml
- include_tasks: winrar.yml
  • ansible-playbook -i inventory.yml site.yml

成功した後にプラグラムの一覧を確認すると WinRAR がインストールされているのか確認できると思います
win2016_with_ansible1.png

最後に

WindowsServer2016 に ansible を実行してみました
基本は WinRM 経由でいろいろとコマンドを実行する感じです
コマンドは shell などではなく Powershell がメインになります

Powershell を覚えさえすればいろいろとできるかなと思います

参考サイト

2017年5月12日金曜日

docker の最新版を CentOS7 にインストールする ansible のレシピ

概要

CentOS7 を構築するたびに docker のインストールを手動で行っていたので ansible のレシピ化をしてみました
別のディストリビューションでの動作やバージョンを指定してのインストールには対応していません

環境

  • CentOS 7.3.1611
  • docker 17.05.0-ce, build 89658be
  • docker-compose 1.12.0
  • ansible 2.3.0.0 (実行側クライアント)

レシピ

  • vim roles/docker/tasks/install.yml
- name: Remove installed docker
  yum:
    name:
      - docker
      - docker-common
      - container-selinux
      - docker-selinux
      - docker-engine
    state: absent
- name: Install yum-utils
  yum:
    name:
      - yum-utils
    state: present
- name: Add docker yum repositry
  command: yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
- name: Enable repo
  command: yum-config-manager --enable docker-ce-edge
- name: Make cache
  command: yum makecache fast
- name: Update
  yum:
    name: '*'
    state: latest
- name: Install newest docker
  yum:
    name:
      - docker-ce

ちなみに docker-compose までインストールしたい場合は以下を追記すれば OK です

- name: Install docker-compose
  shell: "curl -L https://github.com/docker/compose/releases/download/1.12.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose"
- name: Add execution permission
  command: 'sudo chmod +x /usr/local/bin/docker-compose'

という感じのレシピを作成してメイン側の yml ファイルで

---
- hosts: all
  user: root
  roles: 
    - docker

とかすれば OK かなと思います

最後に

CentOS7 に ansible で docker の最新版をインストールする方法を紹介しました
docker のインストール方法は結構変わるのでいずれ使えなくなる可能性がありますが、備忘録として残しておきます

今回のレシピは変数やテンプレートも使っておらずかなりシンプルなレシピになっています
とりあえず動けば的な感じで作ったので command や shell も多用しています

ansible は最新バージョンに追従したりしなきゃいけないので結構メンテナンス大変ですね、、、

2017年3月10日金曜日

Ansible で SSH に鍵認証を使用する方法

概要

前回 の入門では ID/PW による方法とノンパスログインによる方法を紹介しました
今回は秘密鍵と公開鍵を使った SSH 認証で Ansible を実行する方法を紹介します

環境

  • CentOS 6.7 64bit
  • Ansible 1.9.4

設定方法

鍵認証を使う方法はいくつかあります

設定ファイルに記載する方法

  • vim /etc/ansible/ansible.cfg

を編集します

private_key_file = /path/to/key/secret_key.pem

というパラメータがあるので、秘密鍵へのパスを設定します
これで実行すると実行時に鍵のパスフレーズの入力を求められます

$ ansible all -m ping
Enter passphrase for key '/path/to/key/secret_key.pem': 

実行時のパラメータで指定する

  • ansible all --private-key=/path/to/key/secret_key.pem -m ping

で同様にパスフレーズの入力を求められます

最後に

紹介は以上です
全サーバ共通の秘密鍵を使うなら設定ファイルに記載して、サーバごとに異なる場合は実行時のパラメータで指定する感じでしょうか
また、今回は ansible コマンドで紹介しましたが、ansible-playbook コマンドでも同様な指定方法になります

Tips

ちょっと気になった挙動があって、パスフレーズの入力を求められたあと認証に成功すると一定時間認証情報をどっかに保存しているのかパスフレーズを求められないことがありました
詳しく調査してないのですが、そんな機能があるのだろうか

Ansible 超入門

概要

今更 Ansible に触ったのでメモしておきます
Playbook という YAML で記載するレシピ的なものを作ってみるところまで実施してみました

環境

  • CentOS 6.7 64bit
  • Ansible 1.9.4

Ansible のインストール

EPEL が使えるのであれば yum でインストールすることができます
今回は yum を使いました

  • yum -y install ansible
  • ansible –version

1.9.4

となれば OK です

Inventory ファイルを編集する

Inventory ファイルはいわゆる hosts ファイルです
Ansible 用の hosts ファイルで実行するホストを定義します

  • vim /etc/ansible/hosts

に Ansible を実行するホストを定義します
デフォルトだと「alpha.example.org」やら「beta.example.org」やらが定義されていてサンプルを流すと絶対エラーになるので適当に削除してください
とりあえずサンプルを動作させるので同じホストでも記載しておいてください
今回は「192.168.1.10」に実行するものとします

192.168.1.10

定義はグループにわけて記載したり、正規表現的なものを使って複数サーバを定義することもできます

ping モジュールを実行する

Inventory ファイルに記載したサーバに ping モジュールというデフォルトで用意されているモジュールを実行してみます

  • ansible all -m ping

で実行することができます
定義サーバにはデフォルトだとノンパスでログインしようとします
公開鍵や ID/PW 認証を使って実行することもできますが、とりあえず今回は素直にノンパス設定をしました
実行に成功すると以下のように表示されます

192.168.1.10 | success >> {
“changed”: false,
“ping”: “pong”
}

失敗する場合は対象のサーバに対してちゃんと SSH できるか確認する他、ping モジュールはその名の通り ping を投げるので、ping が届くか確認してください
また、デフォルトだと SSH のポートが 22 番なのでそれ以外でアクセスしたい場合は Inventory ファイルに hostname:port 形式で記載してください

独自の Playbook を定義する

所謂レシピになります
YAML で記載することができます

  • cd work/ansible
  • vim sample_pb.yml
---
- hosts: all
  user: root
  tasks:
    - name: This is a test playbook
      command: /bin/hostname
    - name: This is a test playbook 2
      command: /bin/echo 'hoge'

hosts: で実行する対象のグループまたはホストを指定します
とりあえず今回は全台 ( 1 台 ) に実行するので all にしています
user: は実行するユーザを指定します
別のユーザで SSH ログインする場合は、そのユーザを指定してください
また、そのユーザは先程設定したように SSH でノンパスログインできるようにしておいてください

tasks: にタスクを記載します
まず name: で実行するタスクの名前を指定します
ここには自然言語で好きな名前を指定することができます
そして実際に実行するコマンドを記載します
今回は command: というデフォルトで用意されているモジュールを使いました
これは指定されたコマンドを実行するモジュールになります
chef でいうところの executebash リソースみたいな感じです

記載できたら実行してみましょう

  • ansible-playbook sample_pb.yml

で実行できます
実行するとなぜか cowsay が結果を表示してくれます

 ____________ 
< PLAY [all] >
 ------------ 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


 _________________ 
< GATHERING FACTS >
 ----------------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


ok: [192.168.1.10]
 _______________________________ 
< TASK: This is a test playbook >
 ------------------------------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||



changed: [192.168.1.10]
 _________________________________ 
< TASK: This is a test playbook 2 >
 --------------------------------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


changed: [192.168.1.10]
 ____________ 
< PLAY RECAP >
 ------------ 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


192.168.1.10              : ok=3    changed=2    unreachable=0    failed=0

結果が unreachable や failed にならなければ問題なく実行できたことになります
command: だと毎回 changed になってしまうので、changed になるのが嫌な場合は changed_when というモジュールを使って制御するみたいです

最後に

とりあえず今回は以上です
触ってみた感覚だと確かに chef よりは簡単に書ける感じはありました
ただ、ansible も大規模になると attributes や template, file, environment みたいな概念が出てくるんじゃないかなと思いました
chef は Rack タスクなので Ruby のコードもガンガン書けましたが、Ansible は YAML なので、ちょっと工夫しないと Python は使えない感じでした

あと、ちょっと今後引き続き調査したいなと思う点をメモしておきます

  • 実行したコマンドの結果を cowsay が表示してくれないか
  • 鍵認証で使うことはできないか
  • Python と組み合わせて使う

たぶんと言うか絶対全部実現できると思いますが、自分的に未調査なので

Tips

  • ID/PW を入力して実行する方法
    • ansible all -m ping --ask-pass
  • 全ホストに任意のコマンドを実行する方法
    • ansible all -a ‘hostname’
  • cowsay を抑制する方法
    • export ANSIBLE_NOCOWS=yes
  • cowsay をランダムの動物にする方法
    • export ANSIBLE_COW_SELECTION=random

CentOS6 で Ansible2 をインストールする方法

概要

yum でインストールしている場合、Ansible1.9 がインストールされます
いろいろと 2.0 から変わっているので 2.0 用に作成された playbook を 1.9 で動作させることができないのでアップグレードしてみました

環境

  • CentOS 6.7 Final
  • Python 2.6
  • Pip 8.0.3
  • Ansible 1.9.3 -> 2.0.1.0

既存の Ansible をアンインストール

  • yum remove ansible

Ansible 2.0 をインストール

  • yum -y install python-devel
  • pip install ansible
  • pip install pycrypto==2.3

Ansible を使うには pycrypto が一緒に必要になります
pycrypto をインストールするのに python-devel が必要になるのでまずインストールします
そして、ansible2.0 が無事インストールできたら pycrypto を 2.3 にダウングレードします
自分の場合最新版の 2.6 がインストールできたのですが、どうやら 2.4 以上のバージョンだと

AttributeError: ‘module’ object has no attribute ‘HAVE_DECL_MPZ_POWM_SEC’

というエラーが発生しうまく動作しないようです

$ ansible --version
ansible 2.0.1.0

無事インストールできてバージョンが表示されれば OK

Tips

遭遇したエラーなどを記載しておきます

ERROR: [DEPRECATED]: include + with_items is a removed deprecated feature (in /root/Documents/work/mqtt_confirm/ansible/roles/mosquitto/tasks/main.yml). Please update your playbooks.

これは 2.0 にアップグレードしたら発生しなくなります

Rolling back uninstall of pycrypto
Command “/usr/bin/python -u -c “import setuptools, tokenize;file=’/tmp/pip-build-5_ybnn/pycrypto/setup.py’;exec(compile
(getattr(tokenize, ‘open’, open)(file).read().replace(‘\r\n’, ‘\n’), file, ‘exec’))” install –record /tmp/pip-XsPI
zH-record/install-record.txt –single-version-externally-managed –compile” failed with error code 1 in /tmp/pip-build-5_yb
nn/pycrypto

これは python-devel をインストールすれば解決します