LoginSignup
0
0

LimaでK3sのクラスター環境を構築する

Posted at

Lima の仮想マシン内に K3s のクラスターを構築してみました。

1. 仮想マシンの実行

今回は arm64 環境を使っているため、下記の設定で仮想マシンを実行します。
containerd を rootful モードで実行するために containerd.system を true に設定しています。

k3s_cl.yaml
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 にマウントする必要があります。

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 を付けて実行します。

K3s Server 実行
$ 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 としてコピーしておきます。

k3s.yaml のコピー
$ mkdir ~/.kube
$ sudo nerdctl cp server-1:/etc/rancher/k3s/k3s.yaml ~/.kube/config
$ sudo chown $USER ~/.kube/config

kubectl コマンドをインストールします。

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 を確認します。

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 ファイルをコピーしておきます。

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 ファイルの内容
K3s Agent 実行
$ 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つになりました。

Node 確認
$ 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 のクラスターを構築できたので、次のようなサンプルアプリケーションを実行して動作確認します。

sampleapp/main.go
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 を用意してビルドします。

sampleapp/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
プライベートレジストリへ push
$ sudo nerdctl push localhost:5000/sampleapp

なお、localhost のような特定のリポジトリ名を使うと HTTP で push するのでこれで問題ありませんが、それ以外のリポジトリ名(例えば registry.example.com:5000)にすると HTTPS で push しようとするので --insecure-registry オプションが必要になります。

実行

下記のマニフェストで Pod を実行します。

sampleapp.yaml
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
リソース作成(Pod 実行)
$ kubectl apply -f sampleapp.yaml

Pod を確認すると、次のようにそれぞれの Node で 1つずつ Pod が実行されていました。

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 を確認します。

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 のログはそれぞれ次のようになっており、問題なく動作しているようです。

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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0