potpro (ぽとぷろ)
Full-stuck engineer(Not Full-stack)
JS/PHP/Go/Docker/Nginxなど。技術または趣味寄りの発信ブログです。
全 85 記事Oracle Cloudの無料枠だけでKubernetes(k3s)クラスタを構築する
Oracle Cloudの無料枠だけでKubernetes(k3s)クラスタを構築する(したい)
タダでkubenatesを運用してみたいんじゃオラ
という気持ちの元に、ギリギリ無料でkubenatesを運用できそうな物が出てきたので、構想を現実にするという記事です。
Oracle Cloud Infastructure と Kubernetes
Kubernetes、Docker(厳密には違うけど)のオーケストレーションツールとして、事実上の標準として居座っているオープンソースソフトウェア。
コンテナの運用技術としてこれから試してみたい人も結構多いと思いますが、やるのであれば実際にクラスタ組んでサービスをデプロイしてインターネットからアクセスできる、そんなレベルまでやってみたいものです。
やるとすれば、実際Kubernetesを採用しているクラウドサービスは多く、GCPはマネージドKubernetesなおかつGoogleのお膝元なので非常にやりやすく簡単なのですが・・・
クラスタを組むということはまあ最低でも2台ほしいということで、ずっと運用しておくにはソコソコのお金がかかるのです。
なので・・・
今回は完全無料で何とかということで最近ちょっと話題になった__Oracle Cloud Always Free Tier__を使用して作っていきます。
Oracle Cloud Always Free Tierとは、Oracle様のクラウドサービスである「Oracle Cloud Infrastructure(以下OCI)」が9月ごろに発表した、その名の通り「無期限無料」な枠のことです。
Oracle Cloudを期限なく無料で使える「Always Free」発表 1GBのVM2つ、Autonomous DB2つなど提供
タイトルにあるように、メモリ1GBのVM2つが無料対象。Always(永遠に)。
これはたしかにものすごい太っ腹な対応です。GCPでも無料枠はあるのですが、f1-microというメモリ0.6GBのサーバー1つが対象で、それと比べると総メモリ数は約3倍以上です。
しかし、当然いろいろありまして・・・
この記事を書くのに、OCIに登録して約1カ月と掛かってます。
何故かというと、大体VMを立てるのに1カ月掛かりました。
理由は、Oracle側の"Out of Capacity"エラー。多分OCI側のサーバが枯渇しているんじゃないかと思われます。 多分Free以外のサーバは多分建てられたのではと思いますけど・・・、やはり無料の扱いって感じ。
そんなこんなで #OCIチャレンジ を数回繰り返し、やっと今日建てることが出来ました。
タダより安い物はないので、こういうことにもめげずにやっていきますよ。 Always Free のサービス終了しないように祈りながら。Oracleさんよろしくお願いします!
k3s
早速構築していきたいのですが・・・
標準のkubenatesの推奨環境にはCPU 2コア/メモリ 2GBとなっているため、足りていない! コア数も足りていないので、k8sサーバーだけで負担でカツカツになってしまう可能性高し。
ということで、kubenatesの軽量版として開発が進められているk3sを採用して環境を構築していきます。
k3sはRancher Labs社が開発している軽量なKubernetesディストリビューションとなっています。軽量といってもちゃんとしたKubernatesで、コマンドもほぼ同じように動作します。
軽量の理由は1つのバイナリに全部まとめて使わなそうな機能は全部省いたとか。
k3sであればサーバの最低メモリは512MBなので、しかもシングルバイナリで設定コマンド一つで構築完了で簡単。これを動かしてもまだ余裕あるかなという感じ。
ということで今回はk3sを採用。
余談ですが、OCIにはContainer Engine for KubernetesというマネージドKubenatesがあるようです。
使用するのに料金は掛からないと書いてありましたが、VM等の料金は掛かるため、立てられたVMがAlways Free対象にならない可能性があり、今回は使用しておりません。Always Free対象のVMとLBのみ使用しています。
構築
OCIからFree TierであるVMインスタンス VM.Standard.E2.1.Micro を 2つ起動。
いろんなOSが選べましたが今回は個人的に使っているということでCentOS7になりました。
少し悩んだのがsshのアクセス。PublicIPアドレスがデフォルトで付与されませんでした。Webで付与設定をしてsshアクセス正常動作の確認完了まで完了。ここから構築。
サーバー構成
k3sはServer(master)とAgent(worker)の2層に分かれていて、k8s全体の処理等はServer、containerを動作させる部分(node)はagentとなっています。
今回は2サーバー構成なので、サーバA(ocloud-potproject-1)をServer+Agent兼用、サーバB(ocloud-potproject-2)をAgentとして、レプリケーションと冗長化が出来るようにしておきます。
k3s Server
k3sのページにあるコマンドを叩きます。
> curl -sfL https://get.k3s.io | sh -
[INFO] Finding latest release
[INFO] Using v0.10.2 as release
[INFO] Downloading hash https://github.com/rancher/k3s/releases/download/v0.10.2/sha256sum-amd64.txt
[INFO] Downloading binary https://github.com/rancher/k3s/releases/download/v0.10.2/k3s
[INFO] Verifying binary download
[INFO] Installing k3s to /usr/local/bin/k3s
[INFO] SELinux is enabled, setting permissions
which: no kubectl in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/opc/.local/bin:/home/opc/bin)
[INFO] Creating /usr/local/bin/kubectl symlink to k3s
which: no crictl in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/opc/.local/bin:/home/opc/bin)
[INFO] Creating /usr/local/bin/crictl symlink to k3s
which: no ctr in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/opc/.local/bin:/home/opc/bin)
[INFO] Creating /usr/local/bin/ctr symlink to k3s
[INFO] Creating killall script /usr/local/bin/k3s-killall.sh
[INFO] Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO] env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO] systemd: Creating service file /etc/systemd/system/k3s.service
[INFO] systemd: Enabling k3s unit
Created symlink from /etc/systemd/system/multi-user.target.wants/k3s.service to /etc/systemd/system/k3s.service.
[INFO] systemd: Starting k3s
Job for k3s.service failed because the control process exited with error code. See "systemctl status k3s.service" and "journalctl -xe" for details.
これだけで構築完了の模様。早い。Systemdに登録と自動起動まで勝手にやってくれるようです。
でもrootじゃなくて起動失敗したっぽいのでrootで起動。パスも通す。
起動後、nodeの確認。コマンドは先頭にそのままk3sがついてk3s kubectl get node
。
で、後から気づいたんですが無くても動いた。
これでもうサーバ構築完了。すごい早いぞ。
> sudo service k3s start
Redirecting to /bin/systemctl start k3s.service
> k3s kubectl get node
NAME STATUS ROLES AGE VERSION
ocloud-potproject-1 Ready master 64m v1.16.2-k3s.1
k3s Agent
次はサーバBのAgentを起動。そのままだとServerが起動してしまうので、コマンドにAgent用の値を渡します。
tokenは、Serverの/var/lib/rancher/k3s/server/node-token
に書かれた値を使えばOK。ポート開放も忘れずに。
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.2:6443 K3S_TOKEN=XXX::node:XXX sh -
ここでServerとAgent間で繋がらないエラー。pingは通るので多分iptablesの問題に見える。自動設定してくれるものだと思っていたがOCI側から入れられている設定の問題っぽい。
どうやら6443/tcpと8472/udpの許可が必要なので、firewall-cmdにて許可設定。セキュリティ的にはOCI側でやっているので割と雑でも大丈夫なはず。
> firewall-cmd --add-port=6443/tcp --zone=public --permanent
success
> firewall-cmd --add-port=8472/udp --zone=public --permanent
success
> firewall-cmd --reload
これでnodeに追加完了。ちゃんと表示された!
> k3s kubectl get nodes
NAME STATUS ROLES AGE VERSION
ocloud-potproject-1 Ready master 144m v1.16.2-k3s.1
ocloud-potproject-2 Ready <none> 23m v1.16.2-k3s.1
Nginxを立ててみる
後は普通にkubectlを使ってデプロイ可能です。
このあたりを参考に・・・
Deploymentを使用してステートレスアプリケーションを実行する
> kubectl apply -f https://k8s.io/examples/application/deployment.yaml
deployment.apps/nginx-deployment created
> kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deployment-54f57cf6bf-hx95b 1/1 Running 0 79s
nginx-deployment-54f57cf6bf-l2nsm 1/1 Running 0 79s
# ちゃんと冗長化されているか確認
> kubectl describe pod nginx-deployment-54f57cf6bf-hx95b | grep Node:
Node: ocloud-potproject-2/10.0.0.3
> kubectl describe pod nginx-deployment-54f57cf6bf-l2nsm | grep Node:
Node: ocloud-potproject-1/10.0.0.2
# とりあえずmasterのローカルのnginxをIP直接で叩いてみる
> kubectl describe pod nginx-deployment-54f57cf6bf-l2nsm | grep IP:
IP: 10.42.0.7
IP: 10.42.0.7
> curl http://10.42.0.7/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<略>
</head>
</html>
ローカルのcurlですがちゃんと表示されました!
ロードバランサを使って外部公開サービスを作る
ここまでやってデプロイは出来たので、後は外部公開するだけ。ここまで終われば完璧にWebサービスになります。
そして、複数構成になっているのでロードバランサーが必要になるのですが・・・
ここで使用するロードバランサーはk8sのLBやIngressではなく、OCIのロードバランサーを使います。
なんとロードバランサ(1インスタンス、10Mbpsの帯域幅)もFree Tier対象なのです。 これを使わない手はない。こちらを使います。
OCIのロードバランサを作るところは割愛します。そんなに難しいところはない感じでした。ポートの指定とバックエンドのサーバーを設定するだけ。
ここで、ロードバランサー側で受けるバックエンドのポートはTCP 30080にしておきます。理由は後で。
NodePortを使って外部公開する
今回は簡単なやり方としてk8sのServiceとしてNodePortを使った公開とします。
NodePortは設定したポートを全てのホストが外部から受け取り、コンテナのポートに転送してくれます。Dockerでexposeを使った状態と同じです。
別途Ingressなどを使えばより高性能にロードバランシング出来るのですが、OCIのロードバランサーを使っているのでNodePortの欠点である単一障害点の問題はないと思われますし、単純にまだ勉強不足などで無しとなっています。
(正直、ここよりもOCIのセキュリティルールの設定にてこずりました)
上のnginxの参考にしつつ、こんなServiceとdeployment用のyamlを書きました。
apiVersion: v1
kind: Service
metadata:
name: whoami-service
labels:
app: whoami
spec:
selector:
app: whoami
type: NodePort
externalTrafficPolicy: Local
ports:
- protocol: TCP
port: 8080
targetPort: 8000
nodePort: 30080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami-deployment
spec:
selector:
matchLabels:
app: whoami
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: jwilder/whoami
ports:
- containerPort: 8000
jwilder/whoamiは昔も少し使ったことのある、コンテナIDを出すだけのWebサーバです。
これでどのコンテナに転送されているか、ちゃんと全てのサーバにアクセス出来ているか確認できます。
externalTrafficPolicy: Local
という設定は、アクセスされたサーバーのコンテナを返すという設定。
今回はk8sの上にロードバランサがあるので、k8s上でのバランシングは必要なく、これを設定しました。
また、NodePortでのポート設定が30080となっています。 こちらはNodePortの制約上、30000-32767にポートが制限されているから。なのでOCIロードバランサ側もTCP30080にしています。
設定変えれば別もいけるらしいが、今回は変更しないということで。 これで実行。デプロイが完了したら見れるようになります。
> kubectl apply -f whoami.yaml
service/whoami-service created
deployment.apps/whoami-deployment created
> kubectl get pods -l app=whoami
NAME READY STATUS RESTARTS AGE
whoami-deployment-5ff8cd9445-vzmsv 1/1 Running 0 11m
whoami-deployment-5ff8cd9445-5wn5n 1/1 Running 0 11m
実際に外部から確認。いい感じに問題なく2つのサーバにまたがっている2つのコンテナを表示できることを確認できました!
所感
とりあえずここまで。実はDockerは割とやってきたのですがKubernetesはまだまだ・・・
しかし勉強用に使っていく環境が出来たということで良し。正直うまいことやればほぼスペックが要らない公開サービスなんかもこの環境で立てたい。
しかしロードバランサも無料となると、うまいことやればこれでも個人でそこそこの可用性が担保できるサービスを作れるんじゃないでしょうか。まあ立てられればの話ですが・・・
そして欲を言えばもう一台欲しかった・・・こうして結局課金する羽目になりそうです。
最後に、証明としてコストのスクショを乗っけておきます。これ見るとわかる通り、1円もかかってません。ちなみに今も元気に稼働中。Oracleさん最高!神企業!以上です。
↓続編の記事を書きました。より詳しく知りたい場合はご覧ください!