用途別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のバージョンによるのかもしれません。
このページの参照回数は、873です。