えいのうにっき

a-knowの日記です

安価なGKE(k8s)クラスタを作って趣味開発に活用する

tl;dr

  • GKEでk8s(kubernetes)クラスタを作成すると、各ノードはGCEインスタンスとして起動する
  • GCEインスタンスには preemptible モードが指定でき、これはGKEクラスタとして起動するノードに対しても指定可能
  • GCEのf1-micro無料枠の適用と合わせて、この運用費用は約 $7.68/month

動機

趣味開発用途として手軽にあれこれ試すことができて、それでいてできるだけ安くあがるコンテナオーケストレーション環境(k8s環境)がほしい。

  • k8sそのものを運用したい・そのノウハウを学びたいというわけではないのでマネージドサービスがいい
    • 「k8s上でアプリケーション・サービスを運用する経験」がしたい
  • その上で、できるだけ安く運用したい

GKEでk8s(kubernetes)クラスタを起動すると、各ノードはGCEインスタンスとして起動する。そしてGCEインスタンスには preemptible モードという、通常の価格の数分の一の価格で使える反面、以下のような制約が課せられる、とても個性的な特徴を持つインスタンスを作成することができる。

  • いつシャットダウンされるかわからない
  • 最大でも24時間しか起動しつづけられない
  • 要求したタイミングでインスタンスを立ち上げることができない可能性もある

この preemptible オプションはGKEクラスタを作成するときでも指定することができ、そしてGKEを使うことでこの preemptible インスタンスのデメリットを最小化しつつも活用できそうだったので、今回はそれを試してみる。

ノードに preemptible インスタンスを使ったGKEクラスタの立ち上げ手順

まずは下準備。GKEをはじめとしたGCPに対しての操作は gcloud コマンドが、k8sクラスタに対する操作には kubectl コマンドが、それぞれ便利なので、まだの人はセットアップしましょう。

  1. GCloud SDKのセットアップをする
  2. kubectl のセットアップ
    • gcloud components update
    • gcloud components update kubectl

リージョン選択の際、GCEのf1-micro無料枠を活用するため、リージョンを 北バージニアを除く米国リージョン のいずれかを指定する(リージョン一覧 )。

ここまでできたら、さっそくGKEクラスタを起動してみる。以下のコマンドをたたく。

gcloud container clusters create <クラスタ名> --preemptible --machine-type=f1-micro --num-nodes=3 --disk-size=10

クラスタを立ち上げる際、クラスタを構成する GCE インスタンスの machine-type を指定できるのだけど、そのスペックにより、最小限求められるインスタンス数が異なる。f1-micro の場合は、3インスタンス(--num-nodes=2 などと指定してもエラーとなる)。上記のコマンドで3つの preemptible なインスタンスで構成されるk8sクラスタが作成される。

2018-06-21 追記こちらのブログで試していただいた結果、--enable-autorepair オプションの指定がないと警告がでるらしいです。gcloud のバージョンの違いかな、補足ありがとうございます!

上記のようにして作られたクラスタでは、preemptible としていることで仮に不意にシャットダウンされたインスタンスがあっても、すぐに3つのインスタンスを保つよう、preemptible なインスタンスが作られる。

かといって、preemptible なインスタンスのみで k8s クラスタを構成してしまうと、最悪「すべてが同時にいなくなったのち、ひとつもインスタンスを確保できない」という事態に陥る可能性もあるので、上記とは別にf1-microインスタンス1台は最低限、起動しておいてもらうことにする。ここでそのコストが心配になるかもしれないけど、GCPでは f1-micro インスタンス1台はずっと無料(Google Cloud Free Tier  |  Google Cloud Platform Free Tier)なので、ここにそれが適用される。ハズ。

以下のコマンドで、さきほど作成した preemptible なクラスタに対して preemptible でない f1-micro インスタンス1台だけのノードプールを追加する。

gcloud container node-pools create <ノードプール名> --cluster <クラスタ名> --machine-type=f1-micro --num-nodes=1 --disk-size=10

確認してみる。

$ gcloud container node-pools list --cluster <クラスタ名>
NAME          MACHINE_TYPE  DISK_SIZE_GB  NODE_VERSION
default-pool  f1-micro      10            1.8.10-gke.0
<ノードプール名>  f1-micro      10            1.8.10-gke.0

default-pool というのが preemptible なインスタンスのそれ。よさそう。

