「sudo: unable to resolve host」の解決 - hostname と /etc/hosts

「sudo: unable to resolve host」の解決 - hostname と /etc/hosts

「sudo: unable to resolve host」とは何を示すのか?

結論: これはエラーではなく警告。sudo が起動時に自ホスト名を解決できなかったことを示す。多くの場合コマンド自体は実行されるが、解決のタイムアウトで毎回数秒待たされる。原因はほぼ hostname/etc/hosts の不一致に集約される。

sudo を実行すると、コマンドの出力の前に次のような行が出る。

$ sudo apt update
sudo: unable to resolve host myserver: Name or service not known
[sudo] password for user:

メッセージは「myserver というホスト名を解決できない」という意味だ。重要なのは、これが出ても多くの場合 sudo は最後まで動く点。つまり「sudo が壊れた」のではなく「自分自身のホスト名が名前解決の経路に登録されていない」状態を sudo が報告しているにすぎない。

ただし副作用がある。sudo はホスト名解決に失敗するまでリゾルバのタイムアウトを待つため、sudo を打つたびに数秒の遅延が発生する。警告自体は無害でも、この遅延が運用上のストレスになる。

前提(対象環境)

  • OS: Ubuntu / Debian 系を中心とした一般的な Linux
  • /etc/hostname / /etc/hosts を編集できる権限(直前まで sudo が使えていれば問題ない)
  • ホスト名を変更した直後・クラウドイメージ・コンテナ等で発生しやすい

なぜ sudo は自ホスト名を解決しようとするのか?

結論: sudo は sudoersHost 指定のマッチングログ記録のために、自分が動いているホスト名を確定させる必要がある。そのため起動時にホスト名→IP の解決を試み、失敗すると警告を出す。

sudoers には特定ホストでだけルールを有効にする Host / Host_Alias という仕組みがある。これを評価するため、sudo は「いま自分はどのホストで動いているか」を知る必要がある。具体的には gethostname(2) で取得した名前をリゾルバで解決し、ホストの同定に使う。

# sudoers の Host 指定の例(NIS/集中管理環境で使われる)
user  ALL=(ALL)  ALL          # 全ホスト
user  web01=(ALL)  ALL        # web01 でだけ有効

単一サーバ運用では Host 指定を使わないことも多いが、sudo はその有無に関わらず起動時に解決を試みる。さらに syslog へ「どのホストで誰が何を実行したか」を記録する用途でもホスト名を参照する。だからホスト名そのものが解決できないと、本処理の前に警告が出る。

この警告は sudo の設計上の副作用であって、ネットワークやインターネット接続とは無関係。オフラインのマシンでも、自ホスト名さえ /etc/hosts で引ければ警告は出ない。

原因はどこにあるのか?

結論: 原因は「/etc/hostname(現在のホスト名)」と「/etc/hosts(静的名前解決テーブル)」の食い違いにほぼ限定される。ホスト名を変えたのに /etc/hosts を更新していない、というのが典型。

自ホスト名の解決は、通常まず /etc/hosts を見にいく(/etc/nsswitch.confhosts: 行で files が先頭にあるため)。ここに現在のホスト名のエントリが無いと、次に DNS へ問い合わせ、そこでも引けずに失敗する。原因を整理すると次のとおり。

原因 起きやすい状況 確認の起点
ホスト名変更後 /etc/hosts 未更新 hostnamectl set-hostname だけ実行した cat /etc/hosts
/etc/hosts の自ホスト行が消えている クラウドイメージ / cloud-init / 手編集ミス getent hosts NAME
/etc/hostname を書き換えただけ 再起動後にホスト名だけ変わり hosts と乖離 hostname
nsswitch.confhosts: 行が不正 files が無い / 順序がおかしい cat /etc/nsswitch.conf

切り分けは単純で、「いまのホスト名」と「/etc/hosts に書かれている名前」が一致しているかを見るだけでよい。一致していなければ、それがそのまま原因になる。

クラウド / コンテナ環境では、起動ごとにホスト名が動的に振られたり、cloud-init が /etc/hosts を管理していることがある。手編集しても再起動で上書きされる場合は、後述の「恒久変更」セクションで管理経路ごと押さえる。

どう確認するのか?

結論: hostname で現在の名前を、cat /etc/hosts で登録済みの名前を見比べ、getent hosts <名前> で実際に解決できるかを最終確認する。getent が空なら、それが警告の直接原因。

まず、いま使われているホスト名を確認する。

$ hostname
myserver

次に /etc/hosts の中身を見る。ここに上の名前が含まれているかが核心だ。

$ cat /etc/hosts
127.0.0.1   localhost
::1         localhost ip6-localhost ip6-loopback

