Help us understand the problem. What is going on with this article?

[随時更新]ansibleでよく使うコマンド・モジュールまとめ

More than 3 years have passed since last update.

はじめに

大体使うモジュールっていつも一緒だったのでまとめてみました。
また、ansibleでスクリプトを作るときのちょっとしたノウハウなども書いていますので参考にして頂ければと思います。

環境

  • ansible 1.9.1~2.2.0.0-1
  • target OS CentOS6.6,RHEL6.6

インストールの仕方

情報源

  • 慣れてくると公式サイトくらいしか見なくなりますので、Module Indexをブックマークしておきます。

コーディング規約

こちらのエントリーが素敵です。
このエントリーをベースにして合わない所、追加が必要なところをgitのwikiに追記してプロジェクトでは利用しています。
Ansible コーディング規約 (の例)

実行方法

通常実行

ansible-playbook -i [インベントリファイル] sites.yml

デバッグ出力しつつ実行

ansible-playbook -vvv -i [インベントリファイル] sites.yml

実行時にパラメータ付与する

  • {{ xxx }}でplaybookで変数取得可能。
  • yumが使える環境か否かを外部から与えてあげて、playbook内で分岐させたりします。
ansible-playbook --extra-vars "xxx=yes" -i [インベントリファイル] sites.yml
ansible-playbook -e "xxx=yes" -i [インベントリファイル] sites.yml

ansible実行するsshポート/ユーザ/パスワード/sudoするパスワードを変更する。

[all:vars]
ansible_port=22
ansible_user=vagrant
ansible_pass=password
ansible_sudo_pass=password

ansible実行するときのユーザの秘密鍵を指定して実行する

ansible-playbook -i [インベントリファイル] sites.yml --private-key=/path/key.pem

タグを指定して実行する

  • タグ名はtagsで指定します。
  • タグの付与を簡単にするため、tasks/main.ymlには処理を書かずplaybookをincludeしてinclude単位でtagを指定するようにすることが多いです。
- include: user.yml tags=user
ansible-playbook -i [インベントリファイル] sites.yml -t user
  • 複数指定するときはカンマ区切りで。
ansible-playbook -i [インベントリファイル] sites.yml -t group,user

前準備

libselinux-python

  • selinuxが無効化されていない状況でcopy,file,templateモジュールを使うとlibselinux-pythonを入れなさい。って怒られる。
  • epelいれて、libselinux-pythonをyumでインストールすれば回避できるが、selinuxを利用しない場合は実行前にselinuxを無効化しておけば良い。

ansibleが取得する値を確認しておく

  • ansible_os_familyの値などずらっと表示してくれます。
ansible -m setup 10.0.1.10

デバッグ

  • デバッグ出力する
- debug: msg="XXXX"

コマンド

コマンドを実行する

  • 20文字の乱数を作る場合
  • ignore_errorsでエラーが発生しても無視する。changed_when: falseとしておくことでコマンド実行時に常にchangedになるのを防ぐ。コマンド実行時にこの2つは常にセットで使うことが多い。
- name: generate passwod salt for new user
  shell: "cat /dev/urandom | tr -dc '[:alnum:]' | head -c 20"
  register: salt
  ignore_errors: true
  changed_when: false

コマンド実行結果を取得する

  • 標準出力を取得する
register_val.stdout
  • 標準出力結果に文字が含まれているかで分岐する。findで-1は存在しない場合を表す。
 when: register_val.stdout.find('test') != -1
  • 標準出力結果を配列で取得する。with_itemsと合わせて使うとループできたりする。
with_items: register_val.stdout_lines
  • コマンドが成功したか、失敗したかで分岐する
 when: register_val | success
 when: register_val | failed

ファイルダウンロード

ファイルダウンロード その1

  • get_urlを使うパターン。
  • 存在チェック&同一か否かも判定してくれるので冪等性が担保される。
vars/main.yml
elasticsearch_configs:
  dl_url: https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.1.noarch.rpm
  rpm_path: /usr/local/src/elasticsearch-1.7.1.noarch.rpm
tasks/elasticsearch.yml
- name: download elasticsearch rpm
  get_url: >
    url={{ elasticsearch_configs.dl_url }}
    dest={{ elasticsearch_configs.rpm_path }}

ファイルダウンロード その2

  • 通常はget_urlを使うが、ヘッダを指定したい場合がある。
  • 冪等性を担保しつつjdkのrpmを取得する場合はこんな感じで実装する。
vars/main.yml
---
jdk:
  dl_url: http://download.oracle.com/otn-pub/java/jdk/8u45-b14/jdk-8u45-linux-x64.rpm
  rpm_path: "/usr/local/src/jdk-8u45-linux-x64.rpm"
tasks/jdk.yml
- name: check java rpm
  stat: path={{ jdk.rpm_path }}
  register: java_exists