次に、preemptible インスタンスが最大24時間で消失したり再生したりする様子を視覚的に確認してみたいので、mackerel-agent コンテナを各ノードにひとつずつ配置する(daemonset)。

以下のような yaml ファイルを用意して......

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  labels:
    name: mackerel-agent
  name: mackerel-agent
spec:
  template:
    metadata:
      labels:
        app: mackerel-agent
    spec:
      containers:
        - name: mackerel-agent
          image: mackerel/mackerel-agent:latest
          imagePullPolicy: Always
          env:
          - name: apikey
            value:<API KEY>
          - name: opts
            value: -role=<MACKEREL SERVICE NAME>:<MACKEREL ROLE NAME>
          - name: enable_docker_plugin
            value: "1"
          - name: auto_retirement
            value: "1"
          lifecycle:
            preStop:
              exec:
                command: ["/usr/bin/mackerel-agent", "retire", "-force"]
          volumeMounts:
          - name: docker-sock
            mountPath: /var/run/docker.sock
          - name: mackerel-id
            mountPath: /var/lib/mackerel-agent/
      volumes:
        - name: docker-sock
          hostPath:
            path: /var/run/docker.sock
        - name: mackerel-id
          hostPath:
            path: /var/lib/mackerel-agent/

以下のコマンドを実行。

$ kubectl create -f test.yaml

podの確認。

$ kubectl get pods
NAME                   READY     STATUS    RESTARTS   AGE
mackerel-agent-5lln2   1/1       Running   0          1m
mackerel-agent-7ndm4   1/1       Running   0          1m
mackerel-agent-9sb72   1/1       Running   0          1m
mackerel-agent-jrshg   1/1       Running   0          1m

preemptible なインスタンス x 3、ふつうのインスタンス x 1 の合計4インスタンスに対し、daemonset で実行したので各ノードで mackerel-agent コンテナが動いている様子がわかる。

この状態で数時間放置したあとの、Mackerel のグラフ(ロールグラフ:クラスタ全体の推移がみやすい)は以下のようなかんじ。

f:id:a-know:20180617192047p:plain

各色で示されるのは各インスタンスのリソース状況。preemptible なインスタンスが入れ代わり立ち代わり、次々とシャットダウン/新たに起動されつつも、not preemptible なインスタンスは稼働しつづけているのがわかる。よさそう。

これを見ていてふと思ったけど、preemptible なインスタンスを使うことで chaos monkey 的なかんじになって逆に強くもなってるのでは?とか思った。......というのは、ポジティブすぎるか。w

www.publickey1.jp

コスト

こちらのページ に記載のとおり、preemptible な f1-micro インスタンスの1ヶ月の運用コストは $2.56 (オレゴンリージョン)。これが3台なので $7.68。そして追加のノードプールで動く1台の not preemptible インスタンスは無料枠が適用されるはずなので無料。......というわけで、趣味開発用途としては許容範囲かな? ほら、heroku の Hobby dyno も $7/month だし(?)。

ちなみに g1-small や n1-standard-1 のマシンタイプだと1台でGKEクラスタを作ることができて、それぞれの preemptible 料金は $5.11/month , $7.30/month。なのでこっちでもいいのでは?と思えるけど、1台だと「クラスタ」感がないのがなんかヤだし、なにより1台しかないのに preemptible 指定をしてしまうとダウンタイムが発生してしまうと思われるのでちょっと微妙かなと思っている(そして preemptible にしないとそこそこのコストにもなる)。

ただ、このクラスタ上でWebアプリケーションなどを動かしてそれを外部からアクセスできるように、とかしようとすると、これとは別にロードバランサーとか転送量費用が掛かると思うので、その点は要注意(ロードバランサー費用の方が支配的になるんだろうな、きっと......)。

2018-06-21 追記)上記の構成で数日動かしてみたときの課金額を以下に貼っておく。

f:id:a-know:20180621220602p:plain

注意とお願い :pray:

ノードプールとかの細かい仕様や制限を調べきることなくこれを書いているので、考慮漏れなどあるかもしれないのでそこは注意してほしいのと、もっと賢い運用の仕方などがあればぜひ教えてください。僕は「趣味開発用途として手軽にあれこれ試すことができて、それでいてできるだけ安くあがるコンテナオーケストレーション環境(k8s環境)がほしい」、ただそれだけ!

2018-06-25 追記

このクラスタを使って、小さなWebアプリケーションをデプロイ & HTTPS対応をやってみました。

blog.a-know.me

参考