CoreDNSとは何か?
CoreDNSは、Go言語で実装された柔軟で拡張可能なDNSサーバーです (CoreDNS の仕組みとプラグイン - ストイックに生きたい) (Using CoreDNS for Service Discovery | Kubernetes)。もともとCaddyサーバーの仕組みを基に構築されており、CoreDNS自体のほとんどの機能は「プラグイン」 として提供されています (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。たとえばゾーンファイルの提供やキャッシュ、メトリクス収集といった機能はすべてプラグインとして実装されており、必要なプラグインを組み合わせてDNSサーバーの振る舞いをカスタマイズできます ( Writing Plugins for CoreDNS )。CoreDNSはCNCF (Cloud Native Computing Foundation) にホストされており、KubernetesクラスターのデフォルトDNSサーバー(kube-dnsの後継)としても利用されています (CoreDNS の仕組みとプラグイン - ストイックに生きたい) (Using CoreDNS for Service Discovery | Kubernetes)。
CoreDNSにおけるプラグインの役割
CoreDNSにおけるプラグインは、DNSサーバーの機能をモジュール単位で拡張・変更する役割を担います (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。プラグインを追加・有効化することで、DNSサーバーに新たな機能を簡単に組み込めます。例えば:
- フォワード (forward) プラグイン: 特定のドメインに対するクエリを別のDNSサーバー(例: 1.1.1.1)に転送します (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。
- キャッシュ (cache) プラグイン: DNSクエリ結果をTTLに基づいてキャッシュし、高速な応答を可能にします (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。
- ログ (log) プラグイン: DNSクエリを標準出力に記録します。トラブルシューティングやアクセス解析に役立ちます (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。
- エラー (errors) プラグイン: サーバー内で発生したDNSエラーを検知してログに出力します (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。
-
リライト (rewrite) プラグイン: クエリ名を書き換えてから処理を続行します。例として
foo.example.com
をfoo.default.svc.cluster.local
に書き換える、などの用途があります ( How Queries Are Processed in CoreDNS ) ( How Queries Are Processed in CoreDNS )。 - その他: etcd連携のetcdプラグインや、後述するkubernetesプラグインなど、さまざまなユースケースに対応するプラグインが用意されています。
プラグインはCorefile(CoreDNSの設定ファイル)に記述することで有効化され、記述されたプラグインの組み合わせによってDNSサーバーの動作が決定します (CoreDNS の仕組みとプラグイン - ストイックに生きたい) (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。必要な機能だけをプラグインとして組み込み、不要な機能は外して軽量化することも可能です。このプラグイン機構によって、CoreDNSは非常に高い拡張性と柔軟性を実現しています。
有名なプラグイン例: Kubernetesプラグイン
数ある公式プラグインの中でもkubernetesプラグインは特に有名で、Kubernetes環境におけるサービスディスカバリ(内部DNS)を実現する重要なプラグインです (Using CoreDNS for Service Discovery | Kubernetes)。Kubernetesプラグインを有効にしたCoreDNSは、クラスタ内部のServiceやPodに対応するDNSレコードを自動的に提供します。これにより、Kubernetes内のアプリケーションは<サービス名>.<ネームスペース>.svc.cluster.local
といった名前でお互いを名前解決できます。
なぜKubernetesプラグインが必要か? 従来、Kubernetesではkube-dnsというコンポーネントが内部DNSを提供していましたが、CoreDNSはその後継として採用されました (Using CoreDNS for Service Discovery | Kubernetes)。CoreDNS本体はプラグインの組み合わせで動作を変えられるため、Kubernetesに特化したプラグインを追加するだけでクラスタDNSの要件を満たせます。kubernetesプラグインはKubernetes APIと連携し、サービスやエンドポイントの変更を監視してDNSレコードに反映します。CoreDNS起動時にはこのプラグインがKubernetes APIサーバーへの接続を確立し、リソース(ServiceやPod情報)のウォッチを開始します ( kubernetes )。初期同期が完了するまでは最大5秒間DNS応答を遅らせ、同期完了後にクエリ応答を開始します ( kubernetes )(5秒経っても同期できない場合は同期が続く間SERVFAILで応答)。このようにしてクラスタ内のリソース変化(新規サービス追加やPod消滅など)に追随し、常に最新のDNSレコードを提供します。また、エンドポイント情報取得には効率化のためKubernetesのEndpointSlices APIを利用しています ( kubernetes )。
kubernetesプラグインはバックエンド系プラグインの一例であり、外部システム(Kubernetes)をデータソースとしてDNSレコードを生成する役割を持ちます ( How Queries Are Processed in CoreDNS )。このプラグインのおかげで、Kubernetesクラスターでは各種サービスを名前でスムーズに発見でき、CoreDNSはクラウドネイティブ環境におけるサービスディスカバリの中核を担っています。
CoreDNSプラグインの仕組み (アーキテクチャ)
CoreDNSでは、プラグインは**チェイン(鎖状)**に連結されてDNSクエリを処理します。各プラグインは順番に呼び出され、必要に応じて次のプラグインに処理を渡しながら最終的な応答を生成します ( How Queries Are Processed in CoreDNS ) ( How Queries Are Processed in CoreDNS )。Corefileに記述された各サーバーブロック(ゾーンごとの設定)が、内部的にはひとつのプラグインチェイン(dnsserver.Server
)となります。問い合わせが来ると、そのクエリにマッチするゾーンのチェイン上でプラグインが順次実行されます ( How Queries Are Processed in CoreDNS ) ( How Queries Are Processed in CoreDNS )。プラグインの実行順序はCoreDNSをビルドする際に決まっており、plugin.cfg
ファイルに列挙された順番がそのままチェインの順序になります ( How Queries Are Processed in CoreDNS )。したがって、Corefile上の記述順と実行順が必ずしも一致しない点には注意が必要です(例えばcache
プラグインはCorefile下部に書かれていても、ビルド時の順序で後段に配置されていれば最終処理ではありません ( How Queries Are Processed in CoreDNS ))。
( How Queries Are Processed in CoreDNS )CoreDNSにおけるプラグインチェインの概念図。上図のように、CoreDNSはCorefileの各サーバーブロックに対応してプラグインチェインを構成し、問い合わせは該当するチェイン上の各プラグインを順に通過して処理されます ( How Queries Are Processed in CoreDNS ) ( How Queries Are Processed in CoreDNS )。プラグインには種類があり、一部はリクエスト処理自体を行わず設定のために存在します(例: root
やstartup
などは実行時の処理チェインに入らない) ( How Queries Are Processed in CoreDNS )。通常のプラグインはリクエストを何らか処理した後、基本的には次のプラグインへ処理を渡します ( How Queries Are Processed in CoreDNS )。たとえば前述のrewriteプラグインはクエリを書き換えた後で次のプラグインに渡し、チェイン後段で応答が戻ってきたら質問セクションを書き換える前の状態に戻してクライアントに返します ( How Queries Are Processed in CoreDNS )。一方、バックエンド系プラグイン(例: kubernetesやfileなどゾーンデータを返すもの)はそれ自身が権威データソースとなりうるため、基本的に自分で回答(もしくはNXDOMAIN)を返してチェインを終端させます ( How Queries Are Processed in CoreDNS )。ただし、バックエンド系でも fallthrough
オプション が指定されると、該当ゾーンにレコードが無い場合にNXDOMAINの代わりに次のプラグインに処理を委譲できます ( How Queries Are Processed in CoreDNS )。
プラグインは実装上、Goのインターフェースであるplugin.Handler
を満たすことで定義されます。plugin.Handler
はHTTPサーバーのハンドラに似たインターフェースで、DNSクエリ用のServeDNS
メソッド(引数にDNSクエリとコンテキスト、レスポンスライターを取る)とName()
メソッド(プラグイン名を返す)から構成されています ( Writing Plugins for CoreDNS ) ( Writing Plugins for CoreDNS )。開発者はプラグインごとに構造体を定義し、このインターフェースを実装します。典型的には以下のようになります ( Writing Plugins for CoreDNS ):
// プラグインのハンドラ構造体(Nextは次のプラグインへの参照)
type MyPlugin struct {
Next plugin.Handler
// 他に必要な設定フィールドがあれば追加
}
// プラグイン名を返す
func (m MyPlugin) Name() string { return "myplugin" }
// DNSクエリを処理するメソッド
func (m MyPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) {
// (ここで何らかの処理を行う)
return m.Next.ServeDNS(ctx, w, req) // 次のプラグインに処理を委ねる
}
上記のように、自作プラグインのServeDNS
内で基本は次のプラグイン (m.Next
) のServeDNS
を呼び出して処理を渡します ( Writing Plugins for CoreDNS )。これにより、現在のプラグインがチェイン上のフィルタのように機能します。ただし、プラグインが最終的な応答を自分で返せる場合(例えば特定のドメインに対して固定のIPを返すカスタムプラグインなど)は、次のプラグインを呼ばずにここで応答を返してチェインを終了しても構いません (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。応答を返す際には、github.com/miekg/dns
パッケージを用いてDNSメッセージを作成し(例: Aレコードのdns.Msg
を構築)、dns.ResponseWriter
のWriteMsg()
でクライアントに返送します (Developing Custom Plugins for CoreDNS - DEV Community) (Developing Custom Plugins for CoreDNS - DEV Community)。一方、次のプラグインに処理を渡す場合でも、plugin.NextOrFailure(pluginName, Next, ctx, w, req)
といったヘルパーを使うことで、万一チェインの次にプラグインが存在しない場合でも適切に処理できます (example/example.go at master · coredns/example · GitHub) (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。
各プラグインでは、必要に応じてログ出力やメトリクスカウントも行えます。CoreDNSにはplugin/pkg/log
というログパッケージが用意されており、これを使ってプラグイン名付きのロガーを簡単に生成できます (example/example.go at master · coredns/example · GitHub) (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。log := clog.NewWithPlugin("plugin名")
のように設定すれば、log.Info(...)
やlog.Debug(...)
で情報やデバッグログを出力可能です (Developing Custom Plugins for CoreDNS - DEV Community) (Developing Custom Plugins for CoreDNS - DEV Community)。デバッグログはdebug
プラグインをCorefileで有効化することで出力されるようになります (CoreDNS の仕組みとプラグイン - ストイックに生きたい) (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。
CoreDNSプラグイン開発の手順 (ステップバイステップ)
それでは、CoreDNSのプラグインを自作する具体的な手順を順を追って説明します。ここではGo言語に習熟しているがCoreDNSプラグイン開発は初めてという読者を想定し、基本的な流れを示します (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。
-
プラグインの雛形作成: 開発を始める前に、プラグインのプロジェクトディレクトリ(Goモジュール)を用意します。公式リポジトリには
example
という最小実装のサンプルプラグインが提供されており、参考になります (CoreDNS の仕組みとプラグイン - ストイックに生きたい) (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。まずはこのような雛形をベースに、自分のプラグイン用にパッケージを作成しましょう。例としてプラグイン名をfoo
とします。 -
プラグインの登録 (
init
関数): CoreDNSに新しいプラグインを認識させるには、プラグインのパッケージでplugin.Register()
を呼び出して登録する必要があります。これは通常、プラグインパッケージのinit()
関数内で行います ( Writing Plugins for CoreDNS )。例えば以下のように記述します:package foo import "github.com/coredns/coredns/plugin" func init() { plugin.Register("foo", setup) }
この
plugin.Register("foo", setup)
により、CoreDNSはCorefile内でfoo
というディレクティブ(プラグイン名)を見つけた際に対応するセットアップ関数を呼び出すようになります ( Writing Plugins for CoreDNS )。プラグイン名はコアプラグインと重複しないユニークなものを付けましょう。 -
セットアップ関数の実装: 次に、上で登録した
setup
関数を実装します。シグネチャはfunc setup(c *caddy.Controller) error
で、Corefileの該当部分のパースとプラグインの初期設定を行います ( Writing Plugins for CoreDNS )。caddy.Controller
(CoreDNSはCaddyサーバーv1由来のコンフィグパーサーを使用)は、トークン化された設定行を提供します。一般的な実装としては、次のようになります ( Writing Plugins for CoreDNS ) (example/setup.go at master · coredns/example · GitHub)。func setup(c *caddy.Controller) error { // プラグイン名トークンを読み飛ばし、次のトークンへ c.Next() // 必要に応じて引数をパース if c.NextArg() { arg := c.Val() // 例えば一つだけ引数を取る場合 // ... arg を使った設定処理 ... } if c.NextArg() { // 想定外の追加引数があればエラーを返す return c.ArgErr() } // プラグインチェインに自分のプラグインを追加 dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { return &FooPlugin{Next: next /*, 他のフィールド設定 */} }) return nil }
上記では、
c.Next()
でディレクティブ名(foo
)をスキップし、c.NextArg()
で必要な引数を1つ読み取っています ( Writing Plugins for CoreDNS )。もし引数が期待数に満たない/多い場合はc.ArgErr()
でエラーを返し、CoreDNSの起動プロセスにエラーを伝えます ( Writing Plugins for CoreDNS )。最後に、dnsserver.GetConfig(c).AddPlugin(...)
を呼び出してこのプラグインをチェインに登録しています (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。AddPlugin
には次のプラグインを受け取って自分のハンドラを返す関数を渡します。上記例では、無名関数内で自分のプラグイン構造体(FooPlugin
)を生成し、フィールドNext
に渡されたnext
ハンドラをセットして返しています (example/setup.go at master · coredns/example · GitHub)。これにより、CoreDNSのプラグインチェイン中にfoo
プラグインが組み込まれることになります。 -
プラグインハンドラの実装: プラグインの主要部分である
plugin.Handler
インターフェースを実装します。まず構造体を定義します。例えば:type FooPlugin struct { Next plugin.Handler // 次のプラグイン(チェインの後続) Option string // 何らかのオプション(引数)例 }
ここで
Option
は設定で受け取った引数など、プラグインが動作に必要なデータを保持するためのフィールドです(必要なものを自由に追加できます)。次に、この構造体にName()
メソッドとServeDNS()
メソッドを実装します。func (f *FooPlugin) Name() string { return "foo" } func (f *FooPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) { // 例: クエリ情報を取得してログ出力 state := request.Request{W: w, Req: req} log.Infof("Query for %s from %s", state.Name(), state.IP()) // 他の処理 ... (必要ならreqを改変したりできます) // 次のプラグインに処理を委譲 return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, req) }
Name()
はプラグイン名を返すだけの簡単な関数です ( Writing Plugins for CoreDNS )。ServeDNS()
はDNSクエリごとに呼ばれる処理本体で、この中でプラグイン固有のロジックを実行します (Developing Custom Plugins for CoreDNS - DEV Community)。上記の例では、request.Request
というヘルパー型を使ってクエリ名や依頼元IPを取得し、事前に用意したロガー(後述)で情報を記録しています。その後、plugin.NextOrFailure
を用いて次のプラグインに処理を渡し、その戻り値(DNS応答コードとエラー)をそのまま返しています (example/example.go at master · coredns/example · GitHub)。自プラグインでクエリを完全に処理し応答を返す場合は、ここでw.WriteMsg(responseMsg)
を呼んでdns.RcodeSuccess
などをリターンし、Next
を呼ばずに終了します (Developing Custom Plugins for CoreDNS - DEV Community) (CoreDNS の仕組みとプラグイン - ストイックに生きたい)。 -
ログ出力やユーティリティの利用: 開発時には適宜ログを出力すると便利です。上記コード中の
log
は、プラグインごとにロガーを生成したものです。プラグインではclog "github.com/coredns/coredns/plugin/pkg/log"
をインポートし、var log = clog.NewWithPlugin("foo")
のようにしておくと、ログメッセージに自動で[INFO] plugin/foo:
のようなプレフィックスが付与されます (Developing Custom Plugins for CoreDNS - DEV Community) (example/example.go at master · coredns/example · GitHub)。log.Info(...)
やlog.Debug(...)
を使うことで情報・デバッグログを出力できます(デバッグログはCorefileでdebug
プラグインを有効にした場合に出力されます (CoreDNS の仕組みとプラグイン - ストイックに生きたい))。また、github.com/miekg/dns
パッケージを利用してDNSレコードを表す構造体(例えばdns.A
やdns.TXT
など)を生成し応答に入れることもできます (Developing Custom Plugins for CoreDNS - DEV Community)。さらに、CoreDNS組み込みのメトリクス収集機能(Prometheus統合)を利用してクエリ数などをカウントすることも可能です。必要に応じて公式ドキュメントを参照してください (Developing Custom Plugins for CoreDNS - DEV Community)。 -
プラグインのビルドと組み込み: プラグインの実装ができたら、それを含めてCoreDNSをビルドします。CoreDNSはGoのビルド時にプラグインを静的に組み込む設計になっており、動的ロードはできません。そのため2通りの方法があります ( Compile Time Enabling or Disabling Plugins ) ( Compile Time Enabling or Disabling Plugins )。
-
(A) CoreDNS本体に組み込んでビルド: CoreDNSのソースコードを取得し、
plugin.cfg
というファイルに自作プラグインを追加してビルドします ( Compile Time Enabling or Disabling Plugins ) ( Compile Time Enabling or Disabling Plugins )。plugin.cfg
には各プラグインの名前とパッケージパスが並んでおり、そこに例えばfoo:github.com/あなたのユーザ名/foo
というエントリを追記します (Developing Custom Plugins for CoreDNS - DEV Community)。プラグインの順序はこのファイル内の記述順で決まるため、既存プラグインとの前後関係に注意してください ( Compile Time Enabling or Disabling Plugins )。追加後、go get github.com/あなたのユーザ名/foo
でプラグインのコードを取得(またはgo.modにreplace
を書いてローカル参照)し、CoreDNSディレクトリ内でgo generate && go build
もしくはmake
を実行すると、新しいプラグインを含んだcoredns
バイナリが生成されます ( Compile Time Enabling or Disabling Plugins )。ビルド後、./coredns -plugins
コマンドで組み込まれたプラグイン一覧を確認でき、自作プラグインが含まれていれば成功です (Developing Custom Plugins for CoreDNS - DEV Community)。 -
(B) 外部からラッピングしてビルド: CoreDNSのコードを直接変更せずにプラグインを組み込む方法です ( Compile Time Enabling or Disabling Plugins )。Goの外部パッケージとしてCoreDNSとプラグインをインポートし、プラグインの有効順序リストをコード上で設定した上で
coremain.Run()
を呼ぶ小さなmain.go
を作成します ( Compile Time Enabling or Disabling Plugins ) ( Compile Time Enabling or Disabling Plugins )。例えば以下のようなコードになります ( Compile Time Enabling or Disabling Plugins ) ( Compile Time Enabling or Disabling Plugins ):package main import ( _ "github.com/あなたのユーザ名/foo" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/coremain" ) // プラグイン実行順序の上書き var directives = []string{ "foo", // ... 他の必要なプラグイン ... "forward", "cache", "log", "errors", "health", "reload", // 既存のプラグインも全て列挙する必要があります } func init() { dnsserver.Directives = directives } func main() { coremain.Run() }
こうすることで、CoreDNSのデフォルトプラグイン順序を自分で定義した
directives
に置き換えて起動できます ( Compile Time Enabling or Disabling Plugins ) ( Compile Time Enabling or Disabling Plugins )。import
では自作プラグインパッケージをブランクインポート(_
)することでinit()
関数を実行し、プラグイン登録を済ませています ( Compile Time Enabling or Disabling Plugins )。この方法であればCoreDNS本体に変更を加えないためメンテナンスしやすいですが、CoreDNSのバージョンアップに追随するために自前のmain.go
を更新する手間は発生します。
-
以上が基本的な開発の流れです。次章では、実際に非常にシンプルなプラグインのコード例を示します。
プラグイン実装例: リクエストをログに記録するプラグイン
最後に、CoreDNSプラグインの具体的な実装例としてDNSリクエストをログに記録するだけの簡単なプラグインを示します。これは前述のステップに沿って作成したものです。
// パッケージと必要なインポート
package foo
import (
"context"
"github.com/coredns/caddy"
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/request"
clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/miekg/dns"
)
// ロガーをプラグイン名付きで作成
var log = clog.NewWithPlugin("foo")
// プラグインの登録
func init() {
plugin.Register("foo", setup)
}
// Corefileパーサー兼セットアップ関数
func setup(c *caddy.Controller) error {
c.Next() // "foo"トークンをスキップ
// このプラグインは追加の設定項目を持たないため、余分な引数があればエラー
if c.NextArg() {
return c.ArgErr() // 未知の設定があればエラー
}
// プラグインをチェインに登録
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
return &FooPlugin{Next: next}
})
return nil
}
// プラグインハンドラ構造体
type FooPlugin struct {
Next plugin.Handler
}
// Nameメソッド (プラグイン名を返す)
func (f *FooPlugin) Name() string { return "foo" }
// DNSリクエストを処理する
func (f *FooPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) {
// リクエスト情報を取得
state := request.Request{W: w, Req: req}
qName := state.Name() // 質問のドメイン名
qType := state.Type() // 質問のタイプ(A, AAAAなど)
clientIP := state.IP() // クライアントのIPアドレス
// ログ出力
log.Infof("DNS query for %s (%s) from %s", qName, qType, clientIP)
// チェインの次のプラグインに処理を渡す
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, req)
}
上記foo
プラグインは非常にシンプルで、追加のオプションも持たず、受け取ったDNSクエリの名前・種類・送信元IPをログに記録してから次のプラグインに処理を引き継ぐだけの動作をします。request.Request
構造体を使うことでreq.Question
を手作業で解析することなくName()
やType()
を取得でき、便利です (Developing Custom Plugins for CoreDNS - DEV Community)。ログ出力にはplugin/pkg/log
経由で作成したロガーを用い、Infof
メソッドで情報レベルのログを書き出しています(実行時に-log stdout
オプションを付けて起動すればコンソール上で確認できます)。
このプラグインを利用するには、前述の手順6に従ってCoreDNSに組み込みます。たとえばCoreDNSソースに組み込む場合、plugin.cfg
にfoo:github.com/yourname/foo
を追加してビルドし、Corefileに以下のように記述します。
. {
foo # 自作プラグインfooを有効化
forward . 1.1.1.1
log
}
上記の設定でCoreDNSを起動し、任意のDNSクエリを投げると、通常のlog
プラグインの出力に加えてfoo
プラグインによるログが表示されるはずです。例えばクライアントからexample.com A
レコードの問い合わせが来た場合、foo
プラグインからは次のようなログが出力されます(実行環境によって形式は多少異なります)。
[INFO] plugin/foo: DNS query for example.com. (A) from 192.168.0.10
このように、CoreDNSのプラグインを開発すれば、自分の用途に合わせたカスタムなDNS処理を組み込むことができます。Go言語で実装された柔軟なプラグインシステムを活用し、ぜひCoreDNSを拡張してみてください。
参考文献: CoreDNS公式サイトのプラグイン開発ドキュメント ( Writing Plugins for CoreDNS ) ( Writing Plugins for CoreDNS )や、CoreDNSのGitHub上のサンプルプラグインコード (example/example.go at master · coredns/example · GitHub) (example/example.go at master · coredns/example · GitHub)も併せて参照してください。本記事の内容は2025年時点の情報に基づいており、CoreDNSのバージョンアップにより一部変更となる可能性があります。