じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

Go開発者のための遊び場を用意する - Kindで始めるKubernetesの開発環境構築

はじめに

Kind (Kubernetes in Docker) は、ローカル環境でKubernetesクラスタを簡単に構築できるツールです。そう、「簡単に」と言いましたが、「簡単」の定義は人によって大きく異なりますから。ある人にとっては「簡単」でも、他の人には「アラート対応を猫に教えるくらい難しい」かもしれません。

さあ、気軽に「簡単な」Kubernetes体験の世界へ飛び込みましょう。途中で迷子になっても、パニックにならないでください。結局のところ、私たちプログラマーは迷子になることが仕事なのですから。

サンプルコード

動いているものをおいておくと記事の安心感と信頼性に繋がるので置いておきます。

github.com

Kind公式ドキュメント

kind.sigs.k8s.io

環境情報

環境としてはLimaを利用しております。

syu-m-5151.hatenablog.com

参考リンク

Kindのセットアップと基本的な使用方法

Kindのインストール

KindはHomebrewやバイナリのダウンロード、Go言語を使用したインストールなど複数の方法でインストールすることができます。

kind.sigs.k8s.io

私はbrew で入れているので一応、コマンド記載しときます。

brew install kind

基本的なクラスタの作成

最も基本的なKindクラスタを作成するには、以下のコマンドを使用します。

kind create cluster

これにより、単一ノードのKubernetesクラスタが作成されます。

カスタム設定でのクラスタ作成

より詳細な設定を行う場合は、YAML設定ファイルを使用します。

# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

このファイルを使用してクラスタを作成するには

kind create cluster --config kind-config.yaml

クラスタの管理

クラスタの一覧を表示:

kind get clusters

特定のクラスタを削除:

kind delete cluster --name cluster-name

特定のクラスタkubeconfigを取得:

kind get kubeconfig --name cluster-name

1. Skaffoldとの統合による高速な開発サイクルの実現

ホットリロード可能なローカル開発環境は、DockerとAirの組み合わせで構築できますが、SkaffoldとKindを用いることで、Kubernetes環境で同等の機能を持つ開発環境を実現することも可能です。

skaffold.dev

サービスのソースコード

変更が分かりやすければ正直なんでも良いのでこちらです。

// main.go
package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World from Go!")
    })
    http.ListenAndServe(":8080", nil)
}

Dockerfileの作成

go.mod のバージョンとベースイメージがズレているとエラーが出るので修正しなきゃいけないですわねー

# Dockerfile
FROM golang:1.22 as builder
WORKDIR /app

# Copy go mod and sum files
COPY go.mod ./

# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed
RUN go mod tidy

# Copy the source from the current directory to the Working Directory inside the container
COPY . .

# Build the Go app
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o go-app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/go-app .
CMD ["./go-app"]

Kubernetes設定ファイルの作成

deployment.yaml にServiceも入れてます。

# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: go-app
  template:
    metadata:
      labels:
        app: go-app
    spec:
      containers:
      - name: go-app
        image: go-app
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: go-app
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: go-app

Skaffold設定ファイルの作成

Skaffold設定ファイルの作成します。プロジェクトのルートディレクトリに skaffold.yaml ファイルを作成します。Dockerfileのビルドとkubectl でのデプロイの両方をやってくれます。

# skaffold.yaml

# Skaffoldの設定ファイル
# このファイルはDockerイメージのビルドとKubernetesへのデプロイを自動化します

apiVersion: skaffold/v2beta26
kind: Config

# ビルド設定
build:
  artifacts:
  - image: go-app  # ビルドされるイメージの名前
    context: .     # ビルドコンテキストのパス(通常はプロジェクトのルートディレクトリ)
    docker:
      dockerfile: Dockerfile  # 使用するDockerfileの名前

# デプロイ設定
deploy:
  kubectl:
    manifests:
    - k8s-*.yaml  # デプロイに使用するKubernetesマニフェストファイルのパターン

# 注意点:
# 1. `go-app`はあなたのアプリケーション名に合わせて変更してください。
# 2. Dockerfileがルートディレクトリにない場合は、パスを適切に調整してください。
# 3. `k8s-*.yaml`は実際のマニフェストファイル名のパターンに合わせて変更してください。
# 4. 必要に応じて、プロファイルやテスト設定を追加することができます。

アプリケーションの実行とテスト

Kindクラスタを作成し、Skaffoldを起動してます。

kind create cluster
skaffold dev --port-forward

この後、main.goDockerfileを編集してください。ファイルを保存すると、Skaffoldが自動的に以下の処理を行います。

  • 変更を検知
  • 新しいDockerイメージをビルド
  • ビルドしたイメージをKindクラスタにロード
  • アプリケーションを再デプロイ

変更の確認

ブラウザやcurlコマンドを使用して、アプリケーションにアクセスし、変更が反映されていることを確認します。

curl http://localhost:8080

