k3s とは軽量 Kubernetes です。しばしばメモリ消費量が少ない、バイナリが小さいなど軽量であることに注目されますが、シンプルで簡単に Kubernetes の機能を使い始められ、学習にも向いたツールです。
今回は k3s で Kubernetes クラスターを構築します。そのうえで、適当な Web アプリケーションをクラスターにデプロイし、インターネットからアクセスできるようにすることを目標とします。
私は Kubernetes 初心者なので、k3s を使って Kubernetes を学習していきたいと思います。
せっかくなので、node は 2 つにします。k3s だとそのような構成も簡単にできます。
また、各 node 間の通信は VPN で暗号化します。これも k3s だと簡単です。
バージョン情報など
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
$ k3s --version
k3s version v1.27.3+k3s1 (fe9604ca)
go version go1.20.5
$ sudo k3s kubectl version --short
Flag --short has been deprecated, and will be removed in the future. The --short output will become the default.
Client Version: v1.27.3+k3s1
Kustomize Version: v5.0.1
Server Version: v1.27.3+k3s1
本記事の内容に影響しないはずですが、動作確認は AWS EC2 の t3.medium インスタンスで行いました。2vCPU でメモリは 4GB です。このスペックで十分動作しました。これより小さいサーバーでも大丈夫だと思います。
なお、AWS EC2 は簡単にサーバーが作成できるから利用しただけで AWS であることは本記事の内容には関係ないはずです。
前提
最低限のスペック
k3s は軽量ですが、いくつかの最低要件があります。
詳細はRequirementsに記載されていますが、下記は特に注意と思われます。
| Spec | Minimum | Recommended |
|---|---|---|
| CPU | 1 core | 2 cores |
| RAM | 512 MB | 1 GB |
ポートを開ける
| Protocol | Port | Source | Destination | Description |
|---|---|---|---|---|
| TCP | 2379-2380 | Servers | Servers | Required only for HA with embedded etcd |
| TCP | 6443 | Agents | Servers | K3s supervisor and Kubernetes API Server |
| UDP | 8472 | All nodes | All nodes | Required only for Flannel VXLAN |
| TCP | 10250 | All nodes | All nodes | Kubelet metrics |
| UDP | 51820 | All nodes | All nodes | Required only for Flannel Wireguard with IPv4 |
| UDP | 51821 | All nodes | All nodes | Required only for Flannel Wireguard with IPv6 |
クラスターを作成
WireGuard の準備
k3s のドキュメント(Network Options)によると
The default backend for Flannel is vxlan. To enable encryption, use the wireguard-native backend.
とあります。というわけで、コンテナ間通信を暗号化するために、WireGuard を使います。
上記のドキュメントからリンクのあるWireGuard Install Guideに従い、その準備をします。と言っても ubuntu だと apt でインストールするだけです。
sudo apt install wireguard
クラスターの立ち上げ
k3s のドキュメント(Quick-Start Guide)に従い、クラスターを作成します。
今回は次のようなコマンドをクラスターを立ち上げたいサーバー内で実行しました。
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server" sh -s - --flannel-backend wireguard-native
クラスターを立ち上げる最小のコマンドは次のとおりです。
curl -sfL https://get.k3s.io | sh -
これにいくつかのオプションを追加しています。
INSTALL_K3S_EXEC は k3s のインストール時につけるフラグを指定します。今回は server を指定しています。server、agentは k3s 用語らしいです。serverはKubernetes のコントロールプレーンが動いているノードでagentはコントロールプレーンがないノードのことです。(参考:Architecture)
INSTALL_K3S_EXECにserverを指定することで、serverとしてノードを立ち上げています。(なお、指定しなくてもデフォルトで server として立ち上がるようではあります)
--flannel-backend wireguard-native は Flannel のバックエンドに WireGuard を使うように指定しています。これで Kubernetes のネットワークが暗号化されます。
続けて、もう一つのサーバーにagentを立ち上げます。
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="agent --server https://xxx:6443 --token yyy" sh -s -
今回もINSTALL_K3S_EXECでオプションを指定しています。
agentを含めることで、agentとしてノードを立ち上げています。
さらにagentはserverに接続するために--serverと--tokenを指定しています。
--serverはserverのアドレスを指定します。
--tokenはserverに接続するためのトークンを指定します。--tokenに指定する値はserverノードの/var/lib/rancher/k3s/server/node-tokenに書かれています。
オプションの指定の仕方はいろいろあるので、下記も参照してください。
動作確認
serverノードで次のコマンドを実行してみます。
$ sudo k3s kubectl get node
すると、agentノードも含めて 2 つのノードが表示されるはずです。
ローカルから kubectl コマンドを実行する
k3s は kubeconfig ファイルを/etc/rancher/k3s/k3s.yamlに作成しているのでこれをローカルにコピーしてきます。
kubectl コマンドが利用する kubeconfig ファイルの指定方法はいくつかありますが、例えばKUBECONFIG環境変数に指定します。
なお、Unable to connect to the server: x509: certificate is valid forというエラーが出る場合は、--insecure-skip-tls-verifyオプションをつけます。証明書の確認をスキップしますが、今回は実運用するわけではなく、学習目的なので、一旦このオプションで解決しても良いと思います。
$ export KUBECONFIG=k3s.yaml
$ kubectl get node --insecure-skip-tls-verify
ノードが 2 つ表示されるはずです。
これでローカルからクラスターを操作できるようになりました。
namespace の作成
アプリケーションをデプロイするための Namespace を作成します。
まず、kubectl get namespaceで既存の Namespace を確認します。
$ kubectl get namespace
NAME STATUS AGE
kube-system Active 9m13s
default Active 9m13s
kube-public Active 9m13s
kube-node-lease Active 9m13s
のようにkubeではじまる kube システム用の Namespace やdefaultが見つかるはずです。
ここに Namespace を追加します。
kubectl create namespaceコマンドで作成することもできますが、ここでは YAML ファイルを作成してkubectl applyコマンドで作成します。次のような YAML ファイルを作成します。
apiVersion: v1
kind: Namespace
metadata:
name: app
labels:
name: app
そして、kubectl applyコマンドで YAML ファイルを Kubernetes に適用します。
$ # (namespace.yamlに上記のYAMLファイルを保存しているとして、)
$ kubectl apply -f namespace.yaml
kubectl get namespaceを再度実行することで、appが追加されていることがわかります。
deployment の作成
次に deployment を作成します。
再度 YAML ファイルを作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: sample-app
name: sample-app
namespace: app
spec:
replicas: 2
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- image: containous/whoami:v1.5.0
name: whoami
ここではwhoamiという動作確認用のイメージを使います。
kubectl applyコマンドで YAML ファイルを Kubernetes に適用します。
$ # (deployment.yamlに上記のYAMLファイルを保存しているとして、)
$ kubectl apply -f deployment.yaml
Kubernetes で deployment を作成すると Pod と ReplicaSet が作成されます。次のコマンドで確認できます。
$ kubectl get pod -n app
$ kubectl get rs -n app
-nは Namespace を指定しています。
service の作成
次に service を作成します。
service とは Pod に関するネットワーク関連の設定です。
service の設定は次のような YAML ファイルで行います。
apiVersion: v1
kind: Service
metadata:
labels:
app: sample-app
name: sample-app
namespace: app
spec:
ports:
- name: "whoami"
port: 18080
protocol: TCP
targetPort: 80
selector:
app: sample-app
type: ClusterIP
次のコマンドで service を作成します。
$ # (service.yamlに上記のYAMLファイルを保存しているとして、)
$ kubectl apply -f service.yaml
service が作成されたことは
$ kubectl get svc -n app
で確認できます。
なお、type: ClusterIPなので、service はクラスター内からしかアクセスできません。現時点でサーバーに curl などでアクセスしようとしてもできないはずです。
portは何でも良いはずですが、traefik が 80 番ポートを使っているので、targetPortは 80 以外の適当な値にしています。
現時点ではサーバー内外から curl などでアクセスしようとしてもできません。
ingress の作成
最後に ingress を作成します。
ingress の設定は次のような YAML ファイルで行います。
ingress とは、クラスター外からのクラスター内の service に対するアクセスを管理するものです。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sample-ingress
namespace: app
spec:
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: sample-app
port:
number: 18080
pathは/apiとしています。
whoami コンテナは/にアクセスすると text で結果を返します。一方で、/apiにアクセスすると JSON で結果を返します。
今回は練習のために/ではアクセスできず、/apiにアクセスする必要があるように設定してみます。
これで ingress が「Service に対する外部からのアクセス(主に HTTP)を管理」していることが確認できるのではないかと思います。
これを次のコマンドで適用します。
$ # (ingress.yamlに上記のYAMLファイルを保存しているとして、)
$ kubectl apply -f ingress.yaml
ingress が作成されたことは
$ kubectl get ingress -n app
で確認できます。
ingress の確認
ingress が作成されると、traefik が自動的に ingress を認識してルーティングを行います。
traefik は k3s に同梱された k3s とは別の OSS です。
curl でリクエストを送信して動作確認してみます。
もしSSL certificate problemなどと証明書関連のエラーが発生すれば一旦-kオプションを付けて無視します。
どの node にリクエストを送信しても whoami コンテナのレスポンスを取得できます。whoami コンテナはコンテナがあるサーバーの IP アドレスなども返してくれるので動作確認で便利です。
また、今回は ingress に/apiと指定しているので、/にアクセスしてもレスポンスを取得できないことも確認できます。
$ curl -k https://aaa.bbb.ccc.ddd/api
これで k3s でアプリをデプロイし、外部からアクセスできるように設定できました。
参考
k3s のドキュメント
k3s 日本語ドキュメント