
mDNS/Avahiがもたらす開発の「魔法」
開発者の皆さん、日々の業務で「あのサーバーのIPアドレス、なんだっけ?」「テスト環境のサービス、どこで動いてるんだ?」と、ホスト名やサービス発見に時間を取られていませんか?特に、macOSとMultipass上のUbuntu VMが混在するような環境では、その手間はさらに増大します。
しかし、もしローカルネットワーク上のあらゆるデバイスやサービスが、まるで魔法のように名前で呼び出せるとしたらどうでしょう?そう、それがmDNS/Avahiの力です。
この「mDNS完全攻略」シリーズでは、mDNS/Avahiの基礎から応用までを徹底的に解説してきました。そして最終回となる今回は、単なるホスト名解決の枠を超え、mDNS/Avahiがあなたの開発ワークフローをいかに加速させ、自動化とサービスディスカバリの新たな可能性を切り拓くかについて、具体的な応用例を交えながら深く掘り下げていきます。
さあ、mDNS/Avahiという強力なツールを使いこなし、開発の生産性を劇的に向上させる旅の最終章を始めましょう。
対象読者
- Web開発者: ローカル環境でのWebサービス(バックエンド、フロントエンド、データベースなど)の連携をmDNS/Avahiで効率化したい方。
- マイクロサービス開発者: 複数のサービス間の自動発見と接続設定の簡素化に関心がある方。
- 開発環境の自動化を検討している方: cloud-initやシェルスクリプトを活用したmDNS/Avahi設定の自動化に興味がある方。
- mDNS/Avahiのセキュリティについて学びたい方: ローカルネットワークでの利用におけるセキュリティ上の考慮事項や対策を知りたい方。
- mDNS/Avahiの応用例を探している方: 単なるホスト名解決だけでなく、サービスディスカバリとしてのmDNS/Avahiの可能性を探りたい方。
動作検証環境
この記事で紹介するcurlコマンドの動作は、以下の環境で検証しています。
- OS : macOS Tahoe Version 26.0
- ハードウェア : MacBook Air 2024 M3 24GB
- Multipass : Multipass version 1.16.1
目次
mDNS/Avahiによるサービスディスカバリの活用例
mDNS/Avahiの真価は、単にホスト名を解決するだけでなく、ネットワーク上のサービスを「発見」する能力にあります。これは、特にローカル開発環境において、驚くほどの利便性をもたらします。
ローカル環境でのWebサービス発見
複数のWebサービスやAPIをローカルで開発している場合、それぞれ異なるポートで動作させ、IPアドレスとポート番号を記憶するのは煩雑です。mDNSのサービスディスカバリ機能を使えば、サービス名でアクセスできるようになります。
例えば、my-webapp.local
という名前でWebアプリケーションを公開し、my-api.local
でAPIサーバーを公開するといったことが可能です。これにより、開発者はIPアドレスを意識することなく、直感的な名前でサービスにアクセスできます。

