こちらの記事が面白そうだったので、ハンズオンしてみましたという記事です。
(@gashirarさん、素敵な記事をありがとうございます!!)
上記の元記事ではスマートにまとめてくれている部分も多いですが、シロート気味の私がハンズオンする中で軽くつまづいたり、ググって調べたりしたところもスクショ付きで残しているので、そのあたりがバリューになるかなと思います。
やりたいこと
勉強用のおうちk8sクラスターを無料で運用したい!
→ OCIのFree Tierなら無料でIaaS構築&運用できる
→ k8sはセルフマネージドならコントロールプレーンも無償運用できそう
→ プロビジョニングはTerraform&Ansibleで自動化しちゃおう
やること
- VCN環境をOCIで準備(Terraform)
- コンピュートインスタンス上にkubeadmでk8sクラスター構築(Ansible)
やったこと
私はM1 MacBook Airを利用してハンズオンしました。
ターミナルではzshを使用しています。
OCIコンソールにログイン
私の場合、少し前に触ったまま30日無料枠が終了しており、そのせいか以前ログインできていたIDCS用のサインイン画面だと認証が通らなくなっていていきなりハマりました。
以下はOracle Cloudの中でもOCI専用のサインイン画面となるため、IDCSではなくIAMによる認証が可能なためAlways Freeではこちらを使うのが良いのかなと思われます。
以下はIDCSのサインイン画面。こっちだと以前使えたIDで認証が通らなかった。
メールで届いていた以下の青いボタンから開くサインイン画面で無事にサインインできました。
こっちのURLは個人用っぽかったので、URL掲載はやめときます。
OCI CLIの設定
インストールは簡単でした。ターミナルでhomebrew叩くだけ。
brew update && brew install oci-cli
初期設定はググりながらやりました。
以下コマンドでダイアログ形式の初期設定が可能です。
oci setup config
既定値のままで問題ないものはEnterで飛ばし、個別にパラメーター入れる必要があるものはOCIのコンソールより取得します。
- OCIユーザーのOCID
- OCIテナンシーのOCID
このあと、ローカルに作成した公開鍵をOCIコンソールからIAMに設定する必要があります。
以下のページを参考にさせていただきました。
途中でターミナルに出力される鍵のパス等は、あとでTerraformのコードに埋め込んだりする際にコピペして使います。
設定が終わったはずの人は、以下のコマンドを実行してOCI CLIがうまく操作できるか確認してみてください。問題なければ表形式でリージョン一覧が返却されます。
oci iam region list --output table
ハマりポイント
私の場合、401の権限エラーになってしまったのですが、セットアップコンフィグのダイアログ内で大量のリージョンがリストアップされ「この中から選べ!」と言われた際に「ap-chiyoda-1」なるものを東京リージョンと勘違いし選んでしまっていたことが原因でした。
ユーザーディレクトリ配下の .oci/config ファイルを編集し、「ap-tokyo-1」へ修正することで無事にコマンドが通りました。
コンパートメントの作成
OCIの独自概念。GCPでいう「プロジェクト」のように、アカウント内を論理的に分割しリソースや権限系を別管理しやすくしてくれる機能です。
初期状態で「ルートコンパートメント」も存在していますが、ハンズオン記事に沿って新規コンパートメントをWebコンソールから作成します。
「親コンパートメント」にルートを指定し、子コンパートメントとして作成します。フォルダーのように入れ子にするイメージですね。
GitHubリポジトリのクローン
元記事では親切なことにサンプルコードを公開してくれています。
初心者目線でやることをリストアップすると以下です。
- WebブラウザからGitHubアカウントを作成
- Macにgitをインストールし初期設定
- git cloneでリポジトリをローカルにコピー
- VScodeなどのコードエディターでコピーした資材を開く
gitの初期設定はSSH鍵のパーミッション周りでつまづく人が多いと思うのですが、GitHubからのクローン時はSSHだけでなくHTTPS方式も選択できるため、こちらのURLを利用してクローンすればSSH設定は不要になります。
※gitが難しければ最悪、ZIP形式でブラウザからそのままダウンロードできます。
Terraform設定ファイルの作成
サンプルファイル terraform/main.tfvars.example
をコピーして編集します。
IaCツールの導入
以下をMacにインストールしておきます。
- Terraform v0.12
- jq v1.6
- Ansible v2.9
いずれもHomebrewでインストール可能です。
TerraformとAnsibleは明示的なバージョン指定が必要でした。
brew info <ツール名>
で最新バージョンが確認可能なのと、 brew install <ツール名@バージョン>
でバージョンを指定したインストールが可能です。
インストール後は出力メッセージに従ってパスを通すのを忘れないようにしましょう。
Terraformはバージョン毎の仕様変更が激しく、元記事の執筆タイミング付近のバージョンをインストールしないとスクリプトがエラーを吐いてしまいます。
「tfenv」を使うとバージョン管理が便利なんですが、M1 Macだとv1.0.2より前のバージョンはインストールに失敗するようで、brewで直接インストールしてしまうのが簡単です。
brew install --build-from-source terraform@0.12
echo 'export PATH="/opt/homebrew/opt/terraform@0.12/bin:$PATH"' >> ~/.zshrc
※追記したパス設定を有効化するため、ターミナルの再起動が必要になります。
OCIリソースの作成
クローンしたリポジトリ内のシェルスクリプトを実行すればTerraformが動きます。
./bin/terraform.sh
ハマりポイント1
TerraformのOCIプロバイダーから以下のようなエラーが出ました。
Error: 400-InvalidParameter
Provider version: 4.67.0, released on 2022-03-10.
Service: Core Instance
Error Message: Invalid ssh public key type "-----BEGIN"
Suggestion: Please Update the parameter(s) in the Terraform config as per error message Invalid ssh public key type "-----BEGIN"
ググると「RSAキーペアじゃなくてSSHキーペアを使え」的なQAが見つかりました。
(これは一般的な知識??ITリテラシー低くて分かりませんでした)
とりあえず ssh-keygen
コマンドを実行してMac上でSSHキーペアを作成し、その id_rsa.pub
ファイルのパスを main.tfvars
ファイル内に記載すると上手くいきました。
ハマりポイント2
今度は次のエラーが出ました。
Error: 400-LimitExceeded
Provider version: 4.67.0, released on 2022-03-10.
Service: Core Instance
Error Message: The following service limits were exceeded: standard-e2-micro-core-count. Request a service limit increase from the service limits page in the console.
Suggestion: Request a service limit increase for this resource Core Instance
なんかサービスリミットを超過した的なことが書いてあるので、もしかすると以前にいじった際に消せてないインスタンスがあってAlways Free枠の制限に掛かっているのではと推測。
コンピュートのコンソールを見てみると確かに1台残っていたため、「インスタンスの終了」を実施。
※ちなみにOCIのWebコンソールって、コンピュートインスタンス画面で「コンパートメント」を選択しないと一覧表示されないようです。
上記のハマりポイント2点を乗り越え、無事にterraform apply完了!
途中で何度もエラーでコケてリトライしましたが、いちいちお掃除しなくても成功してくれるのは冪等性が担保されているIaCならではですね。
k8sクラスターの作成
GitHubリポジトリーを見ると、クラスター導入ツールが2オプション(kubeadmもしくはk3s)用意されているようです。
元記事の時点ではkubeadmが利用されていたようですね。こちらはk8sのベストプラクティスに沿って愚直にクラスターを構築してくれるツールのようですが、一方でk3sは超軽量なクラスター作成ツールで、minikube等と並んでおうち実験用クラスター作成などによく使われているようです。
今回は勉強のためにもkubeadmを利用します。
まず ansible-kubeadm
ディレクトリーに hosts.ini
が作成されているか確認しましょう。
存在していない場合、先ほど実行したシェルでTerraformがコケているか、最後に実行されるjqのインストールを忘れている等が考えられます。
上記問題なければ、 ./bin/setup-by-kubeadm.sh
を実行すればAnsibleが走ってkubeadmがクラスターを構築してくれます。
(けっこう時間かかります)
そして私の場合、最後の方のネットワークコンポーネント作成でエラーを吐いてしまいました。
ざっと見た感じ、k8sリソースの中に最近のバージョンでは非推奨になってしまったものは混じっているためのように見えます。Terraformもですがk8sも短期間での仕様変更がかなり激しいですね。
ホントにクラスター作成失敗してるのかよく分からないので、とりあえず動作確認までは進んでみます。
k8s操作用の設定ファイルを配置したら完了!
手元のMacからk8sクラスターにコマンドを発行できるよう、kubectlコマンドをインストールしましょう。
そして今回作成したOCI上のk8sクラスターに命令できるよう、kubectlの設定ファイルを配置します。実は先ほど実行したAnsible用シェルの最後に設定ファイルがMacのホームディレクトリーに吐かれているので、以下のコマンドで移動します。
mv ~/admin.conf ~/.kube/config
これでkubectlする準備が整っているはず…。
おそるおそるk8sクラスターを操作してみる
まずはノードの確認。
%kubectl get nodes
NAME STATUS ROLES AGE VERSION
handson-master Ready control-plane,master 19m v1.23.4
ちゃんとコマンドは通りましたが、マスターノードしかいませんね。怪しさ満点。
ダメ元でPodをデプロイしてみます。まずPodがいないことを確認
%kubectl get pods
No resources found in default namespace.
そして適当なdeploymentを書いてデプロイしてみます。
サンプルファイルは手前味噌ですが、以下の記事にて利用したnginxコンテナを3セット展開するyamlを作成。
%kubectl apply -f deployment.yaml
deployment.apps/triple-nginx created
%kubectl get pods
NAME READY STATUS RESTARTS AGE
triple-nginx-5c6777df98-flh9t 0/1 Pending 0 5s
triple-nginx-5c6777df98-rw2gv 0/1 Pending 0 5s
triple-nginx-5c6777df98-sf7dg 0/1 Pending 0 5s
applyはうまく通ったんですが、Podが全部ペンディングになりました。
ログを確認してみます。
%kubectl logs triple-nginx-5c6777df98-flh9t
(中略)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 16s (x3 over 2m38s) default-scheduler 0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
マスターノードにPodをデプロイしようとしているため、taintに引っかかってPodが起動失敗しているようです。
Ansibleエラーの再確認
長いエラーをちゃんと見直してみると、この辺が致命的なようです。
unable to recognize \"/etc/kubernetes/network/flannel.yaml\": no matches for kind \"ClusterRole\" in version \"rbac.authorization.k8s.io/v1beta1\"
unable to recognize \"/etc/kubernetes/network/flannel.yaml\": no matches for kind \"ClusterRoleBinding\" in version \"rbac.authorization.k8s.io/v1beta1\"
k3sでリベンジ!
kubeadm用のAnsibleプレイブックをデバッグするのは私のスキルでは荷が重いので、もう一つのk3sオプションを試してみようと思います。
面倒ですが、 ./bin/destroy.sh
で一度環境破壊を行います。
無事にterraform destroy完了したら、 ./bin/terraform.sh
でOCI環境を再構築します。
※余談ですが、これまでIaCってWebコンソールでポチポチやるより難しいので一長一短だな〜などと思ったりもしていましたが、こういうことをやっていると破壊と構築を一瞬で繰り返せるので非常に便利ですね。
IaaS環境ができたら、リベンジ用のk3s構築スクリプトは ./bin/setup-by-k3s.sh
です。
こっちはエラーなく完了!インストレーションも一瞬でした。さすがk3s。
そしてもう一度、kubeconfig設定ファイルを更新します。
mv ~/admin.conf ~/.kube/config
k3sクラスターを触ってみる
今度は上手くいくはず?ノード情報を取得してみます。
%kubectl get nodes
NAME STATUS ROLES AGE VERSION
handson-master Ready control-plane,master 7m15s v1.22.7+k3s1
handson-worker Ready <none> 6m13s v1.22.7+k3s1
マスター&ワーカーとも取得できています。
ちなみにAnsible実行直後はkubectlがフリーズしてしまったのですが、数分時間を置くとうまくいきました。クラスターの初期化途中だったのかも知れません。
先ほどと同様、deploymentを適用してみます。
%kubectl apply -f deployment.yaml
Error from server (InternalError): error when creating "deployment.yaml": Internal error occurred: resource quota evaluation timed out
リソースクォータの評価でタイムアウトしてしまった様子。
スペックの問題?deploymentをやめて単一のpodをデプロイしてみます。
%kubectl apply -f pod.yaml
Unable to connect to the server: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
今度は接続自体がタイムアウトしてしまったようです。
ネットワークの問題でしょうか?
げっ、マスターノードのメモリー使用率が100%近くに張り付いています。これが原因かも知れません。
なかなか調子が戻らなさそうなので、もう一度環境破壊→再構築をやり直してみましたが、同じく起動直後にメモリー張り付きが再現し、kubectlがほぼ通らなくなってしまいました。残念。
まとめ
ということでかなり消化不良感がありますが、Always Free枠で運用できるインスタンスだとリソース面がかなりカツカツなのかも?
↓ ちなみに後日、今回の学びをOCIjpでもLTさせていただきました!