- name: wget java
  command: >
    wget -O {{ jdk.rpm_path }} --no-check-certificate --no-cookies
    --header "Cookie: oraclelicense=accept-securebackup-cookie" "{{ jdk.dl_url }}"
  when: java_exists.stat.md5 is not defined

notify

設定ファイルが変更されたタイミングで再起動する。

  • /etc/httpd/httpd-proxy.confが変更されたらapacheを再起動する。
  • notify:の後の文字と、handlers/main.ymlのnameの文字が一致している必要がある。
handlers/main.yml
---
- name: restart httpd
  service: name=httpd state=restarted
tasks/httpd.yml
- name: copy httpd-proxy.conf
  template: src=httpd-proxy.conf dest=/etc/httpd/conf.d/
  notify: restart httpd

リスト

オブジェクトのリスト

  • /etc/servicesの中身をregexp にマッチする行を line で置きかえる
vars/main.yml
services_params:
  - { regexp: "listener 1521/tcp # listner", line: "listener 1521/tcp # listner" }
  - { regexp: "tomcat 8080/tcp #tomcat", line:"tomcat 8080/tcp #tomcat" }
tasks/services.yml
---
- name: add /etc/services
  lineinfile: dest=/etc/services
              state=present
              regexp='^{{ item.regexp }}'
              line='{{ item.line }}'
  with_items: "{{ services_params }}"

テンプレート

jsonファイルを読み込む(lookup)

  • AWS関連のモジュールなどはポリシーをjsonで書く事が多い。
  • これをplaybookにべた書きするのはどうもいけてないのでlookupを使うといい感じ。
  • 引数のconvert_dataというのは\でエスケープするか否かを指定する。
    - name: create-cloudwatch-events
      cloudwatchevent_rule:
        name: "{{ rule_name }}"
        description: "Delete the EBS Snapshot attached at the timing when AMI was deleted"
        state: present
        event_pattern: "{{ lookup('file', './event.json', convert_data=False) |string }}"
        role_arn: "{{ role_arn }}"
        targets:
          - id: "{{ function_name }}"
            arn: "{{ lambda_arn }}"
event.json
{"detail-type":["AWSAPICallviaCloudTrail"],"detail":{"eventSource":["ec2.amazonaws.com"],"eventName":["DeregisterImage"]}}

ansibleで生成したことを残しておく

  • こんな感じでテンプレートに記載しておくと・・・
# {{ ansible_managed }}
  • こんな感じで書かれます。
  • テンプレートに変更があった場合だけ書き込まれます。varsの中身を参照して出力するテンプレートがあった時に、varsの中身が変更されても置き換わりませんでした。
  • 時刻を出しているとnotifyが動くことがあるので注意。(公式ドキュメントにも記載がありましたね)
# Ansible managed: /home/vagrant/ansible-logrotate/roles/logrotate/templates/logrotate.d.j2 modified on 2015-08-09 18:41:11 by vagrant on ansible-host

テンプレートファイルを使ってファイルに反映する。

  • hostsファイルを作成する場合はこんな感じで。
templates/hosts.j2
{% for host in hosts_params %}
# {{ host.comment }}
{{ host.ipaddr }} {{ host.name }} {{ host.alias }}
{% endfor %}
vars/main.yml
---
hosts_params:
  - ipaddr: 127.0.0.1
    name: web1
    alias: localhost localhost.localdomain
    comment: localhost
  - ipaddr: ::1
    name: localhost
    alias: localhost localdomain
    comment: localhost
  - ipaddr: 192.168.1.20
    name: test.web1
    alias: webalias
    comment: web1
tasks/hosts.yml
- name: templates
  template: src=hosts.j2 dest=/etc/hosts
           owner=root
           group=root
           mode=0644

ファイル・ディレクトリ

ディレクトリを作成する

- name: create springboot root directory
  file: path=/home/cmp/app
        state=directory
        owner=cmp
        group=cmp
        mode=0755

シンボリックリンクを作る

  • ln -s /etc/init.d/archiva /opt/archiva/bin/archiva
- name: create symbolic link
  file: src=/opt/archiva/bin/archiva dest=/etc/init.d/archiva state=link

ファイルの中身を置換する

  • /etc/sysconfig/clockのZONE="XXXX"をvarsに定義されたtimezoneで置換する。
vars/main.yml
---
timezone: Asia/Tokyo
tasks/timezone.yml
- name: Set /etc/sysconfig/clock
  lineinfile: >
        dest=/etc/sysconfig/clock
        state=present
        backrefs=yes
        regexp='^ZONE=.*$'
        line='ZONE=\"{{ timezone }}\"'

ファイル内の特定文字の後に追記する。

  • /etc/sysconfig/clock内のZONE=XXXの後にutc=xxxを追記する。
  • 前に追記したい場合はinsertbeforeを使う。
