Javaを利用したシステム開発や運用において、外部のAPIやデータベースと通信を行う際に頻繁に遭遇するエラーのひとつがjava.net.UnknownHostExceptionです。

この例外は、指定したホスト名に対応するIPアドレスを解決できない場合にスローされます。

一見すると単純な接続ミスのように思えますが、その原因はタイピングミスから、DNSサーバーの不具合、コンテナ環境のネットワーク設定、さらにはJava独自のDNSキャッシュ仕様まで多岐にわたります。

本記事では、このエラーが発生するメカニズムを詳しく解説し、現場で役立つ具体的な解決策とトラブルシューティングの手順を網羅的に提供します。

java.net.UnknownHostExceptionとは何か

Javaのネットワークプログラミングにおいて、java.net.UnknownHostExceptionは非常に重要な意味を持ちます。

この例外はjava.io.IOExceptionを継承しており、プログラムが特定のホスト名(例:example.com)をIPアドレスに変換しようとした際、名前解決(DNSルックアップ)に失敗したことを通知するものです。

例外が発生する基本的な仕組み

Javaでネットワーク通信を行う際、内部的にはjava.net.InetAddressクラスなどが利用されます。

コンピュータはホスト名を直接理解できないため、DNS(Domain Name System)サーバーに問い合わせてIPアドレスを取得する必要があります。

このプロセスが正常に完了しない場合、Javaランタイムは「未知のホスト」としてこの例外を発生させます。

多くの場合、以下のメソッドなどを呼び出したタイミングで発生します。

  • InetAddress.getByName(String host)
  • Socket(String host, int port)
  • URL.openStream()(内部的な接続時)

このエラーは「サーバーに接続したが拒否された」という状態ではなく、「接続先がどこにあるかさえ特定できなかった」という段階での失敗であることを理解しておくことが重要です。

UnknownHostExceptionが発生する主な原因

エラーを解決するためには、まず「なぜ名前解決に失敗したのか」を切り分ける必要があります。

主な原因は以下の4つのカテゴリに分類できます。

1. ホスト名の指定ミス(単純な入力エラー)

