個人向けの格安Kubernetes環境はGKEが良さそう


はじめに

安い個人k8s環境を色々模索してきて、これまで以下のようなものに手を出しました。

  • Docker for mac
    →唸り始めるとどうしようもないので、あまり動かしたくない。

  • Microk8s on Lightsail 1Core 500MB
    →動きませんでした。(そもそも稼働条件を満たしていない)

  • k3s on Lightsail 1Core 500MB
    →一応最小の稼働条件ですが、ギリギリ動きませんでした。

  • k3s or kubeadm on Raspi
    →現役です。ただ外部向けとかインターネットからアクセスしたいアプリは動かしにくいし、Arm64イメージをビルドするのが面倒。

ずっとAWSばかり使ってきたので仮想マシンはLightsailが一番安いと思い込んでいました。
今回、下記の素晴らしい記事を参考にGKEを作成したらすごく良かったのでそのメモです。

趣味GKEのIngressを無料で済ませる
https://livingalone.hatenablog.com/entry/2020/04/19/100000

Kubernetes: The Surprisingly Affordable Platform for Personal Projects
http://www.doxsey.net/blog/kubernetes--the-surprisingly-affordable-platform-for-personal-projects

Preemptive Instanceへの対応

また今回Nodeで利用するプリエンプティブル VMへの対応として、NodeIPをRoute53に同期するカスタムコントローラーも作成しましたので、最後に紹介します。
https://github.com/jlandowner/kubernetes-route53-sync

GKE作成時の設定

GKEはControl Planeが無料なので、基本的にはNodeの料金が主です。
Nodeのリソース節約のために、GKEの作成画面で色々オプションを削除していきます。チェックは基本外します。

まずNode数をデフォルトの3から減らします(今回は1つに)

インスタンスタイプはe2-smallのPreemptive Instance
https://cloud.google.com/compute/vm-instance-pricing?hl=ja#e2_sharedcore_machine_types
なんと2コア2GBメモリで一ヶ月あたり$3.67です。

プリエンプティブル VM の実行
https://cloud.google.com/kubernetes-engine/docs/how-to/preemptible-vms?hl=ja
プリエンプティブル VM は、最長持続時間が 24 時間で、可用性が保証されない Compute Engine VM インスタンスです。

今回は1Nodeの想定のため、必ず1日に最低一回ダウンタイムが発生しますが、個人利用なので許容します。

自分はStackdriverもいらないです。

一番重要です。ネットワークロードバランサはお高いので、必ず無効にします。

GKE作成後の設定

GKEのkubeconfigの設定方法は割愛します。
https://cloud.google.com/kubernetes-engine/docs/quickstart?hl=ja#get_authentication_credentials_for_the_cluster

公式ドキュメントにGKEのアドオンを削減方法があるので、それにしたがってリソースを削減していきます。

小規模クラスタでアドオン リソース使用を削減する
https://cloud.google.com/kubernetes-engine/docs/how-to/small-cluster-tuning?hl=ja

以下自分が行ったものです。

dashboard無効
gcloud container clusters update cluster-name \
        --update-addons=KubernetesDashboard=DISABLED
kube-dns-autoscalerの無効とkube-dnsの削減
kubectl scale --replicas=0 deployment/kube-dns-autoscaler --namespace=kube-system
kubectl scale --replicas=1 deployment/kube-dns --namespace=kube-system    

この段階でのクラスターの状態はこちらです。
Dashboardを無効にしてもmetric-serverは消えないようなので、要らない人は消してください(GKEのアドオンは消えないものもあるので、消えなければScale 0に)

$ kubectl get all -A                                                          
NAMESPACE     NAME                                                      READY   STATUS    RESTARTS   AGE
kube-system   pod/kube-dns-5f7d7d8796-rc96q                             3/3     Running   0          46m
kube-system   pod/kube-proxy-gke-cluster-1-default-pool-3e1d5b15-kb4r   1/1     Running   0          46m
kube-system   pod/metrics-server-v0.3.3-7599dd85cd-nx7vq                2/2     Running   0          46m