vars/main.yml
---
utc: true
tasks/timezone.yml
- name: Set /etc/sysconfig/clock
  lineinfile: >
        dest=/etc/sysconfig/clock
        state=present
        backrefs=no
        insertafter='^ZONE=.*$'
        line='UTC={{ utc }}'

ファイルの存在を確認する

  • /etc/init.d/kdumpというファイルが存在しない場合、エラーメッセージを表示する
- name: stat /etc/init.d/kdump
  stat: path=/etc/init.d/kdump
  register: kdump

- name: install check kdump
  fail: msg="kdump(kexec-tool) is not installed."
  when: not kdump.stat.exists

インストール

  • httpdをyumでインストールする
- name: install httpd
  yum:
    name: httpd
    state: installed

サービス

サービスを登録して、起動状態にする。

- name: register service and start
  service:
    name:httpd
    state: started
    enabled: yes

環境変数

環境変数を取得

  • lookupを使うことで実現できます。
  • 以下はec2を起動するときの例でprivate ipやimage idを環境変数から取得しています。
  tasks:
    - name: launch ec2 instance
      ec2:
        private_ip: "{{ lookup('env', 'PRIVATE_IP') }}"
        key_name: LegoTestKeyPair
        group: LegoTestInstanceSecurityGroup
        instance_type: "{{ lookup('env', 'INSTANCE_TYPE') }}"
        image: "{{ lookup('env', 'IMAGE_ID') }}"
        wait: yes
        wait_timeout: 300
        count: 1
        instance_tags:
          Name: xxxxxxxx
        monitoring: no
        vpc_subnet_id: subnet-xxxxxxx
        assign_public_ip: yes
        region: ap-northeast-1

その他(OS)

解凍する

  • dl_pathに指定されたファイルをinstall_pathに解凍する。(install_pathがない場合は新たに作成する)
vars/main.yml
---
archiva:
  dl_path: /usr/local/src/apache-archiva-2.2.0-bin.zip
  install_path: /opt/
  install_name: apache-archiva-2.2.0
tasks/archiva.yml
- name: unarchive archiva
  unarchive: src={{ archiva.dl_path }} dest={{ archiva.install_path }} creates={{ archiva.install_path }} copy=no

カーネルの設定を変更する

  • vars/main.ymlに設定されたカーネルパラメータを反映する
  • カーネル設定する用のモジュールsysctlがあるのでこれを利用する。
vars/main.yml
---
kernel:
  - { param: 'net.core.somaxconn', value: '128' }
  - { param: 'kernel.threads-max', value: '163875' }
tasks/kernel.yml
---
- name: Set {{ item.param }}
  sysctl: name={{ item.param }}
          value={{ item.value }}
          state=present
  ignore_errors: True
  with_items: "{{ kernel }}"

OSグループの追加

  • うっかりvarsにgroupsとか作るとハマります。(groupsはansibleの予約語なので)
vars/main.yml
os_groups:
  test1:
    gid: 1001
tasks/group.yml
- group: name={{ item.key }} gid={{ item.value.gid }}
  with_dict: "{{ os_groups }}"

OSユーザの追加

  • /etc/shadowも冪等性を担保しつつplaybookを作るのは難しいです。
  • こちらを参考にしてみてください。

高速化

pipelining

  • ansibleは実行時に一時ディレクトリを作成したり、スクリプトを転送したり、それを消したりという操作をssh越しに何回も処理を行うが、パイプラインで処理を渡すことによって無駄な通信を発生させないようにすることが可能。
  • ちゃんと動いているかどうかは-vvvオプションで動かすと良くわかる。
  • ansible実行ユーザのrequirettyが無効になっていることが条件なので、まずはplaybookで無効化しておく
- name: requiretty設定を無効化
  lineinfile:
    dest: /etc/sudoers
    regexp: '^Defaults:{{ ansible_user }}\srequiretty'
    line: 'Defaults:{{ ansible_user }} !requiretty'
    validate: 'visudo -cf %s'
    backup: yes
  • pipelinesの有効化(以下のいずれかの方法)
    • varsにansible_ssh_pipelining: yesをつける
    • playbook実行時に -e pipelining=Trueをつける
    • ansible.cfgの[ssh_connection]にpipelining=Trueをつける

予約語

OSのバージョンによって処理を分岐する。

  • CentOS6だったらkexec-toolsをインストールする場合
  • 当初ansible_distribution_version.split('.')[0]|intと書いてありましたが、ご指摘いただき修正しました。
- name: install kexec-tools
  copy: src=kexec-tools-2.0.10.tar.gz
        dest=/usr/local/src
  when: >
        ansible_distribution == 'CentOS'
        and ansible_distribution_major_version == '6'
uzresk
触ったプロダクトのメモです。
https://github.com/uzresk
tis
創業40年超のSIerです。
https://www.tis.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away