問題のシナリオ
12台のWebサーバーに設定ファイルをデプロイしているとします。Playbookを実行し、一連の「changed」ステータスが緑色で表示されることを期待していますが、代わりにぶっきらぼうな赤いエラーメッセージが表示され、プロセスが停止してしまいます。
fatal: [web_server]: FAILED! => {"changed": false, "msg": "could not find src=/etc/configs/nginx.conf"}
自分のマシンで ls -l /etc/configs/nginx.conf を実行し、そこにファイルが存在することを確認すると、混乱が生じます。このエラーは、ディスクからファイルが消えたことを意味することは稀です。通常、Ansibleが相対パスを解決する方法により、意図した場所とは異なる場所を探していることを意味します。
なぜこのエラーが発生するのか
copy モジュールは、control node(コマンドを入力するノートPCやCI/CDランナー)から remote host へファイルを転送します。Ansibleプロセスがローカルでファイルを見つけられない場合、アップロードすることはできません。この失敗は通常、次の4つの問題のいずれかに起因します。
- パスの不一致:
~/projects/からansible-playbookを実行したが、ソースパスが~/projects/ansible/playbooks/からの相対パスになっている。 - ロール階層の違反: Ansibleロール内の標準的な
files/ディレクトリではなく、config/という名前のフォルダにファイルを配置した。 - 変数の失敗:
{{ config_path }}のような変数が未定義であるか、タイポがあり、パスがnullまたは壊れている。 - 権限の障壁: Ansibleを実行しているローカルユーザーにソースファイルの読み取り権限がなく、モジュールから「見えない」状態になっている。
クイックフィックス
1. 絶対パスでテストする
診断ステップとして絶対パスを使用します。フルパスでタスクが成功すれば、相対パスの問題であることが確認できます。例えば、src を /home/dev/deploy/configs/app.conf に変更してみてください。これで動作する場合、ファイルにはアクセス可能であり、マッピングだけがずれていたことがわかります。
- name: Diagnostic copy using absolute path
ansible.builtin.copy:
src: /home/user/project/configs/app.conf
dest: /etc/app/app.conf
2. Playbookのディレクトリに合わせる
デフォルトでは、Ansibleは実行している .yml ファイルからの相対パスでファイルを探します。ディレクトリ構造が以下の例のようになっている場合、src はPlaybookの隣にあるフォルダを指すだけで済みます。
# ディレクトリ構造:
# ├── site.yml
# └── files/
# └── app.conf
- name: Copy app configuration
ansible.builtin.copy:
src: files/app.conf
dest: /etc/app/app.conf
恒久的な対策とベストプラクティス
ロールのディレクトリ構造を活用する
ロールには、タスクを簡素化する組み込みの検索ロジックが備わっています。タスクが roles/webserver/tasks/main.yml 内にある場合、Ansibleは自動的に roles/webserver/files/ からソースファイルを探します。パスを完全に省略することも可能です。
標準的なロール構造:
roles/
common/
tasks/main.yml
files/ntp.conf
main.yml 内のタスク:
- name: Deploy NTP config
ansible.builtin.copy:
src: ntp.conf # Ansibleはこれを自動的に ../files/ から見つけます
dest: /etc/ntp.conf
playbook_dir でパスを固定する
複雑なプロジェクトでは、異なるサブフォルダからPlaybookを実行することがよくあります。パスのポータビリティを維持するために、playbook_dir マジック変数を使用してください。これにより、コマンドをどこから実行したかに関わらず、常にPlaybookフォルダのルートからパスが計算されるようになります。
- name: Copy script from project root
ansible.builtin.copy:
src: "{{ playbook_dir }}/scripts/setup.sh"
dest: /usr/local/bin/setup.sh
mode: '0755'
ローカルコントローラーをデバッグする
エラーが解消されない場合は、Ansibleにローカルマシンの状態を強制的に報告させます。stat モジュールと delegate_to: localhost を使用して、コピーを試行する前にファイルの存在を確認します。これは、権限の問題や空の変数を特定する最も速い方法です。
- name: Verify local file existence
ansible.builtin.stat:
path: "{{ playbook_dir }}/files/my-config.conf"
register: local_file
delegate_to: localhost
- name: Fail if file is missing
ansible.builtin.fail:
msg: "The file was not found on the control node!"
when: not local_file.stat.exists
検証ステップ
修正を適用した後、次の3つのステップでソリューションを検証します。
- 詳細度を上げる:
ansible-playbook -vvを実行します。出力により、Ansibleがローカルディスク上で開こうとしている正確な絶対パスが明らかになります。 - ドライランを実行する:
--checkを付けて実行します。「could not find src」エラーが消えていれば、Ansibleはローカルファイルのマッピングに成功しており、プッシュする準備ができています。 - 権限を確認する: Ansibleユーザーが読み取れるように、ローカルファイルに少なくとも
644権限 (-rw-r--r--) があることを確認してください。
標準的なロール構造を使用したり、{{ playbook_dir }} でパスを固定したりすることで、これらのエラーの90%を排除できます。これらの習慣により、自動化の予測可能性が高まり、異なるチーム環境間でのポータビリティが確保されます。