図:mDNS/AvahiによるローカルWebサービス発見の概念図
SSH接続の簡素化
Multipassで複数のUbuntu VMを立ち上げ、それぞれにSSH接続する際、VMのIPアドレスを毎回確認するのは手間です。mDNSを活用すれば、VMのホスト名で直接SSH接続が可能になります。
例えば、ubuntu-dev.local
というVMにSSH接続したい場合、ssh user@ubuntu-dev.local
と入力するだけで接続できます。これは、特に頻繁にVMを起動・停止する開発環境において、大きな時間短縮に繋がります。
開発用データベースやAPIエンドポイントの自動発見
マイクロサービスアーキテクチャを採用している場合、複数のサービスが互いに連携し合う必要があります。mDNSのサービスディスカバリ機能を使えば、各サービスが自身の存在と提供する機能をネットワークにブロードキャストし、他のサービスがそれを自動的に発見できるようになります。
これにより、サービス間の接続設定をハードコーディングする必要がなくなり、開発環境の柔軟性と保守性が向上します。例えば、データベースサービスが起動すると自動的にその存在を通知し、アプリケーションサービスがそれを発見して接続を確立するといったことが考えられます。
例えば、以下のようなシナリオが考えられます。
- データベースの自動発見:
- 開発用データベース(例: PostgreSQL, MySQL)がVM上で起動する際に、Avahiを通じて
_postgresql._tcp.local.
や_mysql._tcp.local.
のようなサービスタイプで自身の存在をネットワークに公開します。 - アプリケーションサービスは、起動時に
avahi-browse -t _postgresql._tcp
のようなコマンド(またはmDNSライブラリ)を使ってネットワーク上のPostgreSQLサービスを探索し、発見したデータベースに自動的に接続します。これにより、アプリケーションの設定ファイルにデータベースのIPアドレスやホスト名を直接記述する必要がなくなります。
- 開発用データベース(例: PostgreSQL, MySQL)がVM上で起動する際に、Avahiを通じて
- APIエンドポイントの動的な解決:
- 複数のマイクロサービスがそれぞれ異なるVMやコンテナで動作している場合、各サービスが自身のAPIエンドポイント(例:
_myapi._tcp.local.
)を公開します。 - クライアントサービスは、特定のAPIを呼び出す際に、mDNSでそのAPIエンドポイントを解決し、動的に接続先を決定します。これにより、サービス間の依存関係が疎結合になり、サービスのデプロイやスケーリングが容易になります。
- 複数のマイクロサービスがそれぞれ異なるVMやコンテナで動作している場合、各サービスが自身のAPIエンドポイント(例:
このように、mDNSのサービスディスカバリは、特に動的な開発環境やマイクロサービスアーキテクチャにおいて、設定の手間を大幅に削減し、柔軟性の高いシステム構築を可能にします。
Avahiサービス公開設定 (/etc/avahi/services/my-db.service
– VM内)
この設定により、開発用データベースサービスがmDNSで公開されます。
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h PostgreSQL</name> <!-- 例: "my-db PostgreSQL" -->
<service>
<type>_postgresql._tcp</type> <!-- PostgreSQLサービスとして公開 -->
<port>5432</port>
<txt-record>database=mydb</txt-record> <!-- データベース名のヒント -->
</service>
</service-group>
%h
はVMのホスト名(例:my-db
)に置き換えられます。<type>_postgresql._tcp</type>
はPostgreSQLの標準サービスタイプです。txt-record
は追加情報を提供できます。ここではデータベース名のヒントを追加しています。
この設定ファイルをVMに配置し、sudo systemctl restart avahi-daemon
でAvahiデーモンを再起動すると、ローカルネットワークから my-db.local
というホスト名でこのデータベースサービスが発見可能になります。
Webアプリケーションのバックエンドとフロントエンドの連携例
mDNS/Avahiは、Webアプリケーションのバックエンドとフロントエンド間の連携においても、IPアドレスの管理を不要にし、開発の利便性を高めます。ここでは、シンプルなAPIを提供するNode.js (Express) のバックエンドと、そのAPIを呼び出すReactのフロントエンドを例に、mDNS/Avahiの活用方法を具体的に見ていきましょう。

図:Webアプリケーションのバックエンドとフロントエンドの連携概念図
シナリオ: シンプルな「Hello World」API
- バックエンド:
my-backend.local
というホスト名で動作し、/api/hello
エンドポイントを提供します。 - フロントエンド:
my-frontend.local
というホスト名で動作し、バックエンドの/api/hello
を呼び出してメッセージを表示します。
1. バックエンド (Node.js + Express) の実装例
バックエンドは、my-backend.local
というホスト名で動作するMultipass VM上にデプロイされていると仮定します。
注: ここで示すコードは、mDNS/Avahiの連携を説明するために必要な最小限の機能に絞った抜粋です。
server.js
(バックエンドアプリケーション)
// server.js
const express = require('express');
const cors = require('cors'); // フロントエンドからのCORSリクエストを許可するため
const app = express();
const port = 3001; // APIがリッスンするポート
app.use(cors()); // 全てのオリジンからのCORSリクエストを許可(開発用)
// /api/hello エンドポイント
app.get('/api/hello', (req, res) => {
res.json({ message: 'Hello from Backend!' });
});
app.listen(port, () => {
console.log(`Backend API listening at http://localhost:${port}`);
console.log(`This service should be discoverable via mDNS as my-backend.local:${port}`);
});
package.json
(バックエンド)
{
"name": "my-backend",
"version": "1.0.0",
"description": "Simple Express backend API",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.17.1",
"cors": "^2.8.5"
}
}
Avahiサービス公開設定 (/etc/avahi/services/my-backend-api.service
– VM内)
この設定により、バックエンドのAPIサービスがmDNSで公開されます。
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h API</name> <!-- 例: "my-backend API" -->
<service>
<type>_http._tcp</type> <!-- HTTPサービスとして公開 -->
<port>3001</port>
<txt-record>path=/api/hello</txt-record> <!-- APIパスのヒント -->
</service>
</service-group>
%h
はVMのホスト名(例:my-backend
)に置き換えられます。<type>_http._tcp</type>
は一般的なHTTPサービスタイプです。より具体的なサービスタイプ(例:_myapi._tcp
)を定義することも可能です。txt-record
は追加情報を提供できます。ここではAPIのパスのヒントを追加しています。
この設定ファイルをVMに配置し、sudo systemctl restart avahi-daemon
でAvahiデーモンを再起動すると、ホストOSから my-backend.local
というホスト名でこのAPIサービスが発見可能になります。
2. Webフロントエンド (React) の実装例
フロントエンドは、my-frontend.local
というホスト名で動作する別のMultipass VM、またはホストOS上で動作していると仮定します。
App.js
(Reactコンポーネント)
WebブラウザはmDNSを直接解決する機能を持たないため、ReactアプリケーションがWebブラウザ上で動作する際、バックエンドのホスト名(my-backend.local
)を直接指定してAPIを呼び出します。このホスト名が、ホストOSのmDNS解決機能によってIPアドレスに解決されることで、動的な接続が実現されます。

