GKEでサービスを外部公開する際には、 GKE Ingress とそのバックエンド GCP Cloud Load Balancing を使用するのがスタンダードです。が、これには費用 ($18/月~) がかかります。
これをCloudflare DNS + Contourで置き換えて、無料で済ませる方法を説明します。ノードは全台プリエンプティブインスタンスで構いません。
この記事はDoxseyさんによる Kubernetes: The Surprisingly Affordable Platform for Personal Projects を発展させた内容になります。 元記事と同様、紹介する構成は趣味利用にとどめておいてください。
GKEクラスタ作成
まずGKEクラスタを作成してください。3台以上で構築し、プリエンプティブを有効にするのがオススメです。
ちなみにDoxseyさんの記事ではf1-microを使っていますが、 2020年4月18日現在、f1-microではGKEのワーカーノードとして最低限必要なシステムコンポーネントすらまともに動かないようです。 e2-smallにしましょう。
NodeへのHTTP/HTTPSアクセスを許可
ファイアウォール設定で TCP/UDP双方の80・443ポートingressを許可しましょう。 この手順はDoxseyさんの記事に含まれているので、よくわからない方はそちらを参照してください。
ドメイン準備
便宜上、取得したドメイン名をexample.comとします。
Cloudflare DNSの設定
CloudflareのDNSホスティングサービスを使います。 無料から利用できます。この手順もDoxseyさんの記事に含まれているので参照してください。
まずアカウントのホーム画面に移動し、+ Add a site ボタンからサイトを作成します。 前手順で準備したドメイン名を使ってください。
次に作成したサイトのダッシュボードのDNS管理画面に移動します。
Cloudflare DNSで指示される通りに、レジストラで指定しているDNSサーバリストをCloudflare DNSのものに置き換えてください。この置き換えの反映には時間がかかるかもしれません。
この時点では取り敢えずwww.example.comをexample.comにエイリアスするためのCNAMEレコードを作っておきましょう。
kubernetes-Cloudflare-sync のデプロイ
(この手順もDoxseyさんの記事に含まれています)
プリエンプティブインスタンスを使っているとNodeは1日に1度再作成されます。 その際に外部IPが変わってしまうのですが、これを自動的にCloudflare DNSのAレコードに同期するカスタムコントローラkubernetes-Cloudflare-syncがあるのでデプロイしてください。 これを使うとドメイン名からNodeの外部IPを引ける状態が常に維持されます。
デプロイにはCloudflare APIを操作するためのAPIキーが必要になります。詳しくはcalebdoxsey/kubernetes-Cloudflare-syncのREADMEをご覧ください。
Contourをデプロイ
ここまでの手順を行うとドメイン名からノード外部IPを引けるようになっていますが、 そのアクセスをL7制御するコンポーネントがまだデプロイされていません。
Doxseyさんの記事では生のNginx DaemonSetでL7制御しているのですが、 これはあまり使い勝手がよくありません。
そこで生のNginx DaemonSetはやめて、Kubernetesらしく外部アクセス制御するためのIngress Controllerを立てましょう。
今回はIngress ControllerにContourを選びます。 ContourはEnvoyベースのIngress Controllerです。 ダウンタイム無しで設定変更が行える、gRPCを扱える、shadow proxyをサポートしているなどの長所があります。
ここからはContourのデプロイ方法を説明します。バージョンv1.3.0を使用します。
Getting Startedに従いスタンダードにデプロイするとLoadBalancer Serviceを使うのですが、 GKE環境でLoadBalancer Serviceを作ってしまうと前述の課金が発生します。
これを回避するためにHost Networkingデプロイオプションを利用します。 これはEnvoy DaemonSetをホストネットワーク上にデプロイし、Nodeへの80,443アクセスをEnvoyでリッスンするという方式です。
contour/examples/contourのマニフェスト群に以下の変更を加え、applyしてください。
- Envoy用Serviceの
type: LoadBalancer
とexternalTrafficPolicy: Local
の指定を消す - Envoy Podを
hostNetwork: true
にし、dnsPolicy: ClusterFirstWithHostNet
にする - Contourの
serve
コマンドに--envoy-service-http-port=80
と--envoy-service-https-port=443
を追加する
ここまで行うと、HTTPProxyカスタムリソースで任意のServiceをインターネット公開できるようになります。HTTPProxyはIngressリソースの置き換えとなるカスタムリソースです。
ContourはIngressリソースも解釈できますが、Ingressリソースを作成するとGKE Ingress controllerが動作してしまう事故が起きかねないのでHTTPProxyを使うようにしたほうが良いでしょう。
Cert-managerデプロイ
どうせならHTTPSを使用してサービス公開したいのでcert-managerをデプロイしましょう。cert-managerのデプロイはスタンダードなやり方で問題ありません。
ここまでの案内に従うとCloudflare DNSを使用しているはずなので、CloudflareでACME DNS-01チャレンジをするためのIssuer/ClusterIssuerを作成しましょう。CloudflareのAPIトークンを使用します。
HTTP-01チャレンジを選ぶこともできますが、cert-managerでHTTP-01チャレンジを行うとLoadBalancer Serviceが一時的に作られてしまうので、DNS-01チャレンジを利用したほうが良いでしょう。
用意したドメインexample.comについてCertificateリソースを発行し、作成されたSecretをHTTPProxy.spec.tls.secretName
にセットすると、対象のサービスをHTTPSで公開できます。
マニフェスト例を載せておきます。
ClusterIssuer
apiVersion: cert-manager.io/v1alpha2 kind: ClusterIssuer metadata: name: cloudflare-prod spec: acme: email: example@gmail.com privateKeySecretRef: name: cloudflare-account-key server: https://acme-v02.api.letsencrypt.org/directory solvers: - dns01: cloudflare: apiTokenSecretRef: key: api-key name: cloudflare-api-key-secret email: example@gmail.com
Certificate
apiVersion: cert-manager.io/v1alpha2 kind: Certificate metadata: name: example.com spec: commonName: example.com dnsNames: - example.com issuerRef: kind: ClusterIssuer name: clouddns-prod secretName: example-com-prod-tls
HTTPProxy
apiVersion: projectcontour.io/v1 kind: HTTPProxy metadata: name: example spec: routes: - conditions: - prefix: / services: - name: frontend port: 3000 virtualhost: fqdn: example.com tls: secretName: example-com-prod-tls
サブドメイン追加方法
example.comのサブドメイン、たとえばfoo.example.comを追加したい場合の手順を説明します。
Cloudflare DNSにCNAMEレコードを追加し、foo.example.comがexample.comのエイリアスとなるようにしてください。
あとはexample.comのときと同様にHTTPProxyを作るだけです。
他ドメイン追加方法
example.com以外のドメイン、たとえばexample2.comを追加したい場合の手順を説明します。
まずexample.comの時と同様、example2.comをCloudflareにsite追加し、レジストラのDNSサーバをCloudflareのものに切り替えてください。
次にcloudflare-kubernetes-syncを新規でもう1つデプロイしてください。 こうするとexample.comとexample2.comに同じAレコードが割り当てられます。
あとは普通にすでにデプロイ済みのContourでHTTPProxyを作るだけです。
まとめ
GKEでGCPロードバランサとGKE Ingressを使わず、使い勝手を維持したままL7制御するための手順を説明しました。GKEと書きましたが、他のKaaSでも動く気がします。
この記事を書いた後、もう一度この手順を最初から動作確認するのが面倒すぎてやってないので、 なにか書き漏らしがあるかもしれません。 質問があったら書いてください。
あとこの手順はサービスの濫用っぽい気もしなくはないです。だめだったら消すので言ってください。