何が起きたのか
Ansible プレイブックがパッケージのインストールを順調に進めていたところ、何かが狂いました — ホストがインストール中に再起動したか、電源が切れたか、あるいは以前の apt/dpkg の実行が強制終了されたかです。これで、そのホストでパッケージを扱うプレイブックがすべてこのエラーで失敗するようになります:
E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.
apt モジュールはゼロ以外の終了コードを返し、プレイは失敗します。これが午前2時に20台のサーバーで発生している場合、1台ずつ SSH でログインするような対処では間に合いません。
ホストの状態を確認する
何かする前に、本当に dpkg の破損状態が問題なのかを確認しましょう — 同じような見た目の別の apt エラーでないことを確かめてください。SSH でログインして以下を実行します:
sudo apt-get update
まさにそのエラーが表示された場合、dpkg データベースが部分的に設定された状態でスタックしています。次に何が壊れているか確認します:
sudo dpkg --audit
このコマンドは、半分展開されたか、半分設定されたか、postinst スクリプトが失敗したパッケージを一覧表示します。終了コード 1 は何か壊れていることを意味します。その出力を手元に置いておきましょう — 修正を実行したときに何が起きるかがわかります。
直接修正する(1台のホスト)
SSH でログインして実行します:
sudo dpkg --configure -a
出力をよく確認してください。中断されたパッケージの設定を完了しようとします。正常に完了したら、続けて以下を実行します:
sudo apt-get install -f
これで残留した壊れた依存関係チェーンをクリアします。次に簡単な正常確認を行います:
sudo apt-get update && sudo apt-get upgrade --dry-run
エラーがなければ、ホストはクリーンです。Ansible プレイブックを再実行してください。
Ansible で修正する(影響を受けるすべてのホストを一括で)
ローリングアップグレードが失敗した後にクラスター全体がこの状態になっている場合は、Ansible で直接対処しましょう — 手動の SSH ループは不要です。
オプション1: 既存のプレイブックにプレタスクを追加する
パッケージインストールタスクの前にこのブロックを追加します:
- name: Fix interrupted dpkg if present
hosts: all
become: yes
pre_tasks:
- name: Check if dpkg is in interrupted state
command: dpkg --audit
register: dpkg_audit
changed_when: false
failed_when: false
- name: Run dpkg --configure -a if needed
command: dpkg --configure -a
when: dpkg_audit.stdout != ""
- name: Fix broken apt dependencies
apt:
fix_broken: yes
when: dpkg_audit.stdout != ""
正常なホストでも安全に実行できます — dpkg --audit が実際に問題を検出した場合にのみ修正が実行されます。
オプション2: スタンドアローンのリカバリープレイブック
任意のホストグループに投入できる専用のリカバリープレイが必要ですか?手元に置いておけるものを紹介します:
---
- name: Recover interrupted dpkg on Ubuntu/Debian hosts
hosts: "{{ target_hosts | default('all') }}"
become: yes
gather_facts: yes
tasks:
- name: Skip non-Debian systems
meta: end_host
when: ansible_os_family != "Debian"
- name: Audit dpkg state
command: dpkg --audit
register: dpkg_audit
changed_when: false
failed_when: false
- name: Report audit findings
debug:
msg: "Broken packages detected: {{ dpkg_audit.stdout_lines }}"
when: dpkg_audit.stdout != ""
- name: Configure interrupted dpkg packages
command: dpkg --configure -a
environment:
DEBIAN_FRONTEND: noninteractive
when: dpkg_audit.stdout != ""
register: dpkg_configure
- name: Fix broken apt dependencies
apt:
fix_broken: yes
update_cache: no
when: dpkg_audit.stdout != ""
- name: Verify apt is healthy
apt:
update_cache: yes
when: dpkg_audit.stdout != ""
以下のコマンドで実行します:
ansible-playbook recover-dpkg.yml -e target_hosts=webservers
DEBIAN_FRONTEND が重要な理由
dpkg --configure -a タスクに DEBIAN_FRONTEND: noninteractive が指定されていることに注目してください。これを省略すると、インタラクティブな postinst スクリプトを持つパッケージがハングします — Ansible の SSH セッション内では決して届かないキーボード入力を待ち続けることになります。プレイブックはそのまま無限にスタックします。
Ansible の apt モジュールはこれを自動的に設定します。dpkg を直接呼び出す生の command や shell タスクは設定しません。毎回明示的に指定してください。
なぜこれが繰り返し起きるのか
パッケージ操作を保護する2つのロックファイルがあります: /var/lib/dpkg/lock と /var/lib/dpkg/lock-frontend です。これらは apt/dpkg プロセスが同時に1つだけ実行されるようにするためのものです。そのプロセスが操作中に強制終了されると — アップグレード中の再起動、SIGKILL、OOM キラー、クラウドインスタンスの停止など — dpkg は状態ファイルを部分的に書き込んだまま終了します。
次に apt が何かに触れると、それらのファイルを読み込み、不整合を検出して処理を拒否します。頑固にしているわけではありません。中断されたものを完了させたいかどうか確認を求めているのです。
今後の予防策
- インストール中に絶対に再起動しないこと。 事前チェックを追加してください:
/var/run/reboot-requiredが存在する場合は、パッケージに触れる前に再起動を処理します。 - unattended-upgrades の競合を対処すること。 unattended-upgrades を Ansible と並行して実行するとロックの競合が常に発生します。Ansible で管理するホストでは無効にするか、プレイブックのスケジュールと重ならないメンテナンスウィンドウに固定してください。
- apt タスクにリトライロジックを追加すること。 並列実行される CI/CD パイプラインでは一時的なロックエラーが頻繁に発生します:
- name: Install packages
apt:
name: nginx
state: present
register: apt_result
until: apt_result is succeeded
retries: 3
delay: 10
- 安全な場合に古いロックファイルを削除すること。 プロセスがロックを解放せずに終了した場合は削除しますが、実際に何も実行されていないことを確実に確認してからにしてください:
- name: Remove stale dpkg locks if no apt process is running
file:
path: "{{ item }}"
state: absent
loop:
- /var/lib/dpkg/lock
- /var/lib/dpkg/lock-frontend
- /var/cache/apt/archives/lock
when: "'apt' not in ansible_facts.get('services', {})"
修正が成功したか確認する
リカバリー後、ホストで以下を実行します:
# 出力なしで終了コード0が返るはずです
sudo dpkg --audit && echo "dpkg is clean"
# エラーなしで完了するはずです
sudo apt-get update
# 元のプレイブックを再実行します
ansible-playbook your-playbook.yml --limit affected_host
プレイブックが完了し、出力に dpkg エラーがなければ — ホストは復旧しています。
まとめ
dpkg was interrupted は、dpkg が不整合を検出し、処理を続ける前に確認を求めているというメッセージです。修正方法は常に同じです: dpkg --configure -a、次に apt-get install -f。2つのコマンドだけです。
難しいのは、これを自動的に検出してリカバリーできるよう Ansible プレイブックを構築することです — 午前3時に1台のホストが中断しても、全員が寝ている間にデプロイパイプライン全体が止まらないように。