NAMESPACE     NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
default       service/kubernetes       ClusterIP   10.8.0.1     <none>        443/TCP         47m
kube-system   service/kube-dns         ClusterIP   10.8.0.10    <none>        53/UDP,53/TCP   46m
kube-system   service/metrics-server   ClusterIP   10.8.8.93    <none>        443/TCP         46m

NAMESPACE     NAME                                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                                                              AGE
kube-system   daemonset.apps/metadata-proxy-v0.1        0         0         0       0            0           beta.kubernetes.io/metadata-proxy-ready=true,beta.kubernetes.io/os=linux   46m
kube-system   daemonset.apps/nvidia-gpu-device-plugin   0         0         0       0            0           <none>                                                                     46m

NAMESPACE     NAME                                    READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   deployment.apps/kube-dns                1/1     1            1           46m
kube-system   deployment.apps/kube-dns-autoscaler     0/0     0            0           46m
kube-system   deployment.apps/metrics-server-v0.3.3   1/1     1            1           46m

NAMESPACE     NAME                                               DESIRED   CURRENT   READY   AGE
kube-system   replicaset.apps/kube-dns-5f7d7d8796                1         1         1       46m
kube-system   replicaset.apps/kube-dns-autoscaler-6b7f784798     0         0         0       46m
kube-system   replicaset.apps/metrics-server-v0.3.3-7599dd85cd   1         1         1       46m

$ kubectl get nodes
NAME                                       STATUS   ROLES    AGE   VERSION
gke-cluster-1-default-pool-3e1d5b15-kb4r   Ready    <none>   50m   v1.15.11-gke.9

これでクラスターの作成は完了です。
以降はこの環境を少し使いやすくしていきたいと思います。

Preemptive Instanceへの対応

あとでIngress Controllerをセットアップしますが、ネットワークロードバランサーは作成しないため、基本的にIngress ControllerはNodePortで公開します。

NodeにはExternal IPでアクセスすることになりますが、Preemptive InstanceのためNodeが削除される度に、External IPが毎回変わってしまいます。

参考にさせていただいた記事では、CloudFlareのDNSとExternal IPを同期する以下のカスタムコントローラーが紹介されていました。
https://github.com/calebdoxsey/kubernetes-cloudflare-sync

自分はCloudFlareは使っていませんでしたが、過去にRoute53で取得したドメインとホストゾーンがすでにあったので、Route53に同期したいと思いました。

公式のAWS Service Operatorというものもありますが、まだ発展途上でRoute53には対応していません。

AWS Service Operator for Kubernetes Now Available
https://aws.amazon.com/jp/blogs/opensource/aws-service-operator-kubernetes-available/

Route53にはヘルスチェックなどの機能もあるので、カスタムリソースで作成できると便利かもしれませんが、
今回はリソースも限られているので、kubernetes-cloudflare-syncのようなライトなコントローラーがいいよねということで、

新たに上記のリポジトリをForkして、Route53にNodeIPを同期するカスタムコントローラーを作成しました。
https://github.com/jlandowner/kubernetes-route53-sync

これをデプロイすることで、Preemptive InstanceでNodeが削除されても、しばらくすれば新しいNodeにアクセスできるようになりました。

EKSや他のk8s環境でも利用することができます。
EKS+Spot Instanceでは効果が期待できると思いますが、EKS自体安くないので、EKS使うなら普通にELBを立てますね。。

外部アクセス(Ingress)の設定

外部アクセス用のCert-managerとIngress Controllerを次回セットアップします。

Kubernetes cert-managerでRoute53で作成したドメインの証明書を作成し、Ingressで利用する。
https://qiita.com/jlandowner/items/9f3c4c0a03d2ae3d2189