はじめに
自分用のKubernetesクラスタがほしいと思ったことはありませんか?私はあります。
minikubeやkindなどもありますが、せっかくなら本物(?)のKubernetesを使いたいですよね。
自分用Kubernetesを構築するパターンとして、①自宅サーバを用意して構築する(Raspberry PiやVM)、②クラウドを利用するという2パターンが考えられます。
しかし、それぞれ以下の懸念点があります。
- ①自宅サーバ
- ・物品調達から必要な場合がある
- ・自分で1から構築しないといけない
- ・サーバの管理が必要
- ②クラウド
- ・料金がかかる
- 例)AWSのEKSは1ヶ月あたり約140 USD
- ※EC2(t3.medium、EBS 30GB)×1台、ALB×1台の場合
そこで、Oracle Cloud Infrastructure(OCI)で無料マネージドKubernetesクラスタを構築してみました。
さらにOCIのマネージドTerraformサービスであるリソースマネージャ(こちらも無料)を使い、IaCによって構築してみました。
無料で使える仕組み
OKEの基本クラスタ
OCIのマネージドKubernetesサービスは OKE(Oracle Container Engine for Kubernetes) といいます。
OKEには一部機能が制限される基本クラスタと全機能が利用できる拡張クラスタの2種類があります。
そして、基本クラスタはクラスタ自体の料金が無料です。
ワーカーノードとしてデプロイされるコンピュートインスタンス(とそのボリューム)や、LoadBalancer Service適用時のロードバランサの料金のみが発生します。

基本クラスタを作成するには、作成時に拡張クラスタ機能を設定しないようにする必要があります。(参考)
Always Freeリソース
OCIにはAlways Freeという無料枠が存在します。
OKEの基本クラスタを構築するために以下のサービスが必要となりますが、各サービスにAlways Freeの無料枠が存在します。
| サービス | 無料枠 | 備考 |
|---|---|---|
| コンピュートインスタンス (シェイプ) |
VM.Standard.A1.Flex(Arm) (合計4 OCPU、24GBメモリ) |
・VM.Standard.E2.1.Micro(AMD) もあるがOKEで使用不可 |
| ブロックボリューム | 合計200GB | ・ブートボリュームを含む |
| ロードバランサ(FLB) | 1台、帯域幅10Mbps | ・LoadBalancer Service適用時に利用 ・フレキシブルシェイプ |
Always Freeはホームリージョンでのみ提供されます。
つまり、OKEの基本クラスタとAlways Freeリソースを組み合わせることで、無料のKubernetesクラスタを構築することができます。
前提条件
- OCIアカウント (有料アカウントにアップグレード済) およびコンパートメントが作成済みであること
有料アカウントにアップグレードしても課金対象のリソースを作成しない限り料金は発生しません。また、Always Freeは有料アカウントでも無料で利用可能です。
構成図
Always Freeや後述するCloudShellによるアクセスのためホームリージョンに構築します。
ホームリージョンはOCIアカウント作成時に指定し、後から変更できないので注意してください。
構築手順
1. Terraform構成ファイル(.tf)の作成
1から構成ファイルを作ってもよいですが、今回はスタックとして保存する機能を使ってTerraform構成ファイル(.tf)のひな形を入手します。
開発者サービス>Kubernetesクラスタ(OKE)>クラスタの作成からクイック作成の画面に行きます。

どうせ後で編集するのでデフォルト設定のまま進み、スタックとして保存します。

拡張クラスタ機能(ノードタイプ「仮想」)を設定していない状態で、この画面の「クラスタの作成」をクリックすると「基本クラスタの作成」のチェックボックスが表示されるので、チェックを入れて「クラスタの作成」をクリックすると基本クラスタが作成されます。
Terraformを使わない場合はこれでOKです。(シェイプの設定などは行ってください。)
スタックとして保存すると、OKEを構築するためのTerraform構成ファイルがリソースマネージャのスタックとして保存されます。
スタックとはリソースマネージャにおけるTerraform構成のセットです。
スタックから構成ファイルをダウンロードしてその内容をもとに編集します。

