LoginSignup
3
2

More than 1 year has passed since last update.

WSL2 に kind と MetalLB v0.13.7 を入れてcurlで通信してみた

Last updated at Posted at 2022-08-14

概要

次の構成でGoアプリに対する通信ができるところまで試してみた。

なお、Qiita で既に記事を書いていそうなら載せないつもりだったけど、MetalLB v0.13で破壊的変更もあり、そのまま動かなかったので、備忘録として載せる。

環境

  • Windows 10
  • Ubuntu 20.04 LTS(WSL2)
    • go1.19 (go version)
    • Docker version 20.10.17, build 100c701
  • Kind .. v0.17.0
  • MetalLB .. v0.13.7

前提

  • go インストール済み
  • dockerインストール済み

インストール(KIND関連)

備忘録として、今回の手順を載せるが、実際やるときは一次情報源の本家のドキュメントをベースに行ってください。

  1. ~/go/bin にパスを通しておく

    ~/.bashrcへの追記例

    test -d $HOME/go/bin && export PATH="$HOME/go/bin:$PATH"
    
  2. KINDのインストール

    go install sigs.k8s.io/kind@v0.17.0
    

    インストール確認

    $ $(go env GOPATH)/bin/kind version
    kind v0.17.0 go1.19 linux/amd64
    
  3. クラスタの作成

    $(go env GOPATH)/bin/kind create cluster --name kind-1
    

    確認

    $ kind get clusters
    kind-1
    
  4. kubectl の導入

    KINDを使う分には kubectl が必須でないようで、必要なら導入すする。

    curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s 
    https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
    sudo install -m 755 kubectl /usr/local/bin/kubectl
    rm kubectl
    

MetalLBのインストール

MetalLB > Installationより。

  1. インストール

    $ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
    
  2. アドレスプールの設定

    次の注意点がある

    • MetalLB v0.13 で非互換があり、configMapの指定は認識されない
    • kindで使用しているの docker のIPを指定しないと、IPの割り当てが成功しても通信ができない

    Dockerのネットワーク上のIPを調べる

    $ docker network inspect kind --format '{{json (index .IPAM.Config 0).Subnet}}'
    "172.18.0.0/16"
    

    metallb-IPAddressPool.yaml

    apiVersion: metallb.io/v1beta1
    kind: IPAddressPool
    metadata:
    name: first-pool
    namespace: metallb-system
    spec:
    addresses:
    - 172.18.254.240/28  # ★環境ごとに書き換える
    autoAssign: true     # ★true: 自動割り当て, false: 手動割り当て(loadBalancerIPで指定)
    ---
    apiVersion: metallb.io/v1beta1
    kind: L2Advertisement
    metadata:
    name: l2-ad
    namespace: metallb-system
    spec:
    ipAddressPools:
    - first-pool
    
  3. デプロイ

    $ kubectl apply -f metallb-IPAddressPool.yaml
    ipaddresspool.metallb.io/first-pool created
    l2advertisement.metallb.io/l2-ad created
    

上記の手順については次のドキュメントにあるが、2022-08-28現在、ConfigMapを使う例となっており、IPAddressPoolとして読み替えが必要。

Goアプリケーションのデプロイ

/api/greet/John に応答するだけのアプリ。

main.go

package main

import (
        "encoding/json"
        "fmt"
        "net/http"
        "path/filepath"
        "strings"
)

func greetHandler(w http.ResponseWriter, r *http.Request) {
        sub := strings.TrimPrefix(r.URL.Path, "/api/greet")
        _, name := filepath.Split(sub)
        var m any
        if name != "" {
                m = map[string]interface{}{
                        "message": fmt.Sprintf("Hello, %s!", name),
                }
        } else {
                m = map[string]interface{}{
                        "status": 404,
                }
        }

        resJson, err := json.Marshal(m)
        if err != nil {
                panic(err.Error())
        }

        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintf(w, "%s", string(resJson))
}

func main() {
        mux := http.NewServeMux()
        mux.HandleFunc("/api/greet/", greetHandler)

        http.ListenAndServe(":8080", mux)
}

Dockerfile

容量を削減する目的でマルチステージビルドを使用している。