この例では myserver の行がどこにも無い。localhost はあるが、肝心の自ホスト名が登録されていない。最後に、リゾルバ経由で実際に解決できるかを getent で確認する。

$ getent hosts myserver
(何も出力されない = 解決できていない)

getent hosts <名前>何も返さないなら、その名前は名前解決の経路上どこにも存在しない。これが sudo: unable to resolve host の直接の引き金だ。逆に、正常なホストでは次のように IP とともに表示される。

$ getent hosts web01
127.0.1.1       web01

getent hosts/etc/nsswitch.confhosts: 行の設定に従って files(=/etc/hosts)→ DNS の順で解決を再現する。sudo が内部でやっている解決とほぼ同じ経路をたどるため、切り分けに最適。

どう直すのか?

結論: /etc/hosts現在のホスト名の行を追加するだけで直る。Debian/Ubuntu 系では 127.0.1.1 <hostname>127.0.0.1 localhost とは別の行として加えるのが慣習。

hostname で得た名前を /etc/hosts に登録する。直前まで sudo が(遅延付きでも)使えていれば、その sudo で編集できる。

# 現在のホスト名を変数に取る
$ hostname
myserver

# /etc/hosts を編集(任意のエディタで)
$ sudo nano /etc/hosts

編集後の /etc/hosts は次のようにする。127.0.1.1 の行を追加した点に注目。

127.0.0.1   localhost
127.0.1.1   myserver
::1         localhost ip6-localhost ip6-loopback

保存したら、すぐに解決できるか確認する。

$ getent hosts myserver
127.0.1.1       myserver

IP とホスト名が返れば修正完了だ。以降の sudo は警告も遅延も出なくなる。設定はファイルに書いた瞬間に有効で、再起動やサービス再起動は不要。

なぜ 127.0.0.1 ではなく 127.0.1.1 なのか: Debian の慣習で、FQDN を持つホストでは localhost127.0.0.1)と自ホスト名(127.0.1.1)を別の行に分ける。同じ行に同居させると、一部のソフトが「自ホスト名 = localhost」と解釈して誤動作することがあるため。固定 IP を割り当てている場合はその実 IP を使ってもよいが、DHCP 環境では 127.0.1.1 が無難。

ワンライナーで追記したい場合は次のようにする。ただし二重登録を避けるため、先に grep で存在確認するのが安全。

$ grep -q "$(hostname)" /etc/hosts || echo "127.0.1.1   $(hostname)" | sudo tee -a /etc/hosts

ホスト名を恒久的に変更するには?

結論: ホスト名を変えるときは hostnamectl set-hostname/etc/hosts の更新を必ずセットで行う。片方だけだと、まさにこの警告を自分で作り込むことになる。

ホスト名そのものを変更したい(それが原因で警告が出ている)場合は、systemd の hostnamectl を使う。これは /etc/hostname を書き換え、稼働中のシステムにも即時反映する。

# 新しいホスト名に変更
$ sudo hostnamectl set-hostname web01

# 反映を確認
$ hostnamectl status
   Static hostname: web01
         Icon name: computer-vm
            ...

hostnamectl/etc/hostname は更新するが、/etc/hosts自動では更新しない。そのため変更後は /etc/hosts の旧ホスト名を新しい名前に直す。

127.0.0.1   localhost
127.0.1.1   web01
::1         localhost ip6-localhost ip6-loopback

最後に新しい名前で解決できることを確認する。

$ getent hosts "$(hostname)"
127.0.1.1       web01

cloud-init を使う環境では /etc/cloud/cloud.cfgpreserve_hostname: true を設定しないと再起動でホスト名が戻ることがある。手編集が消える場合はこの管理経路を疑う。

変更直後に sudo を試すと、シェルがキャッシュした古い認証情報で混乱することがある。sudo -k で認証キャッシュを破棄してから試すと、純粋に名前解決だけの問題かを切り分けやすい。

それでも直らないときのチェックリスト

結論: 警告が消えないなら、hostname/etc/hosts の文字列が完全一致しているか、nsswitch.confhosts: 行に files があるかを順に疑う。タイポと全角・余分な空白が定番の落とし穴。

  • [ ] hostname の出力と /etc/hosts のエントリが1 文字も違わず一致しているか
  • [ ] /etc/hosts127.0.1.1 <hostname>(または実 IP)の行を追加したか
  • [ ] getent hosts "$(hostname)" が IP を返すようになったか
  • [ ] /etc/nsswitch.confhosts: 行の先頭に files があるか(hosts: files dns
  • [ ] FQDN を使う場合、127.0.1.1 web01.example.com web01 のように FQDN と短縮名を併記したか
  • [ ] クラウド / コンテナで再起動後に戻るなら cloud-init の preserve_hostname を確認したか
  • [ ] 編集後に sudo -k で認証キャッシュを破棄して再確認したか

次に読む