作成したファイルはこちらです。
なお、通常Terraformで必要な認証情報はリソースマネージャを操作するユーザの情報を利用するため、構成ファイルに記載したりする必要はありません。
作成のポイント
(必須)
oci_containerengine_clusterのtypeは"BASIC_CLUSTER"(基本クラスタ)とすること-
oci_containerengine_node_poolのnode_shapeは無料枠の対象であるVM.Standard.A1.Flexとし、node_shape_configのOCPUとメモリがそれぞれ無料枠の範囲内に収まるようにすること
今回の構成では1 OCPU、8GBメモリを3台としています。(合計3 OCPU、24GBメモリ)
(任意)
- セキュリティの観点からKubernetes APIエンドポイントはプライベートサブネットにしました
2. リソースマネージャを使って構築
OCIのマネージドTerraformサービスであるリソースマネージャを使って、OKEを構築します。
スタックの作成
リソースマネージャのスタックの作成からマイ構成を選び、作成したファイルをフォルダごとドラッグアンドドロップすることでスタックを作成できます。

ソース・コード制御システムを選ぶとGitHub等で管理しているコードをスタックとすることもできます。
計画(terrafrom plan)
成功するとログに作成予定のリソースが表示されます。
基本クラスタ(BASIC_CLUSTER)を作成予定であることが分かりますね。

適用(terraform apply)
計画が成功したら適用し、リソースを作成します。
先ほど成功した計画をもとに適用します。

基本クラスタが作成できています。(なぜか作成日のところに自分のメールアドレスが表示されています…)

ノードプールには指定した通りワーカーノードが3台作成されています。

実体はコンピュートインスタンスなのでインスタンスの画面からも確認できます。
Always Freeの対象であるVM.Standard.A1.Flexで作成されています。

もちろんVCNやサブネットといったネットワークも作成されています。

ちなみに、適用が完了するまで約25分かかりました。
ノードプールの作成に時間がかかるようです。

破棄(terraform destroy)
3. クラスタにアクセス・動作確認
リソースマネージャでクラスタが作成できたら、kubectlを使ってクラスタにアクセスします。
ここではコンソール上で利用できるターミナルであるCloudShellを使います。
今回作成したクラスタのAPIエンドポイントはプライベートサブネットに配置したため、そのままではCloudShellからアクセスできません。
CloudShellからプライベートサブネットにアクセスする方法はいくつかありますが、今回はCloudShellのプライベートネットワーク機能を使います。
プライベートネットワークはホームリージョンでのみ使用できます。
CloudShellの設定
APIエンドポイントにアクセスするため、プライベートネットワークの設定を行います。
プライベートネットワークをAPIエンドポイントのサブネットに設定します。
画面右上のアイコンからCloudShellを起動し、ネットワークからエフェメラル・プライベート・ネットワークを選び、APIエンドポイントサブネットをアクティブなネットワークとして使用します。
設定が完了するとネットワークがパブリックからエフェメラルになります。

プライベートネットワークの設定ができたら、OKEのクラスタ詳細画面のクイック・スタート:サンプル・アプリケーションのデプロイ>クラスタのアクセスのローカル・アクセスにあるプライベート・エンドポイント・アクセス・コマンドをコピーして実行します。
(APIエンドポイントサブネットがプライベートサブネットだとCloudShellアクセスが選択できないようです。)

