用途別Tips
ファイル、ディレクトリ操作
- ファイルやディレクトリの操作にはFileモジュール
を使います。
ディレクトリ作成
- name: make apache log directory
file:
path: /var/log/httpd/localhost.localdomain
state: directory
owner: root
group: root
mode: 0755
ディレクトリ削除
- name: /var/www/htmlを削除
file:
path: /var/www/html
state: absent
ファイル作成(中身は空っぽ)
- name: create timezone file
file:
dest: /etc/timezone
state: touch
ファイル削除
- name: Remove default vhost
file:
dest: /etc/httpd/conf.d/welcome.conf
state: absent
- ファイルのコピーには、copyモジュール
を使います。ただし、サーバ内のコピーではなく、Ansibleを実行しているローカルー>リモート間でのコピーになります。逆方向の場合はsynchronizeモジュール
を使います。要するにSCPしています。サーバ内部でのコピーや移動はcommandモジュールでコマンドを叩くしかない?
srcには、role/filesからの相対パスになります。
ファイルをコピー
- name: add authorized key
copy:
src: public_keys/test
dest: /home/test/.ssh/authorized_keys
mode: 0600
owner: test
group: test
リモートのファイルをダウンロードする
- リモートのファイルをダウンロードしたり、同期をとったりする場合、synchronizeモジュール
を使います。
モジュール名の通り、内部的にはrsyncです。
リモートにあるファイルをダウンロードする。
- name: ファイルをダウンロードする
synchronize:
mode: pull
src: /tmp/data.tar.gz
dest: ./tmp
中身チェック
- 単純にファイルの中にある文字列が無ければ、何か条件発動する場合、こんな感じでできます。
- name: 設定に値があるか? shell: grep "some_key_string" /foo/var/some_app.ini register: app_settings changed_when: false - name: 設定が必要か? set_fact: need_to_set: "{{ app_settings.stdout_lines.count < 1 }}"
存在チェック
- ファイルやディレクトリの存在チェックには、statモジュール
を使います。
これは、registerと組み合わせる事で、条件分岐に使えます。
デフォルトのバーチャルホスト設定を無効化する。
- name: welcome設定存在チェック
stat: path=/etc/httpd/conf.d/welcome.conf
register: welcome_stat
- name: デフォルトのバーチャルホスト設定削除
command: mv /etc/httpd/conf.d/welcome.conf /etc/httpd/conf.d/welcome.conf.disable
when: welcome_stat.stat.exists
notify:
- "restart apache"
ファイルチェックに時間がかかる場合
statモジュールはファイルのハッシュをとって変更されたかをチェックしているようです。
従って、巨大なファイルの存在チェックをした場合、結構時間がかかることがあります。
その場合は以下のようにハッシュ計算をしないようにします。
- name: スワップファイル存在チェック(遅いのでチェックサムを返さない)
stat:
path: /swap.file
get_checksum: no
get_md5: no
register: swapfile_exists
ユーザ操作
- ユーザの操作にはuserモジュール
を使います。
ユーザ作成
パスワードは以下のコマンドで生成
python -c 'import crypt; print crypt.crypt("hogehoge", "$1$SomeSalt$")'
- name: create user test
user:
name: test
password: "$1$SomeSalt$Drh7s/vUcl5XnIZ/Neglz1"
state: present
Cron設定
- Cronの操作にはcronモジュール
を使います。
Cron作成
- name: add cron cron: name: "test cron" user: root minute: "0" hour: "6" job: "ls -lah > /tmp/list" state: present
ファイルをダウンロードする
- ファイルのダウンロードには、get_urlモジュール
を使います。
ファイルをダウンロードする。
- name: Jenkinsリポジトリの取得
get_url:
url: http://pkg.jenkins-ci.org/redhat/jenkins.repo
dest: /etc/yum.repos.d/jenkins.repo
ファイルのダウンロード時チェックサムをチェックする。
- name: mod_ruidをダウンロード
get_url:
url: http://jaist.dl.sourceforge.net/project/mod-ruid/mod_ruid2/mod_ruid2-0.9.8.tar.bz2
sha256sum: f8a178daf3bccf86e7e50e3224efc52165200470dece7b701466c5fbf1944b19
dest: /tmp/mod_ruid2-0.9.8.tar.bz2
force: True
環境設定
ホスト名設定
- name: ホスト名設定
hostname:
name: "example.com"
fstabへ追加
- name: スワップファイル永続化
mount:
name: swap
src: /swap.file
fstype: swap
opts: defaults
passno: '0'
dump: '0'
state: present
タイムゾーン設定
- name: タイムゾーンをJSTにする
timezone:
name: Asia/Tokyo
yum関連
- yum関連モジュールとして、yumモジュール
、rpm_keyモジュール
、rpm_repositoryモジュール
があります。
yumでインストールする。
- name: Javaのインストール
yum: name={{ item }} state=installed
with_items:
- java-1.7.0-openjdk
- httpd
なお、nameの引数にサーバ内のrpmファイルパスを渡せば、ローカルのrpmをインストールすることが、URLを渡せば直接インストールすることができます。
yum updateをする。
- name: yum update yum: name=* state=latest
rpmキーをインポート
- name: Jenkinsリポジトリのキーをインポート
rpm_key:
key: http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
リポジトリを追加
- name: Add repository yum_repository: name: epel description: EPEL YUM repo baseurl: http://download.fedoraproject.org/pub/epel/$releasever/$basearch/enabled: no
テンプレート関連
- テンプレート関連モジュールとして、templateモジュール
があります。
テンプレートにはJinja2
が使われます。
srcのパスは、role/templatesからの相対パスになります。
テンプレートファイルを展開し、リモートに配置する。
- name: template vhost.conf
template:
src: etc/virtual.conf
dest: /etc/httpd/conf.d/virtual.conf
mode: 0644
owner: root
group: root
notify: "restart apache"
サービス関連
- サービス関連モジュールとして、serviceモジュール
があります。
サービスを起動し、自動起動をセットする。
- name: Jenkins Start
service:
name: jenkins
state: running
enabled: yes
サービスを再起動する。(ハンドラでよく使う)
- name: "restart syslogd" service: name=rsyslog state: restarted
コマンドの実行関連
- コマンドを実行するモジュールとして、commandモジュール
やshellモジュール
があります。
commandモジュールではパイプやリダイレクトなどが使えなかったりするので、そのような場合はshellモジュールを使います。
なお、冪等性の確保は各自で行わないといけないので注意です。
/usr/local/srcへディレクトリを移動した後ファイルを解凍
- name: ファイルを解凍
command: tar -xjf /tmp/mod_ruid2-0.9.8.tar.bz2
args:
chdir: /usr/local/src/
ディレクトリのみパーミッション変更
- name: ディレクトリのみ権限調整
shell: find . -type d -print | xargs chmod 755
args:
chdir: /var/www/html/
サーバ内部でファイルのコピー
- name: タイムゾーンをJSTにする shell: cp /usr/share/zoneinfo/Japan /etc/localtime
ファイル編集関連
- ファイル編集関連としては、一行編集するlineinfileモジュール
やreplaceモジュール
がありますが、
あまり凝ったことができないので、テンプレートを使って丸ごと置き換えるか、shellモジュールでawkなど自力で頑張るしかなさそうです。
なお、lineinfileモジュールは存在しないファイルを指定するとエラーになります。
sudoeas.dに追加する。
元ネタはここ
- name: add users to sudoers
lineinfile: dest=/etc/sudoers.d/{{ item.user }}
owner=root group=root mode=0440
state=present
create=yes
line="{{ item.user }} {{ item.role }}"
validate='visudo -cf %s'
with_items:
- { user: "user1", role: "ALL=(ALL) NOPASSWD: ALL" }
正規表現に一致する行を編集する。
- name: "php.iniの修正"
lineinfile:
dest: /etc/php.ini
state: present
backrefs: yes
regexp: '{{ item.regexp }}'
line: '{{ item.line }}'
with_items:
- regexp: '^;date.timezone ='
line: 'date.timezone=Asia/Tokyo'
- regexp: '^;mbstring.language = Japanese'
line: 'mbstring.language = Japanese'
notify:
- "restart apache"
空のファイルを作成し、最後の行に追記する。(insertafter:EOFはデフォなので省略可能)
- name: テスト用html作成
file:
dest: /var/www/html/index.html
state: touch
owner: apache
group: apache
- name: テスト用html作成
lineinfile:
dest: /var/www/html/index.html
line: "testweb"
insertafter: EOF
owner: apache
group: apache
リポジトリを無効化する。
#ただし、該当するものすべてが置換されるので注意
- name: DisabeRepo
replace:
dest: /etc/yum.repos.d/{{item}}
regexp: "enabled=1"
replace: "enabled=0"
with_items:
- epel.repo
- remi.repo
Apache関連
- Apache関連モジュールとして、apache2_moduleモジュール
やBASIC認証で使うhtpasswdモジュール
があります。
Apacheのモジュールを有効化
- apache2_module:
state: present
name: php
BASIC認証用にhtpasswdファイル作成
- htpasswd:
path: /etc/httpd/htpasswd_basic_user
name: basic_user
password: testpass
- なお、ダイジェスト認証はないが、テンプレートで実現できるらしい

