個人で運用している VPS に Kubernetes を乗せて既存のシステム(Web アプリとか Slack Bot とか Cron ジョブとか)をガッと移行した。
もともとは itamae 使って Systemd でサービス立ち上げていたのだけど、ソフトウェア依存とか権限分離とか冪等性とか面倒になってきて、うおお Docker でデプロイするぞ!となっていた。
初めは Docker Compose で立てようと思っていたけど、k8s のほうが楽しそうなので k8s にした。実際楽しいので良いと思う。
何を使って Kubernetes を立てるか
Microk8s
Microk8s は Canonical が配布しているもので、Ubuntu にログインすると広告が出てくるアレ。
インストールしたらすぐにシングルノードで動かせるように作られている。アドオンもいくつかあって、k8s を自前で運用するときのネットワークのめんどいところを解決してくれるのが魅力的。
初めはこれを使って立てていたのだけど、しばらく運用していると色々デメリットが見えてきてやめてしまった。
メリット
- 入れるのが簡単
- いくつかアドオンが用意されていて、ネットワークプラグインなどを簡単に有効化できる
- シングルノードで動かすようにパラメータなどが予め設定されている
- (k3s とは違って)動いているのは k8s そのものである
デメリット
- 入れ方が通常と異なるのでハマる
- 時々変な挙動になる (例)
- Cilium 対応してるけど Pod の IP range 変だったり…
- 依存ソフトウェアが古かったりする (例)
- 新しいのを入れようとすると Microk8s の環境に合わせる必要があってダルかったり
- アドオンを入れるときに細かい設定ができず、結局自分で入れることになる
- snap 使いたくない
やってることは複雑ではないので自力で修正可能だけど、変な挙動を割と多く見てしまったし、そこで苦労する必要ないなと思った。
kubeadm
kubeadm は Kubernetes の標準的なインストール方法。kubeadm をインストールしてコマンドで kubeadm init
すると証明書等が作成されてクラスタが立ち上がる。
初めは面倒くさそうで避けていたけど、やってみると特段難しいこともなかった *1 ので Microk8s ではなくこれを使うことにした。
メリット
- 素の Kubernetes が入るのでパッケージに特有なことでハマらない
- Ubuntu は利用者が多いのでちゃんと動く
- apt で入るので分かりやすい
デメリット
- アップグレードやネットワークプラグインの選定など、自分で管理する必要がある
- とは言ってもドキュメント読んでたら難しくない
他に考えたこと
-
軽いのは魅力的だが、使いたい機能が無くて困りそうなことや、周辺コンポーネントが動かなかったりしてハマりそうなのでやめた。
-
kubespray は内部で kubeadm を使っている Ansible 用の Playbook。これを使うとアップグレードとかもよしなにやってくれるらしいが、今回はシングルノードかつ kubeadm がそんなに複雑でなかったので不要と判断した。
kubeadm を使ったセットアップは itamae に任せることにした。とは言ってもこれぐらいのシンプルなもの。
# kubeadm や kubelet のインストール include_recipe "../kubeadm" kubeadm_conf = '/tmp/kubeadm.conf' remote_file kubeadm_conf do owner "root" group "root" mode "644" not_if "test -f /etc/kubernetes/admin.conf" end execute "kubeadm init --ignore-preflight-errors Swap --config #{kubeadm_conf}" do not_if "test -f /etc/kubernetes/admin.conf" end
ネットワークプラグイン
ネットワークプラグインには Cilium を使うことにした。Flannel とは違って NetworkPolicy 使えるのと、BPF 使ってて面白そう + 軽そうだから。
実際、 Calico と比べると iptables のルールがとてもスッキリしている。Cilium コマンドを使えば今どんな policy を適用しているかが分かりやすく出てきて良い感じ。
通信周りで mTLS などより細かい制御をするには Istio あたりが必要になるが、今回は重くなるだろうと思ったので入れていない。
Volume 周り
static だけどたまに編集していくようなウェブコンテンツ(https://nonylene.net/ とか)を serve することを考えると、ノード上の適当な場所にコンテンツを置きつつ、 nginx のコンテナから Persistent Volume としてマウントするのが良さそう。
ただ、 Persistent Volume を使うためだけに NFS 立てたりするのは大変なので、去年出た Local Persistent Volume を使うことにした*3。
具体的には、 nonylene.net/storage-web
のラベルがあるノードに PV を作って PVC を発行し、Pod に紐付けるようにした。
apiVersion: v1 kind: PersistentVolume metadata: name: web labels: app.kubernetes.io/name: web spec: capacity: storage: 10Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-storage local: path: /data/web/html nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: nonylene.net/storage-web operator: In values: - "true" --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: web spec: storageClassName: local-storage accessModes: - ReadWriteOnce selector: matchLabels: app.kubernetes.io/name: web resources: requests: storage: 1Gi
オブジェクト管理
Kubernetes は Deployment や Service のオブジェクトを置くことでデプロイを行うのだけど、設定ファイル流し込みとかパッケージ管理とかしたくなる。
まず、設定ファイル流し込みなど YAML の統合的な管理には kustomize を使っている。kustomize を使うと
- 複数の YAML を一つの YAML にマージして適用できる
- アプリを構成するオブジェクトに共通のラベルを付けて管理ができる
- ConfigMap をファイルから生成する
- Secret を env やファイルから一元的に管理・生成する
ので k8s の管理がだいぶ楽になる。itamae あたりとちょっと近い。
次に、パッケージ管理としては Helm を使う。これは自分で YAML を触ることがないような Cilium や ingress-nginx あたりのインストールに使う。
初めはこの辺も直接 YAML ダウンロードして kustomzie 配下に置けばいいやと思っていたのだけど、
のでやめた。
とはいっても、 Helm のコマンドだけでは管理しづらいので Helmfile を使ってパラメータの管理をしている。
アプリケーションの移行
今まで Systemd で適当に動かしていたアプリケーションを、 Docker で動かすようにコンテナ化していく。
とはいっても
- できるかぎり環境変数からパラメータを取れるようにする
- 今までは
config.py
を作ることが多かった
- 今までは
- Dockerfile 作る
- (オープンソースで作っていたので) Dockerhub に上げてビルドしてもらう
ぐらいでそこまで大変な作業ではなかった*4。
今は Dockerhub に上げているけど、ビルドキューが捌けるのがとても遅いのがネック。我慢できなくなったら GitHub Actions でやるようになると思う。
感想・補足
コンテナ化
従来と比べてデプロイ周りがとても楽になった。 git clone してセットアップして…というサーバー上で動かさないといけないことが随分と減った。 さらに、冪等性がどうこうとか考えなくてよく、統一的な方法でデプロイできるので、各種ミドルウェアを導入するのが今までよりも気軽にできるようになった。apt で配ってないソフトウェアでも Docker では多く配布されてるので、それらが引っ張ってくるだけで動くのが嬉しい。
-
Kuberentes の一番大きなメリットはコンテナを良い感じのホストに配置してくれることだが、今回はシングルノードのため関係ない。その他の点で Docker Compose と比べると
- 複数のサービスを統合的に管理できる
- Docker Compose とかだとレポジトリごとの管理になるから Systemd unit 増えていくし、共通のデータストアとか作るのめんどい
- オブジェクトの変更を行うと自動的に変更が反映される
- ssh しなくても
kubectl apply
だけで完結できる
- ssh しなくても
- Ingress という、初めに HTTP リクエストを振り分けるためのロードバランサが定義されており、周辺ツールとも統合的に使える
- 例えば cert-manager を用いれば Let's encrypt の証明書取得を自動的に行なってくれる
のが良いと思った。
ただ、YAML を書きまくるのは体験が良くない*5。TOML で書けたり aws-cdk 的なのを使えればいいのにな。
また、たまに「これが欲しいけど Issue になったあと動きがない」というケースがあり、まだ開発中なのだなぁと思う。
- 複数のサービスを統合的に管理できる
リソース
悪い点としてはリソースをたくさん食うことで、k8s だけで 900MB ぐらいはメモリを持っていかれる。今の所メモリ2GBのVPSで動かしているけど、アプリケーションを色々乗せるとカツカツになってしまった。3GB~4GBあると余裕だと思う。
HTTP 以外
メールや DNS の権威サーバーを外部に出したい場合は、Ingress は使えないので MetalLB あたりで仮想のロードバランサーを作る必要がある。今回は DNS とメール転送は Google Domains のマネジメントサービスに移した結果、HTTP だけになっていたので使わなかった。
開発環境
あくまでも k8s はデプロイする環境として使うので、開発環境としては Docker Compose や直接プロセス立ち上げたりする。
これで気軽にサービスをデプロイできる環境が整った。自分で Web アプリケーション作ってもデプロイや運用めんどいな…とか、MongoDB 使うとそのデプロイが…とか最近思っていたけど、これからはどんどん使っていこうと思う。