この記事は 富士通クラウドテクノロジーズ Advent Calendar 2023 18 日目の記事です。
昨日は @earth429 さんの PrometheusのNode Exporterで取得した値とNodeのパフォーマンスを比較してみた でした。
Prometheus、 最初は PromQL がとっつきにくくてとても苦労した記憶があります。
ちなみに kube-prometheus-stack は社内 GitLab 運用でも使っていますがとても便利ですね!ただバージョンをちゃんと追従させていかないと、あとで一気にあげようとすると痛い目を見ます。。。(経験者 )
皆さん。Kubernetes は活用されていますか?
先日、社内の Slack を眺めていたら、「external-dns をニフクラに対応させたいなぁ」みたいな投稿を見かけました。個人的に様々な Kubernetes のアドオンをニフクラに対応させてきたということもあって、 今回は external-dns をどうやったらニフクラに対応させられるのか?を調べて実装してみることにしました。
external-dns とは
external-dns は Kubernetes SIG で開発されているアドオンの一つで、 Kubernetes から クラスター外の DNS レコードを操作できるようにするものです。(クラスター内であれば何もしなくてもデフォルトで Service 作成時にレコードを登録してくれますね)
具体的にはアノテーションが付与された Service (type: LoadBalancer
) や Ingress を external-dns が監視し、対象に応じて適宜外部の DNS サービス上にレコードを登録・更新・削除をしてくれるというもののようです。
Kubernetes 上の操作だけで DNS サービス上のレコードを操作できるようになると、さまざまな運用にも役立ちそうですね!
external-dns がサポートする DNS プロバイダー
本日時点での最新版である v0.14.0 では、
- Google Cloud DNS
- AWS Route 53
- AzureDNS
などの大手クラウドプロバイダーの DNS サービスを始め、様々なサービスが対応されているようです。 (参照)
対応プロバイダーの仲間入りをしたいけど...
さて、こうなると対応プロバイダーの一覧にニフクラも仲間入りしたいところですね!
しかし、直近のプロバイダー追加 PR (例えばこれ) を見てみると、
もう新しいプロバイダーは追加しないよ!対応させたいなら Webhook を使ってね!
のようなコメントがされており、どうも新規で external-dns 本体にプロバイダー追加をすることは叶わなくなってしまったようです。。。
external-dns の Webhook
ここで、コメントに書かれている Webhook とはなんのことなのでしょうか。コメントと一緒にリンクされている PR は下記です。
なるほど。どうもこれは v0.14.0 でリリースされた機能で、DNS レコードを操作する処理を外部の HTTP API に移譲することができる仕組みのようです。
詳細はドキュメントに記載されていますが、具体的には、下記のエンドポイントを持つ API を実装することで、 external-dns 本体にコードを追加することなく、対応するプロバイダーを増やせるということのようです。
-
GET /
- 対象のプロバイダーが管理しているゾーン情報を返却する
-
GET /records
- 登録されているレコード一覧を返却する
-
POST /adjustendpoints
- プロバイダ固有のレコード設定等があれば、情報を付け加えたり書き換えて返却する
- 例えば AWS Route 53 プロバイダーでは ALIAS レコードに対する処理などがされている
-
POST /records
- レコードの登録・更新・削除をする
Webhook を実装するには
さて、 Webhook を実装すれば様々なプロバイダーに対応できそうなことはわかりました。では Webhook はどのように実装すればよいのでしょうか?
上述のとおり、特定のエンドポイントを持つ HTTP API を作成すればよいだけではあります。つまり、実装言語は何でも良いです。ですが、 external-dns にコミットされているコードを活用すると、かなり簡単に Webhook を作成できるようです。ただし、external-dns は Golang で書かれているため、この方法を使う場合は Golang 実装限定になります。
ここでは external-dns のコードを活用した方法に限定して話を進めていきます。
Provider インターフェースを満たす実装を書く
まず、 provider.Provider
インターフェースを実装する struct を実装します。
ただし必ず実装しなければならないのは
Records(ctx context.Context) ([]*endpoint.Endpoint, error)
ApplyChanges(ctx context.Context, changes *plan.Changes) error
の 2 つだけで、
AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error)
GetDomainFilter() endpoint.DomainFilter
は動かすだけであれば特に実装せずに OK です。その場合は下記のように struct に provider.BaseProvider
を埋め込んでおくのが楽です。
type nifcloudProvider struct {
provider.BaseProvider
}
HTTP サーバーを作成・起動する
さて、 provider.Provider
インターフェースを満たす実装ができたら、その実装を webhook.StartHTTPApi
に渡してあげましょう。
StartHTTPApi
関数は前述の Webhook が実装すべき HTTP API エンドポイントと provider.Provider
の実装をマッピングし、 HTTP サーバーを起動してくれます。
具体的には下記のルーティングが設定されます。
-
GET /
:GetDomainFilter
-
GET /records
:Records
-
POST /records
:ApplyChanges
-
POST /adjustendpoints
'AdjustEndpoints
ニフクラ向けの Webhook を書いてみた
というわけで、実装方針が見えたのでニフクラ DNS 向けの Webhook を書いてみました。
足りない実装が結構ありますが、さらっと試すくらいならこの実装で十分です。
メインロジックである provider.Provider
インターフェースを実装している箇所は https://github.com/aokumasan/external-dns-nifcloud-webhook/blob/main/internal/cloud/nifcloud.go あたりです。
試してみる
external-dns-nifcloud-webhook のインストール
example/webhook.yaml
を使えばインストールできるようにしてあります。 Secret の nifcloud-secrets
は使用するニフクラアカウントのアクセスキーとシークレットキーに書き換えてください。(Helm chart 作ろうとしましたが、間に合いませんでした )
$ kubectl apply -f example/webhook.yaml
external-dns のインストール
external-dns 本体を Helm でインストールします。
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm upgrade --install external-dns external-dns/external-dns \
-n external-dns \
--set 'image.tag=v0.14.0' \
--set 'provider=webhook' \
--set 'extraArgs={--webhook-provider-url=http://external-dns-nifcloud-webhook:8888}'
インストール時に下記の設定をしています。
- 2023/12/18 時点で最新版の external-dns の helm chart が
v0.13.6
だったため、image.tag
に webhook 対応版のv0.14.0
を指定 - webhook を使うため、
provider
にwebhook
を指定 - webhook の URL を external-dns に設定するため、
externalArgs
にwebhook-provider-url=<webhook URL>
を指定
kubectl -n external-dns get pods
して、 external-dns 本体と webhook の 2 つの Pod が Running になっていれば OK です。
$ kubectl -n external-dns get po
NAME READY STATUS RESTARTS AGE
external-dns-54986b5868-s7sjj 1/1 Running 0 64s
external-dns-nifcloud-webhook-5bbcd9b64d-n45qt 1/1 Running 0 79s
Ingress リソースを適当に作成してみる
今回はサンプルとして、 Hello
と表示するだけの Web ページを Ingress 経由で公開してみようと思います。(※事前に ingress nginx を導入してあります。)
下記のマニフェストを apply します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo
spec:
replicas: 1
selector:
matchLabels:
app: echo
template:
metadata:
labels:
app: echo
spec:
containers:
- name: app
image: hashicorp/http-echo
args:
- -text=Hello
- -listen=:8080
---
apiVersion: v1
kind: Service
metadata:
name: echo-service
spec:
selector:
app: echo
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: example.test.aokuma.net
http:
paths:
- backend:
service:
name: echo-service
port:
number: 80
path: /
pathType: Prefix
作成してしばらくすると、対応するレコードがニフクラ DNS に無事に登録されていました!!
ちなみに現状は対象の Ingress オブジェクトを削除しても DNS レコードが削除されなくて、原因を調べてましたがちょっと時間切れでした。。。後日controllerのコードを読んで原因を探ろうと思います。
さいごに
今回は external-dns にプロバイダーを追加する方法を探り、その中で Webhook 機能を調査し、実際にニフクラ DNS にレコードを登録できるところまで実装してみました。あくまでも調査目的のため簡単な実装しかしていませんが、 external-dns の動作を知るよいきっかけにはなったと思います。今後やる気が出てきたら、もっと厳密な実装をチャレンジしてみようと思います。
また、Kubernetes 関連のアドオンで Webhook に対応しているものというと、 cert-manager なんかもあるのですが、 DNS サービスのようにプロバイダーがたくさん存在するようなものは今回のような webhook の仕組みがとても生きてくるような気がしますし、実装する側も本家にコントリビュートするよりも気楽に開発できてさらっと試す分には良いなと思いました。
明日は @sameshima_alt さんが「UbuntuでDAW環境を構築するためにN100ミニPCを買った話」という記事を書いてくれるみたいです。自分も N100 のミニ PC を使って NAS に貯めてある写真を Amazon Photos に同期する仕組みを作ってたりしますが、 N100 マシンはアイデア次第でいろいろ活用できて面白いですね!
※本記事の内容は 2023/12/18 (v0.14.0) 現在の情報になります。Webhook に関してはまだ公開されたばかりの機能であるため、今後仕様が大きく変更される可能性もあります。また、完全に動く実装までたどり着けなかったこともあり、理解が間違っている可能性もありますのでご承知おきください。