Lima の仮想マシン内に K3s のクラスターを構築してみました。
1. 仮想マシンの実行
今回は arm64 環境を使っているため、下記の設定で仮想マシンを実行します。
containerd を rootful モードで実行するために containerd.system
を true に設定しています。
images:
- location: "https://cloud-images.ubuntu.com/releases/23.10/release/ubuntu-23.10-server-cloudimg-arm64.img"
arch: "aarch64"
mounts:
- location: "~/temp/k3s_cl"
writable: true
containerd:
system: true
user: false
仮想マシンを実行して、以降の作業は仮想マシン内で実施します。(作業ディレクトリは ~/temp/k3s_cl)
% limactl start --tty=false k3s_cl.yaml
% limactl shell k3s_cl
2. プライベートレジストリの実行
K3s のクラスター環境に必須というわけではありませんが、自前のサンプルアプリケーションを実行したいので Docker のプライベートレジストリを実行しておきます。
$ sudo nerdctl run -d --name reg1 -p 5000:5000 registry:2
これで、HTTP 接続用のプライベートレジストリが起動します。
このプライベートレジストリからコンテナイメージを取得させるには、K3s の Server や Agent の実行時に下記のようなファイルを /etc/rancher/k3s/registries.yaml
にマウントする必要があります。
mirrors:
localhost:5000:
endpoint:
- "http://reg1:5000/v2"
この設定により、リポジトリ名が localhost:5000
のコンテナイメージを http://reg1:5000/v2
から取得するようになります。
3. K3s Server 実行
K3s Server を実行します。
kubectl コマンドで https://127.0.0.1:6443
へ接続する事になるので 6443 ポートで接続できるようにしておきます。
なお、特権モードが必要そうなので --privileged
を付けて実行します。
$ sudo nerdctl run -d --privileged --name server-1 -p 6443:6443 -v $(pwd)/registries.yaml:/etc/rancher/k3s/registries.yaml rancher/k3s server
kubectl の準備
kubectl コマンドで操作できるように、K3s Server(server-1)の /etc/rancher/k3s/k3s.yaml
ファイルを ~/.kube/config
としてコピーしておきます。
$ mkdir ~/.kube
$ sudo nerdctl cp server-1:/etc/rancher/k3s/k3s.yaml ~/.kube/config
$ sudo chown $USER ~/.kube/config
kubectl コマンドをインストールします。
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl"
$ chmod +x kubectl
$ sudo mv kubectl /usr/local/bin/
これで kubectl コマンドを使用できるようになったので、kubectl で Node を確認します。
$ kubectl get node
NAME STATUS ROLES AGE VERSION
d5f7e7bfd478 Ready control-plane,master 12m v1.29.1+k3s2
4. K3s Agent 実行
次に、K3s Agent を実行して Node を 1つ追加してみます。
K3s Server への接続にトークンが必要になるため、K3s Server の /var/lib/rancher/k3s/server/token
ファイルをコピーしておきます。
$ sudo nerdctl cp server-1:/var/lib/rancher/k3s/server/token ./
token ファイルや K3s Server の URL を下記の環境変数へそれぞれ設定して Agent を実行します。
環境変数 | 内容 |
---|---|
K3S_URL | K3s Server の URL |
K3S_TOKEN | token ファイルの内容 |
$ sudo nerdctl run -d --privileged --name agent-1 -e K3S_URL=https://server-1:6443 -e K3S_TOKEN=$(cat ./token) -v $(pwd)/registries.yaml:/etc/rancher/k3s/registries.yaml rancher/k3s agent
これで Node が 2つになりました。
$ kubectl get node
NAME STATUS ROLES AGE VERSION
d5f7e7bfd478 Ready control-plane,master 16m v1.29.1+k3s2
dd20bb3b3071 Ready <none> 19s v1.29.1+k3s2
5. 動作確認
K3s のクラスターを構築できたので、次のようなサンプルアプリケーションを実行して動作確認します。
package main
import (
"fmt"
"log"
"net/http"
"os"
"strings"
)
func main() {
port := os.Getenv("APP_PORT")
if strings.TrimSpace(port) == "" {
port = "3000"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Printf("url query: %s", r.URL.RawQuery)
fmt.Fprintf(w, "ok")
})
log.Fatal(http.ListenAndServe(":"+port, nil))
}
ビルド
Dockerfile を用意してビルドします。
FROM golang:1.21-bullseye AS build
WORKDIR /work
COPY main.go ./
RUN CGO_ENABLED=0 go build -o sampleapp main.go
FROM scratch
COPY --from=build /work/sampleapp /app/sampleapp
CMD ["/app/sampleapp"]
ビルド後にプライベートレジストリへ push しておきます。
$ sudo nerdctl build -t localhost:5000/sampleapp ./sampleapp
$ sudo nerdctl push localhost:5000/sampleapp
なお、localhost
のような特定のリポジトリ名を使うと HTTP で push するのでこれで問題ありませんが、それ以外のリポジトリ名(例えば registry.example.com:5000)にすると HTTPS で push しようとするので --insecure-registry
オプションが必要になります。
実行
下記のマニフェストで Pod を実行します。
kind: Service
apiVersion: v1
metadata:
name: sampleapp
labels:
app: sampleapp
spec:
selector:
app: sampleapp
ports:
- protocol: TCP
port: 3001
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sampleapp
labels:
app: sampleapp
spec:
replicas: 2
selector:
matchLabels:
app: sampleapp
template:
metadata:
labels:
app: sampleapp
spec:
containers:
- name: sampleapp
image: localhost:5000/sampleapp
env:
- name: APP_PORT
value: "3001"
ports:
- containerPort: 3001
$ kubectl apply -f sampleapp.yaml
Pod を確認すると、次のようにそれぞれの Node で 1つずつ Pod が実行されていました。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sampleapp-845b8c8fb5-x8gmj 1/1 Running 0 70s 10.42.0.10 d5f7e7bfd478 <none> <none>
sampleapp-845b8c8fb5-g5lks 1/1 Running 0 70s 10.42.1.4 dd20bb3b3071 <none> <none>
Service を確認します。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 26m
sampleapp LoadBalancer 10.43.90.28 10.4.0.24,10.4.0.25 3001:31343/TCP 116s
sampleapp の EXTERNAL-IP へ接続して、アプリケーションの処理を呼び出してみます。
$ curl "http://10.4.0.24:3001/?a"
ok
$ curl "http://10.4.0.24:3001/?b"
ok
Pod のログはそれぞれ次のようになっており、問題なく動作しているようです。
$ kubectl logs sampleapp-845b8c8fb5-x8gmj
2024/02/07 13:39:54 url query: a
$ kubectl logs sampleapp-845b8c8fb5-g5lks
2024/02/07 13:39:58 url query: b