タスクの実行結果を保持したい
- タスクの実行結果を変数に保持して、次のタスク実行を制御したい時があります。
そのような場合は、registerを使い、結果を変数に保存し、次のタスクのwhenで条件分岐させます。
例えば、shellモジュールでファイルをリネームする時、1回目はファイルが存在するので良いのですが、2回目以降は対象となるファイルが存在しないのでエラーになります。
ファイルの存在チェックを行い、ファイルが存在したらshellタスクを実行する。というようにします。具体例はデフォルトのバーチャルホスト設定を無効化する。を見てください。
変数を作成する。
- タスク実行時、複数の条件で判定させたい時があります。whenだと一つしか条件が指定できないので、registerでそれぞれの結果を保持し、それをset_factモジュール
を用いて、条件判定後の結果を変数にセットします。
Apacheのmod_ruid2をインストール
- name: mod_ruidをダウンロード
get_url:
url: http://jaist.dl.sourceforge.net/project/mod-ruid/mod_ruid2/mod_ruid2-0.9.8.tar.bz2
sha256sum: f8a178daf3bccf86e7e50e3224efc52165200470dece7b701466c5fbf1944b19
dest: /tmp/mod_ruid2-0.9.8.tar.bz2
force: True
register: downloaded_tarball
- name: 既にバイナリが無いかチェック
stat: path=/usr/lib64/httpd/modules/mod_ruid2.so
register: st
- name: コンパイルが必要かどうか?
set_fact:
need_to_build: "{{ st.stat.exists == False or st.stat.size == 0 or
downloaded_tarball | changed }}"
- name: ファイルを解凍
command: tar -xjf /tmp/mod_ruid2-0.9.8.tar.bz2 -C /usr/local/src/
when: need_to_build
- name: compile&install mod_ruid2
command: apxs -a -i -l cap -c mod_ruid2.c chdir=/usr/local/src/mod_ruid2-0.9.8
when: need_to_build
インベントリファイルについて
- インベントリファイルでグループ共通変数を使えると便利なことがあります。
例えば、webserverとstagingserverというグループがあるとします。
これらは、group_versでそれぞれ独立して定義できますが、例えば共通のAPIサーバにアクセスするなどの場合、同じ定義をそれぞれしないといけません。その場合、次のように共通のグループを作ってあげるとよい。[webserver] 192.168.0.xxx 以下省略 [stagingserver: 192.168.1.xxx 以下省略 # グループ共通変数を読み込む [common_vars:children] webserver stagingserver
こうすると、common_varsという名前でgroup_varsの中にファイルを作成すると、どちらのグループからも参照することができる。
MySQL関連
- MySQL関連として、mysql_dbモジュール
やmysql_userモジュール
があります。
ただし、このモジュールを使用する場合は、リモートサーバ側にMySQLのPythonライブラリ(MySQL-Python)やMySQLクライアントが必要になります。
Pythonライブラリをインストールする際は、リモートサーバ上のPythonバージョンを確認したうえで、適切なバージョンのライブラリをインストールしましょう。参考
MySQL5.7+mysqlsecureinstallation
元ネタはここ
- set_fact:
mysql_log_file: /var/log/mysqld.log
- name: 初回起動時か判定する為にログファイルの存在チェック
stat:
path: "{{ mysql_log_file }}"
register: log_file
changed_when: false
- name: MySQL5.7起動
service:
name: mysqld
state: started
enabled: yes
- name: 一時パスワード取得
shell: cat {{ mysql_log_file }} | grep 'temporary password'
register: temporary_password
changed_when: false
when: log_file.stat.size == 0
- set_fact:
mysql_temp_password: "{{ temporary_password.stdout.split(' ')[-1] }}"
when: log_file.stat.size == 0
#MySQL5.7はパスワードポリシーが厳しいので、自動生成されたパスワードでパスワードを再設定して、SecureInstallationを実行する。
- name: 一時的にパスワード変更
command: >
mysqladmin password '{{ mysql_temp_password }}' -u root -p'{{ mysql_temp_password }}'
changed_when: false
when: log_file.stat.size == 0
- name: MySQLSecureInstallation実行
command: mysql_secure_installation -u root -p'{{ mysql_temp_password }}' -D
changed_when: false
when: log_file.stat.size == 0
#その後Rootのパスワードを設定する
- name: PWポリシーを一時的に緩める
shell: |
mysql -u root -p'{{ mysql_temp_password }}' --connect-expired-password -e "SET GLOBAL validate_password_l ength=8;"
mysql -u root -p'{{ mysql_temp_password }}' --connect-expired-password -e "SET GLOBAL validate_password_policy=LOW;"
changed_when: false
when: log_file.stat.size == 0
- name: MySQLのRootパスワード変更
command: >
mysqladmin password '{{ mysql_root_password }}' -u root -p'{{ mysql_temp_password }}'
changed_when: false
when: log_file.stat.size == 0
データベースの作成
- name: Create database
mysql_db:
db: '{{ mysql_dbname }}'
state: present
encoding: utf8
login_user: root
login_password: '{{ mysql_root_password }}'
ユーザの作成
- name: Create database user
mysql_user:
name={{ mysql_dbuser }}
password="{{ mysql_dbpass }}"
priv={{ mysql_dbname }}.*:ALL
state=present
host=%
login_user=root
login_password='{{ mysql_root_password }}'
DMPのインポート
- name: DBのインポート
mysql_db:
name : "database_name"
state: import
target: /tmp/all_data.sql #リモート上のパスなので注意
login_user: "db_admin"
login_password: "hogehoge"
login_host: "localhost"
PostgreSQL関連
- PostgreSQL関連モジュールとして、postgresql_dbモジュール
やpostgresql_userモジュール
があります。
ただし、このモジュールを使用する場合は、リモートサーバ側にPostgreSQLのPythonライブラリ(python-psycopg2)やPostgreSQLクライアントが必要になります。
なお、初回インストール時のinitdbや、接続に必要な設定(postgresql.confのlisten_addressesとかpg_hba.confとか)は設定してくれないので、事前に設定しないと動きません。
データベースの作成
- name : データベース作成
postgresql_db:
name: "{{ pgsql_dbname }}"
encoding: "UTF-8"
login_user: postgres
sudo_user: postgres
sudo: yes
ユーザ作成
#NOSUPERUSERをつけるとコケる。https://groups.google.com/forum/#!topic/ansible-project/-tDBSPtByfM- name: ユーザ作成 postgresql_user: db: "{{ pgsql_dbname }}" name: "{{ pgsql_dbuser }}" password: "{{ pgsql_dbpass }}" priv: ALL encrypted: yes state: present login_user: postgres role_attr_flags: "NOCREATEDB,NOCREATEUSER,NOCREATEROLE" sudo_user: postgres sudo: yes
Zabbix関連
ZabbixServerへAgentの登録
元ネタはここ
Ansibleのモジュールはかなり高機能で、使い勝手が良い。
- name: ZabbixServerへ登録
zabbix_host:
server_url: http://zabbix_server.example.com
login_user: "{{ zabbix_server_admin_id }}"
login_password: "{{ zabbix_server_admin_password }}"
host_name: "{{ host_name }}"
host_groups:
- Linux servers
link_templates:
- "Template OS Linux"
status: enabled
state: present
interfaces:
- type: 1
main: 1
useip: 1
ip: "{{ webserver_ip }}"
dns: ""
port: 10050
その他
タスクの失敗を無視したい
- タスクを実行する時、エラーになっても続行したい場合、ignore_errorsをTrueにすると、失敗しても無視されます。
MySQLのテストデータベースを削除
# RootのPW変更後実行するとエラーになるので、エラーを無視するようにしている。 - name: remove the MySQL test database action: mysql_db db=test state=absent login_user=root login_password='' ignore_errors: True
テンプレートファイル内で{}が使いたい。
- シェルスクリプトなど{}をつかうファイルをJinja2テンプレートで作成すると、文法エラーが出て困ることがあります。
そういうときは、適用させたくないブロックを{% raw %}、{% endraw %}で囲むと、テンプレートが適用されません。#!/bin/bash backup_dir=/var/www/html {% raw %} cd ${backup_dir} datetime=`date +%Y%m%d%H%M` backup_file=/backup/backup_${datetime}.tar.gz tar cvzfp ${backup_file} ./ {% endraw %}
Ubuntuなどでsudoする際にパスワード入力で止まる。
- ubuntuなどで、sudoする際にパスワード入力が出て止まることがあります。
まぁちゃんとsudoの設定をすればいいのですが、その作業はAnsibleで出来ないという事になるので、出来ればスクリプト化したいですよね。
その場合、セキュリティ的には微妙になるのですが、group_varsにansible_sudo_pass: p@ssw0rd
のようにパスワードを書くと自動入力されます。
間違ってもgitとかで公開しちゃだめだぞ!!
Ansible実行時、UTF8じゃないと怒られる。
- Ansible実行時、以下のようなメッセージが出ることがあります。
ERROR! Unexpected Exception: 'utf8' codec can't decode byte 0x8a in position 82: invalid start byte
これば、PlayBookがUTF-8じゃないときに出ますので、UTF-8で保存しましょう。
特に日本語でコメント入れていたりすると結構な頻度で発生します(汗
ググってもPythonの問題が出てきたりと割とはまります・・・
Ansible実行時、いちいちKnownHostsに登録するか聞いてくるのが面倒
- SSH接続を公開鍵認証しているとき、カギを作り直したりサーバを作り直したときいちいち登録するか聞いてくるのが面倒になります。
その時は、Ansibleの実行ユーザにて、以下の設定をすると聞いてこなくなります。vi ~/.ansible.cfg [default] host_key_checking = False
複数のタスクでループを回したい
元ネタは ここ
なんですが、
Ansibleで複数タスクをループで回したい時があります。
Ansibleでループと言えばwith_itemsですね。
- name: データベース作成
mysql_db:
db: "{{ item }}"
state: present
encoding: utf8
login_user: root
login_password: '{{ mysql_root_password }}'
with_items:
- hoge_db
- hugga_db
- name: データベースユーザ作成
mysql_user:
name: "{{ item.user }}"
password: '{{ item.pass }}'
priv: "{{ item.db }}.*:ALL"
state: present
host: "%"
login_user: root
login_password: '{{ mysql_root_password }}'
with_items:
- db: hoge_db
user: hoge_user
pass: huga_pass
- db: huga_db
user: huga_user
pass: huga_pass
うん。とっても冗長です。
この二つが同時に回せると便利ですね。
これ、Ansible2系から 使える
らしいのですが、クラスメソッドさんのブログにあるように、include単位でwith_itemsが使えます。
ついでに変数にまとめましょう。
- common_vers/all.yml
mysql_root_password: Do̲you̲love̲MySQL57? mysql_databases: - mysql_dbname: hoge_db mysql_dbuser: hoge_user mysql_dbpass: hoge_pass - mysql_dbname: huga_db mysql_dbuser: huga_user mysql_dbpass: huga_pass - roles/mysql/tasks/main.yml
# DBを複数作成する - include: create_resources.yml with_items: - "{{ mysql_databases }}" loop_control: loop_var: resources - roles/mysql/tasks/create_resources.yml
# データベース作成 - name: データベース作成 mysql_db: db: "{{ resources.mysql_dbname }}" state: present encoding: utf8 login_user: root login_password: '{{ mysql_root_password }}' - name: データベースユーザ作成 mysql_user: name: "{{ resources.mysql_dbuser }}" password: '{{ resources.mysql_dbpass }}' priv: "{{ resources.mysql_dbname }}.*:ALL" state: present host: "%" login_user: root login_password: '{{ mysql_root_password }}'
インベントリファイル(hosts)やグループ変数(common_varsやhosts_varsなど)が編集しづらい
実はこれらのファイルは、
hosts -> hosts.ini
common_vars -> common_vars.yml
と拡張子をつけても問題ありません。(もちろん実行時に拡張子を含めて指定する必要はありますが)
エディタの関連付けが動かなくて面倒な時にちょっとしたTipsです。
インベントリパターンで、インベントリファイルの直下以外のロールを読み込みたい
Ansibleはディレクトリ構成が自由にできるのですが、一応ベストプラクティスな構成があります。
ここ
にある、詳細ステージパターンや、ここ
にあるような、ほかのサーバで利用出るロールを共通にしたいけど、サーバ毎にロールをコピペするのは避けたい場合、
環境ごとにインベントリファイルのディレクトリを分けて、
ansible-playbook ./inventries/staging/main.yml -i ./inventries/staging/hosts.ini
のような感じで動かします。
Ansibleはデフォルトでインベントリファイルの直下にあるroleディレクトリからロールを探しに行きます。
その場合、rolesに相対パスで書くのですが、何故かそんなロールはないと怒られてしまいました。
その場合、
- name: staging
hosts: staging
become: yes
vars_files:
- ../group_vars/common_vars.yml
- ./group_vars/sites.yml
roles:
- { role: ../roles/common }
- { role: ../roles/mysql }
- { role: ./staging }
のようにするとうまくいきました。Ansibleのバージョンによるのかもしれません。
このページの参照回数は、1053です。