Linux
CentOS
Ansible

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

More than 1 year has 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'