2. マイクロサービスアーキテクチャのシミュレーション

Skaffoldを使用して、2つのGoマイクロサービスを含む環境をKindでシミュレートします。

Kindクラスタの作成

# kind-multi-node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 8080
  - containerPort: 30001
    hostPort: 8081
- role: worker
- role: worker

デプロイじゃい

kind create cluster --config kind-multi-node.yaml

サービスのソースコード その1

// service-a/main.go
package main
import (
    "fmt"
    "net/http"
)
func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from Service A!")
    })
    http.ListenAndServe(":8080", nil)
}

サービスのソースコード その2

// service-b/main.go
package main
import (
    "fmt"
    "net/http"
)
func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from Service B!")
    })
    http.ListenAndServe(":8081", nil)
}

Dockerfileの作成

各サービスのディレクトリに以下のDockerfileを作成します。

# service-a/Dockerfile と service-b/Dockerfile
FROM golang:1.22 as builder
WORKDIR /app
COPY go.mod .
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]

Kubernetes設定ファイルの作成

service-aとservice-bのファイルを適切なディレクトリに設置します。

# k8s-manifests.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service-a
spec:
  replicas: 1
  selector:
    matchLabels:
      app: service-a
  template:
    metadata:
      labels:
        app: service-a
    spec:
      containers:
      - name: service-a
        image: service-a
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: service-a
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
    nodePort: 30000
  selector:
    app: service-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service-b
spec:
  replicas: 1
  selector:
    matchLabels:
      app: service-b
  template:
    metadata:
      labels:
        app: service-b
    spec:
      containers:
      - name: service-b
        image: service-b
        ports:
        - containerPort: 8081
---
apiVersion: v1
kind: Service
metadata:
  name: service-b
spec:
  type: NodePort
  ports:
  - port: 8081
    targetPort: 8081
    nodePort: 30001
  selector:
    app: service-b

Skaffold設定ファイルの作成

プロジェクトのルートディレクトリに skaffold.yaml ファイルを作成します。

# skaffold.yaml
apiVersion: skaffold/v2beta29
kind: Config
build:
  artifacts:
  - image: service-a
    context: service-a
    docker:
      dockerfile: Dockerfile
  - image: service-b
    context: service-b
    docker:
      dockerfile: Dockerfile
deploy:
  kubectl:
    manifests:
    - k8s-manifests.yaml

アプリケーションの実行とテスト

Skaffoldを使用してアプリケーションをビルド、デプロイ、そして監視します。

skaffold dev --port-forward

このコマンドは以下の動作を行います。 - サービスAとサービスBのDockerイメージをビルド - ビルドしたイメージをKindクラスタにロード - Kubernetes マニフェストを適用してサービスをデプロイ - ポートフォワーディングを設定 - ファイルの変更を監視し、変更があれば上記のプロセスを再実行

確認

別のターミナルウィンドウで以下のコマンドを実行して、サービスにアクセスできることを確認します。

curl http://localhost:8080  # Service A
curl http://localhost:8081  # Service B

これで、2つのマイクロサービスがKindクラスタ上で実行され、Skaffoldによって自動的に管理されます。ソースコードを変更すると、Skaffoldが自動的に再ビルドとデプロイを行います。

おわりに

本記事では、Kind(Kubernetes in Docker)を使用したGoアプリケーションの開発について詳しく解説しました。Kindの基本的なセットアップから、Skaffoldとの統合による高速な開発サイクルの実現、そしてマイクロサービスアーキテクチャのシミュレーションまで、実践的なアプローチで解説しました。

ここで学んだtipsとセットアップ方法を活用することで、アプリケーション開発者は以下の利点を得ることができます

  1. 効率的な開発サイクル: Skaffoldとの統合により、コード変更から再デプロイまでのプロセスが自動化され、開発速度が大幅に向上します。

  2. 本番環境に近いテスト環境: Kindの柔軟性により、マイクロサービスアーキテクチャや複雑なネットワーク構成を、ローカル環境で簡単にシミュレートできます。

Kindの強力な機能を利用することで、本番環境に近い状態でアプリケーションをテストし、開発サイクルを迅速化できます。これは、特にクラウドネイティブな開発において非常に重要です。

今後のステップとして、以下のような発展的なトピックにチャレンジすることをおすすめします。まぁ一番は俺が書けって話ですけどね。

  • 本番環境に近いテスト環境の構築
  • 設定管理の簡素化
  • データの永続化
  • セキュリティの強化
  • Kindを使用したCI/CDパイプラインの構築
  • サービスメッシュ(例:Istio)のKind環境への導入
  • Kindを使用したカオスエンジニアリングの実践
  • マルチクラスタ環境のシミュレーションとフェデレーション

最後に、Kindはあくまでもローカル開発とテストのためのツールであることを忘れないでください。本番環境への移行時には、クラウドプロバイダーやマネージドKubernetesサービスの特性を考慮し、適切な調整を行う必要があります。