最も一般的かつ単純な原因は、プログラム内に記述したホスト名や環境変数のタイピングミスです。

  • スペルミス(例:google.comgooogle.com と記述)。
  • 不要なプロトコル表記の混入(例:ホスト名が必要な場所に https://example.com と記述)。
  • 前後に不要な空白文字が含まれている。

2. ネットワーク接続および環境の問題

クライアントマシンがそもそも外部ネットワークやDNSサーバーにアクセスできない状態です。

  • インターネット接続の切断。
  • 社内LANやVPNの未接続。
  • プロキシサーバーの設定が必要な環境で、Java VMにプロキシ設定が反映されていない。
  • ファイアウォールやセキュリティグループによって、DNSクエリ(UDP 53番ポート)が遮断されている。

3. DNSサーバー側の問題

クライアント側の設定が正しくても、参照先のDNSサーバーが正常に動作していない場合があります。

  • 指定したドメインが期限切れ、または削除されている。
  • DNSサーバーがダウンしている、あるいは応答が著しく遅延している。
  • 内部DNS(プライベートDNS)に、新しく作成したサーバーのホスト名がまだ登録されていない。

4. コンテナ環境やクラウド特有の設定

近年増えているのが、DockerやKubernetes(K8s)などの仮想化環境における問題です。

  • Dockerコンテナ内からホストマシンや他のコンテナを名前解決できない。
  • /etc/hosts の書き換えが反映されていない。
  • Kubernetesの CoreDNS 設定の不備。

エラーを再現するサンプルプログラム

実際にどのようなコードでこの例外が発生するのか、簡単なサンプルプログラムで確認してみましょう。

以下のコードは、存在しないドメイン名に対して名前解決を試みるものです。

Java
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 UnknownHostExceptionの発生を確認するサンプルクラス
 */
public class DnsLookupDemo {
    public static void main(String[] args) {
        // 存在しない可能性が高いドメイン名を指定
        String targetHost = "this.is.invalid.hostname.example";

        try {
            System.out.println(targetHost + " のIPアドレスを取得します...");
            
            // 名前解決を実行
            InetAddress address = InetAddress.getByName(targetHost);
            
            // 成功した場合(通常はここに到達しない)
            System.out.println("IPアドレス: " + address.getHostAddress());
            
        } catch (UnknownHostException e) {
            // 名前解決に失敗した場合にキャッチされる
            System.err.println("エラー: ホスト名が見つかりません。");
            System.err.println("例外メッセージ: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

プログラムを実行すると、標準エラー出力に以下のようなスタックトレースが表示されます。

実行結果
this.is.invalid.hostname.example のIPアドレスを取得します...
エラー: ホスト名が見つかりません。
例外メッセージ: this.is.invalid.hostname.example
java.net.UnknownHostException: this.is.invalid.hostname.example
    at java.base/java.net.InetAddress.getAllByName(InetAddress.java:947)
    at java.base/java.net.InetAddress.getAllByName(InetAddress.java:835)
    at java.base/java.net.InetAddress.getByName(InetAddress.java:785)
    at DnsLookupDemo.main(DnsLookupDemo.java:15)

メッセージには、解決に失敗したホスト名がそのまま出力されるため、まずは「意図した通りの文字列がJavaに渡っているか」を確認するのが第一歩となります。

エラーを解消するためのトラブルシューティング手順

UnknownHostExceptionが発生した際、効率的に解決するための確認ステップを解説します。

ステップ1:OSレベルでの名前解決確認

Javaプログラムの問題か、OS・ネットワークの問題かを切り分けます。

ターミナル(コマンドプロンプトやシェル)で以下のコマンドを実行してください。

コマンド用途
ping <ホスト名>疎通確認。IPが引けるか手軽に確認できる。
nslookup <ホスト名>DNSサーバーへの問い合わせ詳細を確認する。
dig <ホスト名>(Linux/Mac)より詳細なDNS情報を表示する。

もしこれらのコマンドでもエラー(例:Non-existent domain)が出る場合は、Javaのコードではなく、OSの設定やDNSサーバー、ネットワーク経路に問題があります。

ステップ2:hostsファイルの確認

開発環境などで特定のホスト名を使用している場合、/etc/hosts(Windowsは C:\Windows\System32\drivers\etc\hosts)に正しい定義があるか確認してください。

# 例: ローカルの開発サーバーを定義する場合
127.0.0.1   dev.local.myserver

JavaはデフォルトでOSのhostsファイルを参照します。

ここに記述ミスがあると、どれだけプログラムを修正してもエラーは解消されません。

ステップ3:Javaのプロキシ設定を確認

企業内ネットワークなど、外部へのアクセスにプロキシが必要な環境では、Java VMに対してプロキシ情報を伝える必要があります。

プログラム実行時に以下のシステムプロパティを指定しているか確認してください。

  • -Dhttp.proxyHost=proxy.example.com
  • -Dhttp.proxyPort=8080
  • -Dhttps.proxyHost=proxy.example.com
  • -Dhttps.proxyPort=8080

プロキシを通すべきホストに対して直接接続しようとすると、名前解決に失敗することがあります。

ステップ4:IPv6とIPv4の優先度設定

一部の環境では、IPv6での名前解決を優先しようとして失敗するケースがあります。

その場合、IPv4を強制的に優先させることで解決することがあります。

Shell
# 実行時の引数に以下を追加
-Djava.net.preferIPv4Stack=true

Java特有の落とし穴:DNSキャッシュの挙動

Javaには、一度解決したホスト名を一定期間メモリ内に保持するDNSキャッシュ機能があります。

これが原因で、「ネットワーク設定を直したのにエラーが出続ける」あるいは「サーバーを切り替えたのに古いIPを見に行ってしまう」といった事象が発生します。

DNSキャッシュの有効期限(TTL)設定

JavaのDNSキャッシュの挙動は、セキュリティマネージャーの有無やシステムプロパティによって制御されています。

  • 成功したキャッシュ: デフォルトでは30秒程度(環境により無期限の場合あり)。
  • 失敗したキャッシュ(Negative Cache): 名前解決に失敗したという情報を10秒間保持します。

短時間にリトライを繰り返してもエラーが続く場合、このネガティブキャッシュが影響している可能性があります。

これを調整するには、java.security ファイルを編集するか、プログラム起動時の引数を指定します。

プロパティ名内容推奨値(トラブル時)
networkaddress.cache.ttl成功した名前解決の保持時間(秒)60
networkaddress.cache.negative.ttl失敗した名前解決の保持時間(秒)0 (キャッシュしない)

設定例(実行引数):

Shell
java -Dsun.net.inetaddr.ttl=60 -Dsun.net.inetaddr.negative.ttl=0 MyApp

クラウド・コンテナ環境での注意点

現代のアプリケーション開発では、DockerやKubernetes、AWSなどのクラウド環境特有の要因も無視できません。

Docker環境

Dockerコンテナ内で UnknownHostException が発生する場合、以下の点を確認してください。

  • コンテナの dns 設定が正しいか(docker run --dns ...)。
  • 同一ネットワーク内の他コンテナを指定する場合、コンテナ名が正しいか。
  • ホストマシンを参照したい場合、host.docker.internal を使用しているか。

Kubernetes環境

Kubernetesでは CoreDNS が名前解決を担います。

  • Service 名の指定が service-name.namespace.svc.cluster.local の形式に則っているか。
  • Podの dnsPolicy が適切に設定されているか。
  • ノード上の /etc/resolv.conf が正しく構成されているか。

AWS環境

EC2やLambdaから名前解決を行う際、VPC内のDNS設定(DNSホスト名、DNSサポート)が有効になっている必要があります。

また、セキュリティグループの送信(Egress)ルールで、UDP/TCPの53番ポートが許可されているかを確認してください。

プログラム内での適切な例外ハンドリング

UnknownHostException は、実行環境の状態に左右される「回復可能な可能性がある」エラーです。

そのため、単純にスタックトレースを出して終了するのではなく、適切なエラー処理を実装することが推奨されます。

Java
public void connectWithRetry(String host) {
    int maxRetries = 3;
    int retryCount = 0;

    while (retryCount < maxRetries) {
        try {
            InetAddress address = InetAddress.getByName(host);
            // 接続処理...
            break; 
        } catch (UnknownHostException e) {
            retryCount++;
            System.err.println("名前解決に失敗しました。リトライ中... (" + retryCount + "/" + maxRetries + ")");
            
            if (retryCount >= maxRetries) {
                // 最大リトライ数を超えた場合の処理
                throw new RuntimeException("ホスト名 " + host + " を解決できませんでした。ネットワーク設定を確認してください。", e);
            }
            
            try {
                // 少し待機してからリトライ
                Thread.sleep(2000);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

このように、一時的なネットワークの不安定さやDNSサーバーの瞬断を考慮したリトライロジックを組み込むことで、システムの堅牢性を高めることができます。

まとめ

java.net.UnknownHostException は、Javaアプリケーションがネットワークの向こう側にある相手を特定できない時に発せられるSOSサインです。

解決のためのポイントを振り返りましょう。

ホスト名の確認

スペルミスや不要な文字列が含まれていないか、ログを精査します。

OSコマンドでの切り分け

nslookupping を使い、Javaの外側でも問題が発生しているかを確認します。

環境設定の点検

プロキシ、/etc/hosts、IPv6設定、クラウドのセキュリティグループなどを確認します。

Javaのキャッシュ仕様

ネガティブキャッシュの影響で、修正後もしばらくエラーが続く可能性があることを考慮します。

コンテナ・オーケストレーション

DockerやKubernetesの内部DNSルールが正しく適用されているかを確認します。

名前解決はネットワーク通信の入り口です。

このエラーを正しく理解し、迅速に対処できるようになることで、分散システムの安定稼働を支えるエンジニアリングスキルを一段高めることができるでしょう。