FROM golang:1.19-alpine3.16 as builder
COPY ./src/main.go ./
RUN go build -o /go-app ./main.go

FROM alpine:3.16 as runtime
EXPOSE 8080
COPY --from=builder /go-app .
ENTRYPOINT ["./go-app"]

Deploy

  1. Build a image

    docker image build --tag greet-go:0.1 .
    
  2. KIND にイメージをPushする

    kind --name kind-1 load docker-image greet-go:0.1
    

    push確認
    go-hello-app が一覧に含まれることが確認できる。

    $ docker container ls --format "table {{.Image}}\t{{.State}}\t{{.Names}}"
    IMAGE                  STATE     NAMES
    kindest/node:v1.25.3   running   kind-1-control-plane
    $ docker exec -it kind-1-control-plane crictl images
    IMAGE                                      TAG                  IMAGE ID            SIZE
    docker.io/kindest/kindnetd                 v20221004-44d545d1   d6e3e26021b60       25.8MB
    docker.io/kindest/local-path-helper        v20220607-9a4d8d2a   d2f902e939cc3       2.86MB
    docker.io/kindest/local-path-provisioner   v0.0.22-kind.0       4c1e997385b8f       17.4MB
    docker.io/library/greet-go                 0.1                  abd18c430d5ec       12.5MB
    quay.io/metallb/controller                 v0.13.7              e73361dabfb86       25.6MB
    quay.io/metallb/speaker                    v0.13.7              738c5d221d601       46.9MB
    registry.k8s.io/coredns/coredns            v1.9.3               5185b96f0becf       14.8MB
    registry.k8s.io/etcd                       3.5.4-0              a8a176a5d5d69       102MB
    registry.k8s.io/kube-apiserver             v1.25.3              4bc1b1e750e34       76.5MB
    registry.k8s.io/kube-controller-manager    v1.25.3              580dca99efc3b       64.5MB
    registry.k8s.io/kube-proxy                 v1.25.3              86063cd68dfc9       63.3MB
    registry.k8s.io/kube-scheduler             v1.25.3              5225724a11400       51.9MB
    registry.k8s.io/pause                      3.7                  221177c6082a8       311kB
    
  3. KIND にデプロイする

    今回はイメージがローカルにしかないので imagePullPolicyNever を設定する必要があることに注意する。

    greet-go.deployment.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: greet-go-app
    spec:
      selector:
        matchLabels:
          app: greet-go-app
      replicas: 2
      template:
        metadata:
          labels:
            app: greet-go-app
        spec:
          containers:
          - name: greet-go
            image: greet-go:0.1
            imagePullPolicy: Never
            ports:
            - containerPort: 8080
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: greet-go-service
    spec:
      type: LoadBalancer
      selector:
        app: greet-go-app
      ports:
        - name: http
          port: 3000
          targetPort: 8080
          protocol: TCP
    

    デプロイ

    $ kubectl apply -f greet-go.deployment.yaml
    deployment.apps/greet-go-app created
    service/greet-go-service created
    

動作確認

Pod IPに対して直接通信する

Pod がデプロイできていれば行える確認。Serviceがなくとも動く。

  1. Pod のIPを確認する

    $ kubectl get pods -l app=greet-go-app -o custom-columns="Pod IP":.status.podIP,"Container port":.spec.containers[0].ports[].containerPort
    Pod IP       Container port
    10.244.0.7   8080
    10.244.0.6   8080
    
  2. Control plane から curl を叩く

    $ docker ps --format "table {{.Names}}\t{{.Image}}" --filter name=kind
    NAMES                  IMAGE
    kind-1-control-plane   kindest/node:v1.25.3
    $ docker container exec -it kind-1-control-plane curl -s http://10.244.0.7:8080/api/greet/John | python3 -m json.tool
    {
        "message": "Hello, John!"
    }
    
  3. Internal DNSでPodに通信する

    以下は Service で2つのPodのいずれかの名前解決を行う。
    この例では Pod側のポートは 8080 だが Service側のポートは 3000 であることに注意する(この辺りの理解がごちゃ混ぜにならないように敢えて変えている)。

    $ kubectl get services greet-go-service
    NAME               TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)          AGE
    greet-go-service   LoadBalancer   10.96.94.74   172.18.254.240   3000:30485/TCP   65m
    $ docker container exec -it kind-1-control-plane curl -s http://greet-go-service.default.svc.cluster.local:3000/api/greet/John | python3 -m json.tool
    curl: (6) Could not resolve host: greet-go-service.default.svc.cluster.local
    $ kubectl run -q -n default -it curl --image=curlimages/curl --rm --restart=Never --wait=true -- -s -L http://greet-go-service.default.svc.cluster.local:3000/api/greet/John | python3 -m json.tool
    {
        "message": "Hello, John!"
    }
    

    補足

    • kubectl run

      • -qpod "curl" deleted などの kubectl run の出力を削るため

      • --wait=true は特定の警告メッセージを出すのを抑制する
        kubectl run が即終わってしまうと次のメッセージが出る

        warning: couldn't attach to pod/curl, falling back to streaming logs: unable to upgrade connection: container curl not found in pod curl_default
        

