プロジェクト

全般

プロフィール

Ansible

LinuxおよびWindowsの環境構築を自動化(スクリプト化)するツールです。複数台の計算機の環境を構築するとか、同じ計算機を繰り返し構築するとかの際に重宝します。類似ツールにはChefとかPuppetがあります。Ansibleの特徴はエージェント不要、すなわち構築対象となる計算機にはAnsibleのソフトウェアを入れなくてもよい点です。

主な構成と必要なツール

設定をされる管理ノード(Management Node)と設定をする制御マシン(Control Machine)から構成されます。

制御マシン(Unix) ーーー> 管理ノード(Unix)
SSH
ーーー> 管理ノード(Windows)
WinRM

バージョン

Ansible 2.1

2016年5月にリリースされたばーじょんです。

  • Windows対応、Microsoft Azure対応およびDocker対応の強化
  • ネットワーク機器への対応

Ansible 2.0

2016年1月にリリースされたバージョンです。

  • 制御マシン(Unix)
    Python 2.6または2.7、SSH接続、およびAnsibleソフトウェア(Pythonモジュール)が必要です。
    管理ノード(Windows)を制御する場合は、Python winrmモジュールが追加で必要です。
    WinRMの接続認証にActive Directoryのドメインユーザーを使う場合は、Kerberos認証ができるOSパッケージおよびPythonモジュールが必要です。
  • 管理ノード(Unix)
    Python 2.x(2.4以降)とSSH接続が可能であれば構成できるので、現行のLinuxディストリビューションであればOS標準パッケージだけで利用可能です。
  • 管理ノード(Windows)
    PowerShell 3.0以降

Ansible制御マシン側の設定

SSH接続のエラー(Host fingerprint)

SSH初回接続では、known_hostsにフィンガープリントが登録されていないのでエラーになります。


$ ansible -i hosts -k -m command -a "date" all
SSH password: **
192.168.1.15 | FAILED => Using a SSH password instead of a key is not possible because
Host Key checking is enabled and sshpass does not support this. Please add this host's
fingerprint to your known_hosts file to manage this host.

一度手動でssh接続をしておくのが常套ですが、そうしたくない場合は次のいずれかの回避策を取ります。

  • 環境変数ANSIBLE_SSH_ARGSに-o StrictHostKeyChecking=noを追加
  • 環境変数ANSIBLE_HOST_KEY_CHECKINGにFalseを設定
  • ansible.cfgファイル(/etc/ansible/下に置く)に以下を記述
    [defaults]
    host_key_checking = False
    
  • known_hostsモジュールを実行してknown_hostsファイルから指定のホストのフィンガープリント情報を追加または削除

known_hostsにフィンガープリントが登録されており、そのホストを再構築するなどしてフィンガープリントが変わってしまった場合、host_key_checking = Falseにしていてもダメなようです。

Windowsを管理ノードとするときの制御ホスト側の設定

  • python pywinrmモジュール(とその依存モジュール群)
  • python kerberosモジュール(とその依存モジュール群)
    ※ kerberosモジュールはActive DirectoryのドメインユーザーをWinRM接続のアカウントに使用する場合に必要

Windowsを制御対象とするときは、通信にSSHではなくWinRM(Windows Remote Management)を使用します。
その際、Pythonライブラリ pywinrm をインストールする必要があります。

~$ pip install pywinrm

