potpro (ぽとぷろ)
Full-stuck engineer(Not Full-stack)
JS/PHP/Go/Docker/Nginxなど。技術または趣味寄りの発信ブログです。
全 85 記事k8s(k3s)クラスタでLet's Encrypt自動更新のIngressを構築する
k8s(k3s)クラスタでLet's Encrypt自動更新のIngressを構築する
サブタイトル:ぼくのかんがえた最強のk3sクラスタ
前回の記事がかなり読まれたようで何よりなので、せっかくなので次のステップに進む記事をちゃんと書きます。
とはいえ前回と違ってk8sのコアな内容になってくるので、難しいかもしれません。
後、前回の記事の続きですので、まず前回の記事を先に見たほうがいいです。
前回の記事はこちら↓
Oracle Cloudの無料枠だけでKubernetes(k3s)クラスタを構築する
Ingress
タイトルは何やらよくわからないことになっていますが、
今回構築する基盤はkubernetesでHTTP(S)ロードバランサーの役割を果たす、Ingressの機能を使用してL7ロードバランサーを構築します。
前回の記事では、NodePortを使って外部公開していました。
しかしこれだとPortごとに1つしか外部公開できません。複数のポートを設定してOCIのロードバランサーを使うことになってしまいます。あまりスマートではない。
またhttpsでのアクセス自体をk8sで対応出来ていません。最近のWebサービスはhttpsがデフォだと思っているのでほしいところ。 なのでうちのブログでもβの頃から使っていたけど今やかなり有名となったフリーな SSL/TLS 証明書のLet's Encryptを使用してhttpsでサービス公開を行いたいと思います。
この問題の解消としてIngressを使うことにより、k8s内部で複数のサービスに振り分けられるL7ロードバランサー機能を持たせることができ、 機能を併用することでLet's EncryptのACMAプロトコル認証により自動でサービスのhttps化もしてくれる、SaaS環境のような基盤へと進化します。
また、公開するサービスはingressを必ず経由することになりますので、一元管理にもなるため、Ingressを使って公開することはk8s自体推奨される方法となっています。
詳しくは、k8sのIngressのリファレンスがわかりやすいです。(日本語が無いのが厳しいけど・・・
Ingress Controller
k8sのingressはただの機能の名称で、実装自体は含まれてません。そのためingress機能を使用するにはingressをサポートしたリバースプロキシのソフトウェア(またはハードウェア)を使う必要があります。これを実装したソフトウェアをIngress Controllerというらしいです。
Ingress Controllers - Kubernetes
マネージドk8sなどであればこのIngress Controllerは最初からそのクラウドのロードバランサーを使用した実装が大体最初からされていて、簡単に使えるようになっています。
されていない場合は自分で組み込んで使用する必要があります。その中でもNGINX Ingress Controllerが有名で記事も結構多いです。やはりnginxが一番メジャーで、信頼性も高いですからね・・・。
しかし今回はk3sがデフォルトで採用されているtraefikを使用しています。
Traefik
traefikはGo言語で書かれたコンテナサービス向けのリバースプロキシのOSSです。
前回の記事でk3s Serverを起動しましたが、実はtraefikはk3sのServerを起動した時点で起動しています。 これにより最初からingressがk3sで使えるようになっています。
> kubectl get pod --namespace kube-system | grep traefik
svclb-traefik-cv4kr 3/3 Running 0 13d
svclb-traefik-t7bc4 3/3 Running 0 13d
helm-install-traefik-f8l5b 0/1 Completed 27 13d
traefik-65bccdc4bd-prd6b 1/1 Running 0 4d10h
触った感じはGo言語製で設定もシンプルでやりやすそうです。
treafikはlegoを使用しLet's EncryptのACMAプロトコルを使ったTLS証明書自動取得機能と3か月ごとの自動更新を備えています。
これにより、Ingressを使用した通信は自動的にLet's Encryptの証明書を使ったhttpsアクセスが可能となります。
NGINX Ingress Controllerを使う場合は、nginxに取得する機能が無いため、別のアドオンであるcert-manager等を使用する必要があるのですが、こっちはその必要が無くシンプルです。
helm charts
k3sには勝手にtreafikが起動されているといいましたが、どうやって起動しているのか。ドキュメントを見ると設定ファイルはどうやらここにあるようです。
Traefikは、サーバーの起動時にデフォルトでデプロイされます。詳細については、「マニフェストの自動展開」を参照してください。デフォルトの設定ファイルは/var/lib/rancher/k3s/server/manifests/traefik.yamlにあり、このファイルに加えられた変更はkubectl applyと同様の方法でKubernetesに自動的にデプロイされます。 Traefik Ingress Controllerは、ホスト上のポート80、443、および8080を使用します(つまり、これらはHostPortまたはNodePortには使用できません)。 traefik.yamlファイルでオプションを設定することにより、ニーズに合わせてtraefikを微調整できます。詳細については、公式のTraemik for Helm Configuration Parametersのreadmeを参照してください。
そしてまたよくわからないものが出てきましたが、この設定ファイルはhelm charts向けのものになっています。
helmはk8sのパッケージ管理ツールで、chartsはその設定ファイルのメタデータに当たります。
つまりkubeの設定ファイルの設定ファイルなわけで、chartsのドキュメントを見ながらtreafitの設定を変更していきます。
(正直ここまでで覚えることが多過ぎて辛い)
treafikのhelm chartsのドキュメントはここにあります。設定していくだけなのでこれを見るとまあそこまで難しくないです。設定項目めっちゃ多いけど。
Traefik Ingress Controllerの設定
ここでやることは、
Traefik Ingress Controllerは、ホスト上のポート80、443、および8080を使用します(つまり、これらはHostPortまたはNodePortには使用できません)。
と書いてあるようにデフォルトのService設定はtype:LoadBalancerでホストからしか繋がらない問題の解消のため、
NodePortを使用してtreafikに渡すための設定変更と、traefikの動作でLet's Encryptを使用する設定を記述します。
/var/lib/rancher/k3s/server/manifests/traefik.yaml
をコピーして値を変更します。
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: traefik
namespace: kube-system
spec:
chart: https://%{KUBERNETES_API}%/static/charts/traefik-1.77.1.tgz
valuesContent: |-
service:
nodePorts:
https: 30000
serviceType: "NodePort"
rbac:
enabled: true
ssl:
enabled: true
enforced: true
metrics:
prometheus:
enabled: true
kubernetes:
ingressEndpoint:
useDefaultPublishedService: true
acme:
enabled: true
logging: true
staging: false # ステージングテストの時はここをtrueに
challengeType: "tls-alpn-01"
email: "[自分のメールアドレス]"
persistence:
enabled: true
いろいろ考えた結果、こんな感じになりました。難しい・・・
そして、後は同じようにkubectl apply -f traefik.yaml
でデプロイできます。
これでIngress Controllerの設定は完了なはず。次は実際にサービスを公開するためのingress設定を書くことになります。
ちなみに、デフォルト設定で書かれているspec.set
は上手く動作しない可能性が高いようです。spec.valuesContent
は正常に動きます。
この不具合に1週間ほどハマって全然起動しなかった・・・ このあたりはまだまだドキュメントが無いってのも辛いです。
HelmChart CRD doesn't support objects in spec.set
· Issue #276 · rancher/k3s
Traefik自動起動の無効化
Traefikの設定を変更しましたが、実はk3s Serverは起動した瞬間にデフォルト設定を再度読み込みデプロイを行う仕様があるため、つまり再起動するとデフォルト設定に戻されてしまいます。
yamlも初期化されてしまうため、別の場所にコピーしたというわけです。
そのため、以下のコマンドで再起動時に再デプロイしない設定を追加します。
k3s server --no-deploy traefik
systemdで起動しているときは、systemdの設定ファイルに追加すれば良いです。
Ingressのサービス公開設定
前回の記事で公開したサービスをIngress経由で公開設定を行います。 ingressの設定はこんな感じです。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
spec:
rules:
- host: whoami.k8s.potproject.net
http:
paths:
- path: /
backend:
serviceName: whoami-service
servicePort: 8080
backend.serviceNameに公開したいサービス、servicePortにポートを設定します。
今回は新しくサブドメインを作り、whoami.k8s.potproject.net
にしました。yamlは他と同じようにkubectl apply
で反映可能です。
ここにhttpsでアクセスすれば動作するという形です。
当然ながら、ドメインを使用したい場合はDNSでのドメイン設定も必要です。使用しているDNSにて、OCIのLBのPublicIPアドレスとドメインをAレコードで登録してください。
この後は同じようにkubectl apply -f ingress.yml
すればOK。
証明書発行のログを調べる
で、これでうまくいけばいいんですが上手く動かないことも多いです。 上手く動かないのでとりあえずログを見てみます。traefikのPodのログを追えばわかります。
> kubectl get pods --namespace kube-system | grep traefik
traefik-66c9745596-29b82 1/1 Running 0 15s
> kubectl logs traefik-66c9745596-29b82 --namespace kube-system
{"level":"error","msg":"Unable to obtain ACME certificate for domains \"whoami.k8s.potproject.net\" detected thanks to rule \"Host:whoami.k8s.potproject.net\" : unable to generate a certificate for the domains [whoami.k8s.potproject.net]: acme: Error -\u003e One or more domains had a problem:\n[whoami.k8s.potproject.net] acme: error: 400 :: urn:ietf:params:acme:error:connection :: Connection refused, url: \n","time":"2019-11-16T03:22:51Z"}
うまくドメインでの接続が出来ていないようです。 ・・・そういえば、OCI側のロードバランサー設定も必要でした。
OCI ロードバランサーの設定
前回の記事で設定したOCIのロードバランサーも変更します。
まずポートを変えたので内部のポートをTCP30000に変更し、外部ポートのエンドポイントがhttpsとなったのでhttpの80ポートから443ポートに変更します。
OCIのリスナーをTCP 443に設定。
そしてバックエンドセットのバックエンドポートをTCP 30000に設定。ヘルスチェックもTCP 30000にします。OCIにhttpsを渡すことになるため、TCPでのヘルスチェックに変更せざるを得ないようです。
今回、OCIのロードバランサーで設定できるTLS証明書を使用せず、ingressでやるというややこしいことになっているのでこんなことになっています。とはいえOCIの証明書はLet's Encryptの自動更新なんてできないので・・・
後、OCIファイアウォールの443を開けるのも忘れずに。
TLS証明書発行後の疎通確認
これで証明書が無い状態での外部との疎通ができるようになりました。ここから証明書を発行するため、treafikを再起動します。
kubenatesなので、再起動のコマンドというよりはtreafikのPodsを削除すると勝手にまた立ち上がるので、それを利用します。
> kubectl delete pods traefik-66c9745596-29b82 --namespace kube-system
pod "traefik-66c9745596-29b82" deleted
これでも駄目なら、ロードバランサーがうまく返してくれないとか、設定ミスとかそんなところかと思います。
そしてアクセス。ログを見てLet's Encrypt系のエラーが無いのを確認。いけてるはず!
出来たものがこちら。実際に外部公開しています。ものすごいアクセスでも来ない限りは大丈夫でしょう。
https://whoami.k8s.potproject.net/
正常にTLS証明書を取得し、https接続が出来ています。これで完成です。完璧。
所感
自己満足のいくkubernetesクラスタ環境の構築が完了しました。
ちなみに前回からの引継ぎなのでやはりここまでも無料です。やはりコストがかからないとなると気楽に試せていいですね。https化も無料ですし、いい時代ですね。
これで大体のk8sを知ることが出来、それを踏まえて本番でk8sクラスタを運用するのは結構な茨の道とは思いました。
しかしk3sは最初からIngressやPVCが用意されていることもあり、普通に構築入門みたいな記事を見てやるより、かなりとっつきやすいと思います。前回の記事見てみればとりあえず機能とかわからなくても動かすまではみんなできるはず。1行打つだけですしね。
これからも普及してもっと本番とかに投入できればいいですね・・・。本当に・・・。