ポート転送して確認する

  1. Pod name を確認する

    $ kubectl get pods -l app=greet-go-app
    NAME                            READY   STATUS    RESTARTS   AGE
    greet-go-app-78cc5cfdd8-4q6lt   1/1     Running   0          13m
    greet-go-app-78cc5cfdd8-kdpdl   1/1     Running   0          13m
    
  2. 別の端末でポート転送を行う
    下記は、Ctrl-Cを打つまで端末を占有するのでWSL内の別端末で確認する。以下のように実行できた。

    $ POD_NAME="greet-go-app-78cc5cfdd8-4q6lt"
    $ kubectl port-forward $POD_NAME 3000:8080
    Forwarding from 127.0.0.1:3000 -> 8080
    Forwarding from [::1]:3000 -> 8080
    ...(注:Ctrl-Cを打つまで復帰しない)
    
  3. Curlでレスポンスを確認する

    $ curl -s http://localhost:3000/api/greet/John \
    --header "Content-Type: application/json" \
    --request "GET" | python3 -m json.tool
    {
        "message": "Hello, John!"
    }
    

External IP経由の確認

この確認が本命となる。

  1. EXTERNAL-IP の確認をする
    うまくいっていれば次のように、EXTERNAL-IPを取得できる

    $ kubectl get services
    NAME               TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)          AGE
    greet-go-service   LoadBalancer   10.96.94.74   172.18.254.240   3000:30485/TCP   17m
    kubernetes         ClusterIP      10.96.0.1     <none>           443/TCP          65
    
  2. Curlでレスポンスを確認する

    $ curl -s http://172.18.254.240:3000/api/greet/John \
    --header "Content-Type: application/json" \
    --request "GET" | python3 -m json.tool
    {
        "message": "Hello, John!"
    }
    

なお、この時点でWindows 10のホスト側からの通信は失敗。多分、追加の設定が必要。

補足: トラブルシューティング

いくつかはまった点を書いておく。

curl で Failed to connect to 172.18.xxx.yyy port 8080: No route to host と怒られる

この記事は Deployment を使った記事で、試しに Pod で作ろうとしたときに発生した。

状況

  • EXTERNAL-IP は取得できている
  • arp コマンドの結果、 HWaddress(incomplete) になっている
  • sudo arping 172.18.xxx.yyy -c 3 ですべてタイムアウトする

原因

結果的には、selector で Pod が見つけられない状態にあった。
labels に app: ~ を追加し問題は解消した。

  • go-hello-app.pod.yaml

    apiVersion: v1
    kind: Pod
    metadata:
    name: go-hello-pod
    labels:                # 追加
        app: go-hello-app  # 追加
    spec:
    containers:
    - name: go-hello-app
        image: greet-go:0.1
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: go-hello-service
    spec:
    type: LoadBalancer
    selector:
        app: go-hello-pod # pod の labelsを見ている
    ports:
        - name: http
        port: 3000
        targetPort: 8080
        protocol: TCP
    

OS再帰動詞、WSLを再開後に IPAddressPool の更新ができなくなる

原因

  • metallb の Controller が Unhelthy になっている

暫定対処

  • kind のクラスタを再作成したらうまくいった

参考

3
2
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
3
2