CentOS 6でpywinrpmを作成する作業をチケット[#53]で実施しています。

ドメインユーザーで接続する場合

ドメインユーザーで接続する場合の接続指定は次のようになります。

[windows:vars]
ansible_user=foobar@bravo.local
ansible_connection=winrm
  • ansible_userに、'@'を含む文字列を指定した場合、Kerberos認証で接続します。
    ドメインユーザーをansibleの接続に使用するときは、Kerberos認証となります。Kerberos認証で接続するときは、保有しているデフォルトプリンシパルのチケットを使用します。チケットは、ansibleを実行する前に取得しておく必要があります。取得には、kinitコマンド(引数にドメインユーザー名を指定)を使います。
  • -vvvvvオプション(vを5個以上)で詳細メッセージを出すと、次のメッセージでKerberos認証が使われているか確認できます。
    <192.168.1.111> WINRM CONNECT: transport=kerberos endpoint=https://192.168.1.111:5986/wsman
    transport= kerberos がKerberos認証を使っていることを示します。ここが、transport= ssl だとKerberosではなくパスワード認証になっていることを示します。

現在保有しているチケットの確認には、klistコマンドを使います。

~$ klist
klist: Credentials cache file '/tmp/krb5cc_12345' not found

  • これは空です。LinuxのKerberosパッケージは、チケットを/tmp/krb5cc_<ユーザーID>に置くようです。
  • Java SE Development Kit(JDK)をインストールしていて、JDKのコマンドへパスを通していると、JKDにもklistがあるので要注意です。

チケットの取得(レルム名は大文字で指定します。Kerberosのレルム名は大文字・小文字を区別します。原則大文字)

~$ kinit foobar@BRAVO.LOCAL
Password for foobar@BRAVO.LOCAL: ********

~$ klist
Ticket cache: FILE:/tmp/krb5cc_12345
Default principal: foobar@BRAVO.LOCAL

Valid starting       Expires              Service principal
2016-02-07T21:04:55  2016-02-08T07:04:55 

Kerberos認証でチケットを保有せずに実行した場合のエラー

TASK [setup] *******************************************************************
fatal: [turner]: FAILED! => {"failed": true, "msg": "ERROR! ssl: 500 WinRMTransport.
 [Errno 8] Name or service not known"}

ユーザーの指定は、アカウント名@ドメイン名 です。

python 2.7.10以降でWindows管理ノードとWinRM通信するときの自己署名証明書対応設定

WinRMは、標準では自己署名証明書を使ったHTTPSプロトコルを使います。
Python 2.7.10以降のバージョンを使う場合、SSLで自己署名証明書は認証エラーとされるようになったため、対応が別途必要になります。(Cygwin 64bit版の場合、python 2.7.10なので該当。CentOS 7.1はpython 2.7.5なので非該当)

CentOS 7.1 Python 2.7.5での接続確認

最初、公式サイトのドキュメントを参照してインベントリに次の記述をします。
http://docs.ansible.com/ansible/intro_windows.html

[windows]
winhost

[windows:vars]
ansible_user: winaccount
ansible_password: WinAcc0unt
ansible_connection: winrm

Windows機を指定してansible 1.9.4を実行したところ、エラーとなりました。

$ ansible winhost -i hosts -m setup
winhost | FAILED => 401 Unauthorized.

公式サイトのドキュメントは Ansible 2.0版での設定なので、1.9.4ではインベントリの変数定義を次に変更してみました。

  [windows:vars]
- ansible_user: winaccount
- ansible_password: WinAcc0unt
+ ansible_ssh_uesr: winaccount
+ ansible_ssh_pass: WinAcc0unt
  ansible_connection: winrm

$ ansible winhost -i hosts -m setup
winhost | success >> {
    "ansible_facts": {
        "ansible_distribution": "Microsoft Windows NT 6.1.7601 Service Pack 1",
    :
Cygwin 7.1 Python 2.7.10での接続確認
$ ansible winhost -m setup
winhost | FAILED => 500 WinRMTransport. [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)

HTTPSプロトコルを使ってWinRM接続をする際、Python 2.7.9以降SSL証明書の検証をちゃんとするようになったらしく、SSLサーバー側(この場合はWindows)が自己証明書の場合エラーになってしまうようです。

Ansible 2.0であれば、接続設定に次を追記します。

ansible_winrm_server_cert_validation=ignore

  • 追記場所は、ansible_connection=winrm他を指定している場所がよいかと

それ以外はちょっと設定や細工が必要です。

  1. 回避策1は、HTTPプロトコルで通信
  2. 回避策2は、ansibleのwinrmプラグインの書き換え
  3. 回避策3は、コールバックの作成
  4. 回避策4は、sitecustomize.py(またはusercustomize.py)に追加コード記載
回避策1

HTTPプロトコルを使うには(ConfigureRemotingForAnsible.ps1ではHTTPを許可していない)、次のコマンドをPowerShellで実行します。

PS > winrm set winrm/config/service '@{AllowUnencrypted="true"}'

設定結果は次のコマンドで確認します。

PS > winrm get winrm/config
  :
      AllowUnencrypted = true
  :

インベントリで接続ポートをHTTPで使用する5985を明示的に指定します(デフォルトはHTTPSの5896)。

[windows:vars]
ansible_ssh_uesr: winaccount
ansible_ssh_pass: WinAcc0unt
ansible_ssh_port=5985
ansible_connection=winrm
回避策2

Cygwin 2.3.1 64bit版で、python 2.7.10、Ansible 1.9.4 の場合、次のパスにあるファイルを修正します。/usr/lib/python2.7/site-packages/ansible/runner/connection_plugins/winrm.py

--- winrm.py.orig       2015-12-20 22:38:06.000000000 +0900
+++ winrm.py    2015-12-25 00:02:04.139133000 +0900
@@ -74,6 +74,8 @@
         '''
         Establish a WinRM connection over HTTP/HTTPS.
         '''
+        import ssl
+        ssl._create_default_https_context = ssl._create_unverified_context
         port = self.port or 5986
         vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \
             (self.user, port, self.host), host=self.host)
  • Ansible 2.0ではうまく入れられなかったので他の回避策を取るのがよいかと
回避策3

省略

回避策4

参考: https://github.com/ansible/ansible/issues/10294

sitecustomize.py もしくはusercustomize.pyに次のコードを追記(ファイルがなければ新規作成)します。

/usr/lib/python-2.7/site-packages/sitecustomize.py

import ssl

try:
    _create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
    # Legacy Python that doesn't verify HTTPS certificates by default
    pass
else:
    # Handle target environment that doesn't support HTTPS verification
    ssl._create_default_https_context = _create_unverified_https_context
  • cygwinのpythonの場合、Windows側から見たディレクトリは、<cygwinインストールパス>\lib\python-2.7\site-packages\sitecustomize.py
  • usercustomize.pyを置く場所は、pythonコードを実行して確認します
    >>> import site
    >>> userdir = site.getusersitepackages()
    >>> print userdir
    

    で表示されたパスになります。

Ansible管理ノード(制御される側)の設定

条件

Linux系OSでAnsible制御を受ける条件

  • Python 2.6または2.7がインストールされていること
  • SSHで制御側からSSHで接続できること
  • SSHで接続したユーザーアカウントでsudoができること
    → 初期設定をAnsibleからrootユーザーで接続することも可能
  • SELinuxが稼動している(EnforcedまたはPermissive)場合は、libselinux-pythonがインストールされていること

Windows系OSでAnsible制御を受ける条件

  • PowerShell 3.0以上がインストールされていること
  • Windows Remote Management(WinRM)で制御側から接続できること
  • PowerShellスクリプトの実行が許可されていること

Windows OS(全般)

Windows OSは、SSHを標準装備していないので、AnsibleではWindow Remote Management(WinRM)を使用します。また、PowerShell 3.0以上を必要とします。これは、Windows 8以降、およびWindows Server 2012以降では標準装備です。Windows 7やServer 2008などは、別途MicrosoftからWindows Management Framework(WinRMとPowerShellを含む)をダウンロードしインストールします。

http://docs.ansible.com/ansible/intro_windows.html

  • WMF 3.0はhotfix2842230を当てる必要あり

WinRM 4.0のインストール(Windows 7, 2008)

Windows Management Framework 4.0をダウンロードします(次のURL)。これには、PowerShell、WinRMなどが含まれます。
https://www.microsoft.com/ja-jp/download/details.aspx?id=40855
Windows 7 64bit用のファイルは次です。
Windows6.1-KB2819745-x64-MultiPkg.msu

WMF4.0は、.NET Framework 4.5を必要としています。Windows 7、Windows Server 2008は、OSの標準搭載.NET Frameworkが3.5と古いので、他のアプリケーションと一緒に.NET Framework 4.5以上がインストールされていない場合、先にインストールしておく必要があります。次の記事の中にあるリンクからダウンロードできます。
https://msdn.microsoft.com/ja-jp/library/5a4x27ek%28v=vs.110%29.aspx

WMF4.0インストーラを実行すると更新プログラムとしてインストールされます。
インストール後再起動を促されるので、再起動してからPowerShellを起動しバージョンを確認します。

PS >  $PSVersionTable

Name                           Value
----                           -----
PSVersion                      4.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34209
BuildVersion                   6.3.9600.16406
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion      2.2

Windows準備作業

WindowsでWinRMを使ってAnsibleの制御を受け入れるために、準備スクリプトをダウンロードし実行します。
https://github.com/ansible/ansible/raw/devel/examples/scripts/ConfigureRemotingForAnsible.ps1

PowerShellスクリプトを管理者権限で実行します。

エラー(デジタル署名されていません。このスクリプトは現在のシステムでは実行できません)

設定ではRemoteSignedにしています。

PS > Get-ExecutionPolicy
RemoteSigned
PS > .\ConfigureRemotingForAnsible.ps1
.\ConfigureRemotingForAnsible.ps1 : ファイル C:\users\torutk\Documents\ConfigureRemotingForAnsible.ps1 を読み込めません。
ファイル C:\users\torutk\Documents\ConfigureRemotingForAnsible.ps1 はデジタル署名されていません。このスクリプトは現在の
システムでは実行できません。スクリプトの実行および実行ポリシーの設定の詳細については、「about_Execution_Policies」
(http://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。
発生場所 行:1 文字:1
+ .\ConfigureRemotingForAnsible.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

これは、ブラウザでダウンロードしたスクリプトファイルを実行したときに発生しました。
ブラウザでダウンロードしたファイルには「ブロック」がかかっていることがあり、その場合、エクスプローラからファイルのプロパティ > 全般 > [ブロックの解除]をする必要があります。

エラー(Windowsファイアウォールサービスとの通信中にエラーが発生)
PS > powershell -ExecutionPolicy RemoteSigned .\ConfigureRemotingForAnsible.ps1
WinRM は要求を受信するように更新されました。
WinRM サービスの種類を正しく変更できました。

WinRM はリモート管理用に更新されました。
このコンピューター上のあらゆる IP への WS-Man 要求を受け付けるため、HTTP://* 上に WinRM リスナーを作成しました。
ローカル ユーザーにリモートで管理権限を付与するよう LocalAccountTokenFilterPolicy を構成しました。

wxf                 : http://schemas.xmlsoap.org/ws/2004/09/transfer
a                   : http://schemas.xmlsoap.org/ws/2004/08/addressing
w                   : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
lang                : ja-JP
Address             : http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
ReferenceParameters : ReferenceParameters

Windows ファイアウォール サービスとの通信中にエラーが発生しました。このサービスが実行されていることを確認して、もう一度
試してください。

PS >

これを実行したPCには、市販のアンチウィルスソフトをインストールしており、Windowsファイアウォールを停止しています(アンチウィルス独自のファイアウォールを使うため)。そのため、このエラーが発生しています。しかし、このままansibleから接続してみたところ、問題なく動作していました。

playbookを作る

YAMLの書き方メモ

  • (1) ファイル名の拡張子.yml
  • (2) UTF-8で記述
  • (3) 2空白インデント
  • (4) 全てのYAMLファイルはリストで開始すること(Ansibleの制約)
  • (5) 全てのYAMLファイルは'---'で開始すること(1つのファイルに複数YAMLを記載するときの区切り子)
    • インクルードされるYAMLファイルには不要(roles下のmain.yml等)
  • (6) 変数は、{{ varname }} と2重波括弧と変数名の前後に空白を入れる
    ---
    - hosts: clients
      tasks:
      - name: myappの更新
        yum: name="myapp" state=latest update_cache=yes
    
    
  • (7)長い行の折り返し(改行)方法はいくつかある
    • 普通に改行
        - name: long line break
          manyoptionmodule: firstOption="foo bar bz" 
            secondOption="Thomas Percy James Henry Gordon" 
            thridOption="river lake sea" 
      
    • YAML折り返し('>'記号)
        - name: long line break
          manyoptionmodule: >
            firstOption="foo bar bz" secondOption="Thomas Percy James Henry Gordon" 
            thridOption="river lake sea" 
      
    • ディクショナリ形式
        - name: long line break
          manyoptionmodule:
            firstOption: "foo bar bz" 
            secondOption: "Thomas Percy James Henry Gordon" 
            thridOption: "river lake sea" 
      

ファイル分割

次の構造をちらほら見かけます。

root
  +-- roles
  |     +-- foo.yml
  |     +-- bar.yml
  +-- hosts
  +-- site.yml

ベストプラクティス構造

1つのymlファイルに全てを記述すると、長くなって把握性、再利用性、確認容易性が悪くなるので、分割して、また可変部を変数等に括り出す構造のお勧め構造があります。

root/
  +-- group_vars/    # グループ毎に定義する変数設定ファイルを収容
  |     +-- blues    #   bluesグループ用の変数設定ファイル
  |     +-- reds     #   redsグループ用の変数設定ファイル
  |
  +-- host_vars/          # ホスト毎に定義する変数設定ファイルを収容
  |     +-- ultramarine   #   ultramarine用の変数設定ファイル
  |     +-- pink          #   pink用の変数設定ファイル
  |
  +-- production    # 本番環境用inventoryファイル
  +-- staging       # 検証環境用inventoryファイル
  |
  +-- site.yml      # site全体用 playbookファイル
  +-- blues.yml     # bluesに対するplaybookファイル
  +-- reds.yml      # redsに対するplaybookファイル
  |
  +-- roles/          # ロール(タスクを目的毎に束ねた単位)を収容
        +-- alfa/     #   alfa作業を収容
        |     +-- tasks/
        |     |     +-- main.yml    # alfa作業のタスク群を定義したファイル
        |     +-- files/
        |     |     +-- alfa.conf   # alfa作業で使用する(対象ホストに送り込む)ファイル等
        |     +-- templates/
        |     |     +-- alfa.ini.j2 # alfa作業で使用するテンプレート定義ファイル
        |     +-- handlers/
        |     |     +-- main.yml    # ハンドラータスクを定義したファイル
        |     +-- defaults/         # 変数のデフォルト設定用
        |     |     +-- main.yml
        |     +-- meta/             # メタ情報用
        |     |     +-- main.yml
        |     +-- vars/             # 変数用
        |           +-- main.yml
        |
        +-- bravo/
        :      :

単一ファイル構成

作り捨て的な作業などで、ベストプラクティス構造を作るのはちょっとなぁ…というときには、一時作業用の単一ファイルで作って実行してもよいかと思います。一時作業といってもplaybookファイルを作って実行し、それを保管しておくと、後日流用したりするのが楽です。

root
  +-- shortshorts
        +-- foo.xml
        +-- bar.xml
        :     :
        +-- files/
              +-- foo.conf
              +-- var.sh
              :     :

モジュール

commandかshellか

Linuxでコマンドを実行するモジュールには、commandとshellがあります。
違いは、commandの場合、shellインタプリタが解釈する機能(環境変数の展開、リダイレクト、パイプ、バックグラウンド実行)が使えません。

GATHERING FACTをスキップして実行時間を短縮

デフォルトではplaybookを実行すると最初に対象ホストすべてに情報を問い合わせます。
この各ホストから取得した情報を使うことがなければ、問い合わせ処理をスキップすることでplaybookの実行を短縮できます。その場合は、gather_facts: noを記述します。

---
- hosts: all
  become: yes
  gather_facts: no
  roles:

逆引き

タスク

被制御側マシン上で特定のユーザーとして処理を実行したい

sudo: yesは、root権限で処理を実行します。rootではなく特定のユーザーで処理を実行したい場合、かつSSH接続するユーザーとは別の場合は次の記述をします。

Ansible 1.9より古いバージョン
  sudo: yes
  sudo_uesr: apache
Ansible 1.9以降
  become: yes
  become_uesr: apache

制御側マシンにあるrpmファイルを被制御側に転送してからインストールしたい

タスクを定義するファイルとRPMファイルを配置します。

some_ansible
  +-- roles
        +-- hello
              +-- tasks
              |     +-- main.yml
              +-- files
                    +-- hello-0.2.4-6.el7.x86_64.rpm

タスクの定義で、copyしてからローカルファイルシステムのパスを指定してyumを実行します。

- name: RPMを対象へコピー
  copy: src=hello-0.2.4-6.el7.x86_64.rpm dest=/tmp/hello-0.2.4-6.el7.x86_64.rpm

- name: RPMをインストール
  sudo: yes
  yum: name=/tmp/hello-0.2.4-6.el7.x86_64.rpm

URLで指定したRPMをインストールしたい

- name: RPMをインストール
  sudo: yes
  yum: name=http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
       state=present

シンボリックリンクを作成したい

fileモジュールが提供されています。

- name: シンボリックリンクの作成
  file: src=/var/lib/redmine-3.1.1
        dest=/var/lib/redmine
        force=yes
        state=link

ホスト名を設定したい

hostnameモジュールが提供されています。

- name: ホスト名を変更する
  sudo: yes
  hostname: name=oscar01

inventoryファイルで定義したホスト名に設定する場合は、hostname: name={{ inventory_hostname }}で指定できます。
FQDNでホスト名を設定する場合も、hostname: name={{ inventory_hostname }}.mydomainで指定できます。

- name: ホスト名を変更する
  sudo: yes
  hostname: name={{ inventory_hostname }}.example.com

/etc/hostsにホスト名とIPアドレスを設定したい

localhostと自ホストだけを定義した/etc/hostsを作成する場合は、自ホスト名とIPアドレスをテンプレートに埋め込む方法が使えます。

- name: /etc/hsotsを設置する
  sudo: yes
  template: src=templates/hosts.j2 dest=/etc/hosts

templateモジュールでテンプレートとなるhostsファイルを指定します。テンプレートは次のようになります。

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
{{ ansible_default_ipv4.address }} {{ inventory_hostname }}.mydomain {{ inventory_hostname }}

ここで、ansible_default_ipv4.address は、"gather fact(setup)"で収集されるホスト情報に含まれます。

$ ansible oscar01 -i ./hosts -m setup
oscar01 | SUCCESS => {
    "ansible_facts": {
        :(中略)
        "ansible_default_ipv4": {
            "address": "192.168.1.11", 
            "alias": "eth0", 
            "broadcast": "192.168.1.255", 
            "gateway": "192.168.1.254", 
            "interface": "eth0", 
            "macaddress": "52:54:00:ab:cd:ef", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.1.0", 
            "type": "ether" 
        }, 
        :(後略)

サービスの自動起動設定、起動をさせたい

- name: MariaDB 起動・自動起動設定
  sudo: yes
  service: name=mariadb state=started enabled=yes

Linux上にグループを作成したい

- name: redmineグループの作成
  sudo: yes
  group: name=redmine gid=1000

Linux上にユーザーを作成したい

- name: redmineユーザーの作成
  sudo: yes
  user: name=redmine uid=1000 group=redmine
        password=$6$rounds=656000$g0iZb...(中略)...EFsbxk/KVBZUIM8rXRUpms0

パスワードは、あらかじめSHA-512でハッシュしたものをここに指定しています。作成方法はansibleのドキュメントに記載(次のURL)されています。
http://docs.ansible.com/ansible/faq.html#how-do-i-generate-crypted-passwords-for-the-user-module

ハッシュの生成には、mkpasswdコマンドか、pythonのプログラム(passlibを使う)が使えます。なお、Cygwinにはmkpasswdがありますが、--methodオプションがなく、本目的のものとは違うようです。

そこで、pythonのプログラムで作成します。プログラムといっても、ワンライナーで実行します。
pip install passlibでpasslibモジュールをインストールして、次のハッシュを作成します。

~$ python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt.encrypt('パスワード')" 

gitのクローンを作成したい

- name: farend_basicテーマのダウンロード
  sudo: yes
  sudo_user: redmine
  git: repo=https://github.com/farend/redmine_theme_farend_basic.git
       dest={{ redmine_dir }}/public/themes/farend_basic

カレントディレクトリと環境変数を設定してコマンドを実行したい

- name: secret tokenの作成
  sudo: yes
  sudo_user: redmine
  command: bundle exec rake generate_secret_token
           chdir={{ redmine_dir }}
  environment:
    RAILS_ENV: production

MySQLの設定に初回インストール時と2回目以降で冪等性を持たせたい

MySQLは初期インストールではrootユーザーがパスワードなしとなっています。mysql_userやmysql_dbなどのモジュールは、接続ユーザー/パスワードの指定(login_user/login_password)がない場合は、my.cnfの認証設定を使用し、それがなければrootユーザーでパスワードなしを使用します。

そのため、rootユーザーにパスワードを設定する次のタスクは、MySQLをインストールした直後の1回目は成功しますが、2回目以降は失敗します。

- name: rootパスワード設定
  mysql_user: name=root
              password={{ db_passwd_root }}

この記述を持つplaybookを実行すると2回目以降で次のエラーが発生します。

failed: [foohost] => {"failed": true}
msg: unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials

冪等性を持たせるには、次のように記述します。

- name: rootパスワード設定
  mysql_user: name=root
              password={{ db_passwd_root }}
              login_user=root
              login_password={{ db_password_root }}
              check_implicit_admin=yes

login_userとlogin_passwordは、MySQLへ接続する認証で、1回目はrootにパスワードが設定されていないのでこの認証はエラーになります。
しかし、check_implicit_admin=yesを設定しておくと、認証に先立ちパスワード無しrootでの接続を試みます。そのため、1回目の設定が可能になります。

設定ファイルの一部修正

対象ホストの設定ファイルを一部修正する場合、状況によってlineinfileモジュール、replaceモジュール、ini_fileモジュールを使うか、commandあるいはshellモジュールでUNIXコマンドレベルで実施するといった方法があります。

lineinfileモジュールは、1行追加、1行置換のどちらかを行います。with_itemsと組み合わせることで複数行の置換も可能です。

次に、/etc/chrony.conf でNTPサーバー設定行を次のように書き換える例を示します。

- server 0.fedora.pool.ntp.org iburst
- server 1.fedora.pool.ntp.org iburst
- server 2.fedora.pool.ntp.org iburst
- server 3.fedora.pool.ntp.org iburst
+ server ntp.nict.jp
+ server ntp1.jst.mfeed.ad.jp
+ server ntp2.jst.mfeed.ad.jp
+ server ntp3.jst.mfeed.ad.jp

これをlineinfileモジュールで修正する場合の例を次に示します。

    - name: chrony設定
      lineinfile: backrefs=yes
                  dest='/etc/chrony.conf'
                  regexp='{{ item.regexp }}'
                  line='{{ item.line }}'
      with_items:
        # nict ntp server
        - regexp: '^server 0.centos.*'
          line: 'server ntp.nict.jp'
        - regexp: '^server 1.centos.*'
          line: 'server ntp1.jst.mfeed.ad.jp'
        - regexp: '^server 2.centos.*'
          line: 'server ntp2.jst.mfeed.ad.jp'
        - regexp: '^server 3.centos.*'
          line: 'server ntp3.jst.mfeed.ad.jp'
  • backrefsを指定すると、正規表現にマッチしない場合は変更なしと判断されます。backrefsを指定しないと、実際には修正をしなくても毎回変更ありとなってしまいます。
  • with_itemsでは、マップ形式でregexとlineの組合せを定義します。
  • バックアップを残したい場合は、backup=yesを設定します。

正規表現に合致した行を、置換ではなく削除したい場合は、lineinfileモジュールでregexとstate=absentを指定します。

    - name: chrony設定(2)
      lineinfile: backrefs=yes
                  dest='/etc/chrony.conf'
                  regexp='^server [123].centos.*'
                  state=absent

または、replaceモジュールでregexpを指定し、replaceを省略します。

    - name: chrony設定(2)
      replace: dest='/etc/chrony.conf'
               regexp='^server [123].centos.*'

特定のホストグループの場合にモジュールを実行したい

インベントリファイルに'ntpservers'グループで定義したホストの場合にサービスnfsを起動・自動起動設定する場合の例を示します。

  - name: NFSサーバーのサービス起動
    service: name=nfs state=started enabled=yes
    when: "'ntpservers' in group_names" 

複数のRPMパッケージを1つのタスクでまとめてインストールしたい(with_items)

  - name: 
    yum: name={{ item }} state=latest update_cache=yes
    with_items:
      - acpid
      - openssh-clients
      - ntp

ハンドラー

あるタスクが実行(changed)されたら実行したい

設定ファイルに変更があればサービスを再起動するとします。

  tasks:
    - name: httpd.confのコピー
      copy: src=files/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd

    - name: httpdのコピー
      copy: src=files/httpd dest=/etc/sysconfig/httpd
      notify: restart httpd

  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

各タスクで、notifyに通知先タスク名を記述しておきます。各タスクの実行結果がchangedであれば、notifyで記述した通知先タスクが実行されます。
handlersは、tasksの処理が全て終わったあとに実行されるので、複数のタスクでnotifyが通知されても1回だけ実行します。

インベントリ

ホスト名とIPアドレスをインベントリに記述する

ホスト名を変数として設定に使用したいが、ホスト名からIPアドレス解決を外部的に持たない場合、インベントリにホスト名とIPアドレスの定義を一緒に記述することができます。

Ansible 2.0未満
[tango]
tango1 ansible_ssh_host=192.168.1.131
Ansible 2.0以降
[tango]
tango1 ansible_host=192.168.1.131

ワンライナー

パスワード指定で各マシンに接続してコマンドを実行する

Ansibleは、インベントリファイルに記述がないホストへは接続しないので、インベントリファイルを用意します。例:inventories

alfa
bravo
charlie
delta
echo
foxtrot
golf

指定したインベントリファイルに記載の対象ホスト(allはすべて)、接続するユーザー名とコマンドを指定し、パスワードを入力して実行します。

~$ ansible all -i inventories -u root -k -m command -a "uname -a" 
SSH password: ********
alfa | success | rc=0 >>
Linux alfa 2.6.32-504.1.3.el6.x86_64 #1 SMP Tue Nov 11 17:57:25 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

bravo | success | rc=0 >>
Linux bravo 2.6.32-504.8.1.el6.x86_64 #1 SMP Wed Jan 28 21:11:36 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

charlie | success | rc=0 >>
Linux charlie 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

 :(略)
  • all インベントリファイルのホスト全てを指定
  • -i inventories インベントリファイルにinventoriesを指定(省略時は/etc/ansible/hostsを使用)
  • -u root rootユーザーで接続を指定
  • -k ユーザー認証はパスワード認証で、ansibleコマンド実行時に対話的に入力する指定
  • -m command commandモジュールを実行
  • -a "uname -a" モジュールに渡す引数

指定したインベントリファイルに記載の対象ホスト(allはすべて)と、ansibleを実行しているユーザーで接続し、sudo でコマンドを実行、パスワードを入力して実行します。

~$ ansible all -i inventories -s -K -m command -a "uname -a" 
SUDO password: ********
  :
  • -s sudo で特権実行
  • -K sudo のパスワードをansibleコマンド実行時に対話的に入力する指定

指定したインベントリファイルに記載の対象ホストと、ansibleを実行しているユーザーで接続し、su でコマンドを実行するユーザーを指定、パスワードを入力して実行します。

~$ ansible redmine_servers -i inventories -U redmine -k -m command -a "tail -1 /var/lib/redmine/log/production.log" 
SUDO password: ********
  :

ロール

ロールにタグを付け、指定したタグのロールのみ実行あるいはスキップ

playbookファイルのロールについてタグを指定します。ロール名と同じ名前でタグを付けるとよいでしょう。

---
- hosts: redmine-servers
  become: yes
  roles:
    - { role: admin, tags: admin }
    - role: system
      tags: system

ansible-playbookコマンドのオプション-t タグ名で実行するロールを指定します。
--skip-tags=タグ名でスキップするロールを指定します。

$ ansbile-playbook site.yml -t "unicorn,nginx" redmine-servers

実行環境

メッセージの色を見やすいように変更する

暗色系の背景でansibleを実行すると、詳細メッセージが青でコントラストが小さく文字が読みにくいことがあります。
/etc/ansible/ansible.cfg で色設定を変更できないか試してみましたが、だめなようです。

デフォルトのカラー設定

部位 定義色名 ANSI設定 備考
詳細メッセージ(verbose) blue 暗色系背景では視認性悪い
警告メッセージ(warn) bright purple
エラーメッセージ(error) red
デバッグメッセージ(debug) dark gray
非推奨メッセージ(deprecate) purple
スキップメッセージ(skip) cyan
到達できず(unreachable) red
OK(ok) green
変更あり(changed) yellow
差異の追加(diff add) green
差異の削除(diff remove) red
差異の行(diff lines) cyan
codeCodes = {
    'black':     u'0;30', 'bright gray':    u'0;37',
    'blue':      u'0;34', 'white':          u'1;37',
    'green':     u'0;32', 'bright blue':    u'1;34',
    'cyan':      u'0;36', 'bright green':   u'1;32',
    'red':       u'0;31', 'bright cyan':    u'1;36',
    'purple':    u'0;35', 'bright red':     u'1;31',
    'yellow':    u'0;33', 'bright purple':  u'1;35',
    'dark gray': u'1;30', 'bright yellow':  u'1;33',
    'normal':    u'0'
}

そこでメッセージを出力している箇所を直接調べてみると、

  • /usr/lib/python2.7/site-packages/ansible/utils/display.py
    def verbose(self, msg, host=None, caplevel=2):
        # FIXME: this needs to be implemented
        #msg = utils.sanitize_output(msg)
        if self.verbosity > caplevel:
            if host is None:
-               self.display(msg, color='blue')
+               self.display(msg, color='bright gray')
            else:
-               self.display("<%s> %s" % (host, msg), color='blue', screen_only=True)
+               self.display("<%s> %s" % (host, msg), color='bright gray', screen_only=True)

トラブルシュート

接続

YAML

参考情報

インターネット公開資料

Ansible公式サイト

書籍

邦訳・和書


約8年前に更新