深夜2時のデプロイの悩み
誰しも経験があるでしょう。ルーチンのアップデートをプッシュし、すぐに成功メッセージが表示されるのを期待していたのに、代わりにターミナルに赤いテキストの壁が吐き出される。単純な make install やスクリプトを実行しようとしたのに、Ansible が作業すべきディレクトリを見つけられませんでした。フォルダがそこにあるはずだと確信しているとき、これは非常に苛立たしい障害となります。
エラーは通常、以下のようになります。 slums:
fatal: [host]: FAILED! => {"changed": false, "cmd": "make install", "msg": "chdir /opt/app/build failed: [Errno 2] No such file or directory: '/opt/app/build'"}
これが発生した場合、Ansible は shell または command モジュールの chdir パラメータが、リモートホスト上に存在しないパスを指していることを伝えています。これはローカルのセットアップのバグではありません。ターゲットサーバー上の状態が欠落しているのです。おそらく、フォルダが作成されていなかったか、前のステップが Playbook を停止させずに失敗した可能性があります。
なぜディレクトリが欠落しているのか?
コードをいじり始める前に、なぜパスが消えたのかを知る必要があります。90% のケースでは、次の 3 つのいずれかが原因です。
- サイレントな失敗: 以前の
git cloneやunarchiveタスクが失敗(403 Forbidden やディスク容量の問題など)し、フォルダが作成されなかった。 - パスの混同:
chdir: buildのような相対パスを使用しており、Ansible が意図したシステムパスではなく、SSH ユーザーのホームディレクトリ(例:/home/ubuntu/build)からの相対パスで見つけようとした。 - 空の変数:
chdir: "{{ app_path }}"のような変数を使用しているが、その変数が未定義であるか、誤って空の文字列に設定されている。
クイックデバッグ:リモートの状態を確認する
推測しないでください。サーバーがどのように認識しているか、アドホックコマンドを実行して確認しましょう。
ansible all -m shell -a "ls -ld /opt/app/build" -i inventory.ini
もしサーバーが ls: cannot access... と返してきた場合、フォルダは物理的に存在しません。ディレクトリの詳細が返される場合は、権限の問題か、Playbook 変数のタイポ(打ち間違い)の可能性があります。
解決策
1. ディレクトリを強制的に作成する
最も確実な修正方法は、コマンドの直前で file モジュールを使用することです。これにより、前のステップで何が起こったかに関係なく、ディレクトリが確実に存在するようにします。これは Infrastructure as Code(IaC)の基本原則です。「想定するのではなく、強制する」ことです。
- name: Ensure the build directory is ready
ansible.builtin.file:
path: /opt/app/build
state: directory
mode: '0755'
- name: Run the installation
ansible.builtin.command: make install
args:
chdir: /opt/app/build
2. 絶対パスに切り替える
相対パスは、「No such file」エラーの主な原因です。Playbook が root として実行される場合、chdir: project は /root/project を探します。ec2-user として実行される場合は、/home/ec2-user/project を探します。Playbook の予測可能性を維持するために、常に /var/www/my-app/build のようなフルパスを使用してください。
3. 安全のために 'creates' 引数を使用する
creates を使用して、タスクをよりスマートにすることができます。これは Ansible に「この特定のファイルが存在しない場合にのみ、このコマンドを実行する」よう伝えます。これはエラーを防ぎ、Playbook のべき等性(idempotency)を確保するための優れた方法です。
- name: Compile the application
ansible.builtin.shell: ./configure && make
args:
chdir: /opt/app/src
creates: /opt/app/src/Makefile
4. 動的なデプロイの処理
タイムスタンプや Git のハッシュに基づいてパスが変わる場合は、変数が正しく登録されていることを確認してください。例えば、今日の日付にちなんだ名前のフォルダにデプロイする場合:
- name: Define the release path
set_fact:
rel_path: "/opt/releases/{{ ansible_date_time.date }}"
- name: Create the release folder
ansible.builtin.file:
path: "{{ rel_path }}"
state: directory
- name: Initialize the app
ansible.builtin.shell: python3 manage.py migrate
args:
chdir: "{{ rel_path }}"
修正の確認
Playbook を更新した後、-v(verbose)フラグを付けて実行します。file モジュールの出力を確認してください。もし "changed": true と表示されれば、ディレクトリが実際に欠落しており、Ansible がそれを修正したことを意味します。また、リモートサーバーで stat /opt/app/build を実行して、作成時間や所有者の権限(例:drwxr-xr-x)を確認することもできます。
重要なポイント
- 明示的であること: 前のタスクがフォルダを作成したと決して信じないでください。
fileモジュールを使用して確実性を期しましょう。 - 権限を確認する: フォルダが存在するのにエラーが出る場合は、
become_userが親ディレクトリに移動するための+x権限を持っていない可能性があります。 - チルダ(~)を避ける:
~/buildは使用しないでください。異なる SSH ユーザー間での曖昧さを避けるため、/home/username/buildを使用してください。