図:WebブラウザとmDNSによるバックエンド解決の流れ
// App.js (Reactコンポーネント)
import React, { useState, useEffect } from 'react';
function App() {
const [message, setMessage] = useState('Loading...');
const [backendUrl, setBackendUrl] = useState('');
useEffect(() => {
// バックエンドのホスト名を直接指定
// mDNSがこのホスト名をIPアドレスに解決してくれる
const host = 'my-backend.local';
const port = 3001;
const url = `http://${host}:${port}/api/hello`;
setBackendUrl(url);
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => setMessage(data.message))
.catch(error => {
console.error('Error fetching data:', error);
setMessage(`Error: Could not connect to backend at ${url}. Is it running and discoverable?`);
});
}, []);
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h1>Frontend Application</h1>
<p>Message from Backend: <strong>{message}</strong></p>
<p>Attempted to connect to: <code>{backendUrl}</code></p>
<p>
<em>(Note: 'my-backend.local' is resolved by mDNS on your local network.)</em>
</p>
</div>
);
}
export default App;
package.json
(フロントエンド)
{
"name": "my-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
mDNS/Avahiの役割
この例では、mDNS/Avahiは以下の役割を果たします。
- バックエンドのホスト名解決: フロントエンドが
my-backend.local
というホスト名でバックエンドにアクセスしようとした際、mDNS/Avahiがローカルネットワーク上でmy-backend.local
のIPアドレスを解決します。これにより、フロントエンドはバックエンドのIPアドレスを知る必要がありません。 - IPアドレス変更への耐性: バックエンドVMのIPアドレスがDHCPによって変更されても、
my-backend.local
というホスト名は変わらないため、フロントエンドの設定を変更することなく、引き続きバックエンドにアクセスできます。
スクリプトによるmDNS/Avahi設定の自動化
mDNS/Avahiの恩恵を受けるためには、その設定を自動化することが重要です。特に、開発環境を頻繁に構築・破棄する場合や、チーム内で一貫した環境を共有したい場合に威力を発揮します。
シェルスクリプトでのAvahi設定スクリプト例
Ubuntu VMにAvahiをインストールし、設定を自動化するためのシンプルなシェルスクリプトの例です。
#!/bin/bash
set -e # コマンドが失敗した場合にスクリプトを終了する
# Avahiデーモン、libnss-mdns、openssh-serverのインストール
echo "Installing Avahi daemon, libnss-mdns, and openssh-server..."
sudo apt update
sudo apt install -y avahi-daemon libnss-mdns openssh-server
# ホスト名の設定(必要に応じて変更)
NEW_HOSTNAME="my-ubuntu-vm"
echo "Setting hostname to $NEW_HOSTNAME..."
sudo hostnamectl set-hostname "$NEW_HOSTNAME"
# nsswitch.confの確認とmDNSの有効化
echo "Verifying nsswitch.conf for mDNS..."
if ! grep -q "hosts:.*\\(mdns4_minimal\\|mdns_minimal\\)" /etc/nsswitch.conf; then
sudo sed -i '/^hosts:.*files/s/files/files mdns4_minimal [NOTFOUND=return] mdns_minimal [NOTFOUND=return]/' /etc/nsswitch.conf
echo "nsswitch.conf updated to include mdns4_minimal and mdns_minimal."
else
echo "nsswitch.conf already includes mdns_minimal or mdns4_minimal."
fi
# SSHサービスのAvahi公開設定ファイルの作成
echo "Creating Avahi service file for SSH..."
sudo tee /etc/avahi/services/ssh.service > /dev/null <<EOF
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_ssh._tcp</type>
<port>22</port>
</service>
</service-group>
EOF
# AvahiサービスとSSHサービスの再起動・有効化
echo "Restarting and enabling Avahi and SSH services..."
sudo systemctl restart avahi-daemon
sudo systemctl enable avahi-daemon
sudo systemctl restart ssh
sudo systemctl enable ssh
echo "Avahi and SSH setup complete. Your VM should now be discoverable as $NEW_HOSTNAME.local"
echo "You can test with: ping $NEW_HOSTNAME.local and ssh ubuntu@$NEW_HOSTNAME.local"
このスクリプトは、Avahiのインストール、ホスト名の設定、nsswitch.conf
の更新、Avahiサービスの再起動までを一貫して行います。
開発環境構築時の自動化への組み込み
Multipassのcloud-init
機能と組み合わせることで、VM起動時に上記のスクリプトを自動実行し、完全に設定済みの開発環境を瞬時にプロビジョニングできます。
例えば、cloud-init.yaml
ファイルに以下の内容を記述し、multipass launch --cloud-init cloud-init.yaml -n my-dev-vm
のように実行すれば、mDNS/Avahiが設定されたVMが自動的に立ち上がります。
#cloud-config
packages:
- avahi-daemon
- libnss-mdns
- openssh-server
users:
- name: ubuntu
ssh_authorized_keys:
- |
# ここに生成したSSH公開鍵の内容を貼り付けます。
# 例: ssh-rsa AAAAB3NzaC... user@host
runcmd:
- echo "hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4" | sudo tee /etc/nsswitch.conf > /dev/null
- sudo systemctl enable avahi-daemon
- sudo systemctl restart avahi-daemon
- sudo systemctl enable ssh
- sudo systemctl start ssh
write_files:
- path: /etc/avahi/services/ssh.service
mode: '0644'
content: |
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_ssh._tcp</type>
<port>22</port>
</service>
</service-group>
mDNS/Avahi利用時のセキュリティ考慮事項
mDNS/Avahiはローカルネットワーク内で便利ですが、利用にあたってはいくつかのセキュリティ上の考慮事項があります。
ローカルネットワーク内での利用の安全性
mDNSは通常、信頼できるローカルネットワーク内でのみ機能するように設計されています。外部ネットワークからのアクセスは、ルーターやファイアウォールによってブロックされることがほとんどです。そのため、一般的な家庭やオフィス環境での利用においては、大きなセキュリティリスクとなることは稀です。
しかし、公共のWi-Fiなど、信頼できないネットワーク環境でmDNSを有効にする場合は注意が必要です。意図しない情報が公開されたり、悪意のあるユーザーにサービスが発見されたりする可能性があります。
意図しない情報公開への注意
mDNSはサービスをネットワークにブロードキャストするため、公開したくない情報(例えば、開発中の未公開サービスや機密性の高いサービス)が意図せず発見される可能性があります。
これを防ぐためには、以下の対策が有効です。
- ファイアウォールの設定: 必要なポートのみを開放し、不要なサービスは外部に公開しないようにファイアウォールを設定します。
- サービス名の慎重な選択: 公開するサービス名には、機密情報を含めないようにします。
- アクセス制御: サービス自体に認証や認可の仕組みを導入し、不正なアクセスを防ぎます。
シリーズ全体のまとめと次のステップ
この「mDNS完全攻略」シリーズを通して、私たちはmDNS/Avahiが単なる技術的な概念ではなく、開発者の日々のワークフローを劇的に改善する強力なツールであることを学んできました。
mDNS/Avahiで実現できることの再確認
- 直感的なホスト名解決: IPアドレスを覚える必要なく、名前でデバイスやVMにアクセス。
- シームレスなOS間連携: macOSとLinux (Ubuntu) 間でのスムーズな通信。
- 効率的なサービスディスカバリ: ローカルネットワーク上のサービスを自動的に発見し、接続設定を簡素化。
- 開発環境の自動化: スクリプトやcloud-initを活用し、一貫した環境を迅速にプロビジョニング。
シリーズ記事
本シリーズの他の記事もぜひご覧ください。
- 【mDNS完全攻略】第1回:ローカルネットワークの賢い名札「mDNS/Avahi」の基本とmacOSでの活用術
- 【mDNS完全攻略】第2回:Ubuntu (Multipass) にAvahiを導入し、ホスト名解決の基盤を築く
- 【mDNS完全攻略】第3回:macOSとUbuntu (Multipass) を繋ぐ!シームレスなホスト名解決を体験
- 【mDNS完全攻略】第4回:もう迷わない!mDNS/Avahiホスト名解決のトラブルシューティングとデバッグ術
- 【mDNS完全攻略】第5回:Multipassの真価を引き出す!ネットワークモードとmDNSの高度な連携術
- 【mDNS完全攻略】最終回:mDNS/Avahiで開発ワークフローを加速!自動化とサービスディスカバリの応用
さらなる学習リソース
読者へのメッセージ
mDNS/Avahiは、一度その便利さを知ってしまうと手放せなくなる、まさに「縁の下の力持ち」のような技術です。このシリーズが、皆さんの開発ワークフローをよりスムーズに、より効率的にするための一助となれば幸いです。
ぜひ、今日からmDNS/Avahiを積極的に活用し、開発の「魔法」を体験してください。そして、もし新たな活用法や面白い発見があれば、ぜひコメントで共有してください。皆さんのフィードバックが、この技術をさらに深く探求する原動力となります。
この記事が役に立ったら、ぜひSNSでシェアしてください!
この記事について質問や疑問があればコメントしてください。あなたの環境でのmDNS活用事例も教えてください!
免責事項
本記事の内容は、執筆時点での情報に基づいています。ソフトウェアのバージョンアップ等により、手順や設定が変更される可能性があります。本記事の内容を参考に作業を行う際は、ご自身の責任において実施してください。いかなる損害が発生した場合でも、筆者および公開元は一切の責任を負いません。