$ oci ce cluster create-kubeconfig --cluster-id <クラスタのOCID> --file $HOME/.kube/config --region ap-tokyo-1 --token-version 2.0.0 --kube-endpoint PRIVATE_ENDPOINT
kubeconfig(~/.kube/config)が作成されます。
kubectlでクラスタにアクセスできることを確認します。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
10.0.10.158 Ready node 27m v1.34.1
10.0.10.16 Ready node 27m v1.34.1
10.0.10.90 Ready node 27m v1.34.1
ワーカーノードが表示され、クラスタにアクセスできていることが確認できました。
これで無料Kubernetesクラスタが使えるようになりました!
サンプルアプリケーションのデプロイ
Kubernetes公式ドキュメントに記載されているdeployment.yamlを適用します。
なお、今回作成したクラスタのバージョンは執筆時点(2025/11)の最新バージョンであるKubernetes 1.34.1およびCRI-O 1.34.0ですが、本バージョンではそのままでは正常に適用できないため、一部修正します。(詳細は別記事にまとめています)
$ wget https://k8s.io/examples/application/deployment.yaml
$ sed -i 's/image: nginx:1.14.2/image: docker.io\/nginx:1.14.2/g' deployment.yaml
修正後のdeployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: docker.io/nginx:1.14.2 #修正
ports:
- containerPort: 80
適用します。
$ kubectl apply -f deployment.yaml
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 2/2 2 2 23s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-5bd9f86786-ch2dm 1/1 Running 0 38s
nginx-deployment-5bd9f86786-k6tvg 1/1 Running 0 38s
deployment.yamlの内容でPodが起動しました。
確認ができたらいったん削除します。
$ kubectl delete -f deployment.yaml
$ kubectl get deployments
No resources found in default namespace.
$ kubectl get pods
No resources found in default namespace.
4. LoadBalancerを使う
LoadBalancer Serviceを作成してOCIのロードバランサ(FLB)が利用できることを確認してみます。
3.で使ったdeployment.yamlにServiceを追加したdeployment_lb.yamlを作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: docker.io/nginx:1.14.2
ports:
- containerPort: 80
---
# 以下追加
apiVersion: v1
kind: Service
metadata:
name: nginx-service
# OCI FLBの設定(帯域幅10MbpsのAlways Free FLB)
# ここがないと有料のロードバランサが作成される
annotations:
oci.oraclecloud.com/load-balancer-type: "lb"
service.beta.kubernetes.io/oci-load-balancer-shape: "flexible"
service.beta.kubernetes.io/oci-load-balancer-shape-flex-min: "10"
service.beta.kubernetes.io/oci-load-balancer-shape-flex-max: "10"
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
適用します。
$ kubectl apply -f deployment_lb.yaml
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 2/2 2 2 5s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-5bd9f86786-8pb7d 1/1 Running 0 18s
nginx-deployment-5bd9f86786-msdvg 1/1 Running 0 18s
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP,12250/TCP 65m
nginx-service LoadBalancer 10.96.240.26 xxx.xxx.xxx.xxx 80:31774/TCP 31s
LoadBalancer Serviceが作成されており、EXTERNAL-IPにグローバルIPアドレスが割り当てられています。
コンソールで確認すると、EXTERNAL-IPに割り当てられたグローバルIPアドレスを持つFLBが作成されています。

また、LB用サブネットのセキュリティリストが自動で更新され、全てのIPアドレス(0.0.0.0/0)からのTCP/80(port: 80)が許可されています。(その他にもLBのヘルスチェックポートなども適切に許可されます。)

セキュリティリストの自動更新を無効にすることもできるようです。
詳細はこちら。
インターネットからFLB経由でPodにアクセスしてみます。
ブラウザでhttp://<FLBのグローバルIPアドレス>にアクセスします。

FLBを経由してPodにアクセスできました!
料金の確認
しばらく4.を適用した状態のままにしましたが、料金は0円でした。

まとめ
OKEを使って無料でマネージドKubernetesクラスタを構築してみました。
Terraformを利用することで構築を自動化でき、簡単に作成/削除ができるようになりました。
学習やちょっとした検証などの用途であれば十分使えるかと思います。
お読みいただきありがとうございました。





