■ Ansibleとは
-
Ansibleの特徴
- エージェントレスでサーバ構築を自動化してくれるツールです。
- sshdが動作しているマシンにSSHで接続し環境を構築してくれます。
- 設定ファイルはYAMLで書きます。プログラムの知識(Ruby、Python、etc.)は不要です。
- 数台~数十台規模のサーバ構築だったらchefよりも簡単です。
環境の準備
以前の記事を参照してください。
Ansible 最初の一歩
■ はじめに
Ansible 1.9から、他のユーザの権限(例えばroot権限)で実行するための'become'が追加されました。
rootでの直接アクセスが禁止されているサーバを管理する場合、一旦、一般ユーザとしてログインし、その後、sudo su -
でrootになって操作されるかと思います。
Ansibleでbecome: ture
してコマンドを実行する場合と、sudo su -
した後にコマンドラインからコマンドを直接実行する場合で、環境変数のPATHが異なるため、コマンドの実行結果が異なる場合があり注意が必要です。
今まで、この辺をあまり意識せずに使っていたため、コマンドを直接実行して確認済みのコマンドが、PlayBookでは失敗する事態が発生し、少し苦労したので、become
と環境変数のPATHについて、改めて調べてみました。
つまづいたポイント
- 「
become: true
≠sudo su -
」だったこと。 -
sudo su -
の状態で各種コマンドを実行する方法が不明なこと。
Ansible本家の見解
Ansible公式HP、GitHubのissuesを調査してみました。
Ansible公式HP(リンク)
Only one method may be enabled per host
Methods cannot be chained. You cannot use
sudo /bin/su -
to become a user, you need to have privileges to run the command as that user in sudo or be able to su directly to it.
意訳:
sudo
とsu
は、同時に使えないので、sudo su -
したい人は、直接コマンドを実行してください。
Ansible GitHub issues
本家でのディスカッションも結論がでていない模様(ステータスがOpenのままです)。
GitHub issues#12686
■ 動作検証
まずは、PATHがどのようになるのか調査してみました。
調査環境:Amazon Linux AMI 2016.03
remote_user: ec2-user
1. become: false(ec2-user)
/usr/local/bin:/bin:/usr/bin:/opt/aws/bin
2. become: true(root)
/sbin:/bin:/usr/sbin:/usr/bin
3. lookup('pipe', 'echo $PATH')
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:
/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin
4. lookup('env', 'PATH')
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:
/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin
5. ec2-user
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:
/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin
6. sudo su -
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:
/opt/aws/bin:/root/bin
7. sudo su
/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin
見事にバラバラですね ^^;
■ 「sudo su -」相当のPATHを設定する
Googleで検索すると、commandやshellで.bashrcや.bash_profileを読み込む方法が紹介されていますが、これをやってしまうと冪等性の保証が難しくなってしまいます。そこで、environmentでPATHを設定する方法について紹介します。
設定前の状態
まず、何もしていないとどのようになるのか実行してみます。
- hosts: all
remote_user: ec2-user
tasks:
- command: echo $PATH
register: become_path
become: true
changed_when: False
- name: become
debug: var=become_path.stdout
- command: echo $PATH
register: not_become_path
changed_when: False
- name: not become
debug: var=not_become_path.stdout
実行してみます。
$ ansible-playbook env_path.yml
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [target03]
TASK [command] *****************************************************************
ok: [target03]
TASK [become] ******************************************************************
ok: [target03] => {
"become_path.stdout": "/sbin:/bin:/usr/sbin:/usr/bin"
}
TASK [command] *****************************************************************
ok: [target03]
TASK [not become] **************************************************************
ok: [target03] => {
"not_become_path.stdout": "/usr/local/bin:/bin:/usr/bin:/opt/aws/bin"
}
PLAY RECAP *********************************************************************
target03 : ok=5 changed=0 unreachable=0 failed=0
一般ユーザのPATH:/usr/local/bin:/bin:/usr/bin:/opt/aws/bin
rootのPATH:/sbin:/bin:/usr/sbin:/usr/bin
になっています。いわゆるログインしていない状態のようです。
lookupを使った場合は以下のようになります。
---
- hosts: all
remote_user: ec2-user
vars:
pipe_path: "{{ lookup('pipe', 'echo $PATH') }}"
env_path: "{{ lookup('env', 'PATH') }}"
tasks:
- name: pipe path
debug: msg={{ pipe_path }}
- name: env path
debug: msg={{ env_path }}
lookupを使った場合の実行結果は以下のようになります。
$ ansible-playbook playbook.yml
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [target01]
TASK [pipe path]] **************************************************************
ok: [target01] => {
"msg": "/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin"
}
TASK [env path]] ***************************************************************
ok: [target01] => {
"msg": "/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin"
}
PLAY RECAP *********************************************************************
target01 : ok=3 changed=0 unreachable=0 failed=0
こちらは、ec2-userでログインした状態になっていました。
environmentでPATHを設定してみる
environmentでPATHを設定してみます。
あらかじめ、sudo su -
して、echo $PATH
を調べておきます。
$ sudo su -
# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin
PlayBookは以下のようになります。
- hosts: all
remote_user: ec2-user
environment:
PATH: "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
tasks:
- command: echo $PATH
register: become_path
become: true
changed_when: False
- name: become
debug: var=become_path.stdout
- command: echo $PATH
register: not_become_path
changed_when: False
- name: not become
debug: var=not_become_path.stdout
実行してみましょう。
$ ansible-playbook playbook.yml
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [target01]
TASK [command] *****************************************************************
ok: [target01]
TASK [become] ******************************************************************
ok: [target01] => {
"become_path.stdout": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
}
TASK [command] *****************************************************************
ok: [target01]
TASK [not become] **************************************************************
ok: [target01] => {
"not_become_path.stdout": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
}
PLAY RECAP *********************************************************************
target01 : ok=5 changed=0 unreachable=0 failed=0
environment:
をPlayBookに対して設定したため、become: true
の場合も、そうではない場合も、いずれも設定したPATHになっています。
ちなみに、environment:
は、タスクのほうに書くことも可能です。タスクごとに設定したい場合は、タスクに対して指定していくことになります。PlayBookは以下のようになります。
- hosts: all
remote_user: ec2-user
tasks:
- command: echo $PATH
register: become_path
become: true
changed_when: False
environment:
PATH: "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
- name: become
debug: var=become_path.stdout
- command: echo $PATH
register: not_become_path
changed_when: False
environment:
PATH: "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin:/root/bin"
- name: not become
debug: var=not_become_path.stdout
実行結果は、PlayBook全体に対してenvironment:
を設定した場合と同じになります。
environmentのスコープ
PlayBookやTaskに対して設定したenvironment:
のスコープは、設定した範囲になります。
例えば、PlayBookに対して設定した場合、別のPlayBookへは影響がでないようになっています。
自分でコマンドを羅列して/bin/shで実行する場合は、設定したPATHを元に戻すコマンドも書かなければならないのですが、AnsibleのPlayBookは、その辺を自動で行ってくれるので便利です。
■ おわりに
今回は、environmentを使ってPATHを設定する方法を紹介しました。
PATHの設定方法は、説明した通りになりますが、PATH依存のPlayBookは思わぬ落とし穴にはまってしまう可能性があります。PlayBookで、shellやcommandモジュールを使う際には、フルパスで書く習慣をつけておくと、安全でよいかと思います。
-
落とし穴にはまる可能性あり(PATH依存)
- command: cmd
-
安全な書き方
- command: /usr/local/bin/cmd
Ansibleのような構成管理ツールを使うにあたり、単に動作しますという状態ではなく、保守性や堅牢性を考慮した運用ができるように心がけていけるとよいですね。