k3dとskaffoldで始めるKubernetesローカル開発
要約
- Kubernetesのローカル開発環境を整える
- skaffoldでビルド・デプロイを自動化する
- docker-compose likeに使えるようMakefileを作ってみる
はじめに
Kubernetes使ってますか? コンテナオーケストレーションのデファクトスタンダードと言われて久しいですが、私が業務で関わる案件は大体ECSが使われています(汗) 自社開発ではKubernetes一択なのですが、なぜコンテナオーケストレーションとして、なぜKubernetesを選ぶかというと以下が理由です。
- デファクトスタンダードである
- 特定のCloudベンダーにロックインされないので、ポータブルである
- ローカルでも論理的には近しい環境を構築できる(アーキテクチャ次第だが)
- k8sのエコシステムを享受できる
- 枯れた技術(?)
ローカルでクラスターを構築できるという点が便利だなと思います。 素のKubernetesクラスターを立てるのは大変なのですが、次のツールなどが出揃っていて、LaptopやDesktopでサクッとクラスターを構築できることが魅力です。これは、ローカル開発においてローカルのk8s cluster上にサービスをデプロイして本番に近しい構成でテストをすることができます。Cloudを使わなくてもテストができるので、結合テストやE2Eの自動化がやりやすくなりますし、開発スピードを向上できることが気に入っています。
Kubernetesディストリビューション
ローカル開発向けで代表的な Kubernetesディストリビューションを列挙します
- microk8s
- minikube
- k3s
- k3d
- Docker for Mac
- etc...
他にもあると思いますが、私が試したことがあるのが上記です。
気に入ったものを使っていけば良いと思いますが、
今回の記事では、k3d
を使っていこうと思います。
VMベースのものより、コンテナーベースのほうが軽量でストレスが少ないかなと思いました。
k3d
は、Docker上にk3sのServer, Agentが動くので、Clusterを作ったり破棄したりするのが簡単にできて便利だと思います。ベースになっているk3s
はCNCFがホストしていますので、普及・成長を期待しています。
k8s clusterのネットワーク上にRegistryのDockerコンテナを動作させて、ローカルでイメージをプッシュすれば、Clusterから参照することができます。これで、一通りローカルでデプロイまで完結することができます。
前提
- OS
- ubuntu 20.04
- Tools
- docker
- kubectl
- make
- curl
この記事の全コードは GitHub を参照ください。 CloudShellで試すこともできます。
workspace directory
mkdir -p ~/workspace/projectcd ~/workspace/project
k3d install
k3dのGitHubに記載されている通り、Installを行います。
この記事を作成時点では、v4.2.0
です。
curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash
k3dがCLIで利用できることを確認します
$ k3d versionk3d version v4.2.0k3s version v1.20.2-k3s1 (default)$ k3dUsage: k3d [flags] k3d [command]Available Commands: cluster Manage cluster(s) completion Generate completion scripts for [bash, zsh, powershell | psh] help Help about any command image Handle container images. kubeconfig Manage kubeconfig(s) node Manage node(s) version Show k3d and default k3s versionFlags: -h, --help help for k3d --verbose Enable verbose output (debug logging) --version Show k3d and default k3s versionUse "k3d [command] --help" for more information about a command.
Makefileを作る
docker-compose likeにClusterのcreateができるようMakefileを作ります
簡単に説明するとcreate
で下記のことを行います。down
は逆のことを行います。
- k3dでclusterを作る
- volumeを作る
- registory containerをk3d clusterと同じネットワークで実行する
# ~/workspace/project/Makefile# k3d, registry varsCLUSTER_NAME=myk3dclusterREGISTRY_IMAGE=registry:2REGISTRY_HOST=registry.localREGISTRY_PORT=5000REGISTRY_VOLUME=local_registry_volumeDEFAULT_REPO="$(REGISTRY_HOST):$(REGISTRY_PORT)"# for k3d cluster.PHONY: createcreate: k3d cluster create $(CLUSTER_NAME) \ --volume "$(PWD)/registry/registries.yaml:/etc/rancher/k3s/registries.yaml" docker volume create $(REGISTRY_VOLUME) docker container run -d \ --net k3d-$(CLUSTER_NAME) \ --name $(REGISTRY_HOST) \ -v $(REGISTRY_VOLUME):/var/lib/registry \ --restart always \ -p $(REGISTRY_PORT):$(REGISTRY_PORT) \ $(REGISTRY_IMAGE).PHONY: startstart: k3d cluster start $(CLUSTER_NAME).PHONY: devdev: skaffold dev --profile dev --port-forward --default-repo $(DEFAULT_REPO).PHONY: downdown: k3d cluster stop $(CLUSTER_NAME).PHONY: destroydestroy: docker stop $(REGISTRY_HOST) docker rm $(REGISTRY_HOST) docker volume rm $(REGISTRY_VOLUME) k3d cluster delete $(CLUSTER_NAME)
registry config
k3dのprivate registryに関する公式ドキュメントに記載されている通り、registries.yamlを配置することでローカルに立てたRegistryにアクセスすることが可能になります。
mkdir -p ~/workspace/project/registry
# ~/workspace/project/registry/registries.yaml
mirrors:
"registry.local:5000":
endpoint:
- http://registry.local:5000
makeでclusterを作ったり破棄する
- create
cluster及びregistryを生成します。
$ make createk3d cluster create myk3dcluster \ --volume "/home/sakurai/workspace/project/registry/registries.yaml:/etc/rancher/k3s/registries.yaml"WARN[0000] No node filter specified INFO[0000] Created network 'k3d-myk3dcluster' INFO[0000] Created volume 'k3d-myk3dcluster-images' INFO[0001] Creating node 'k3d-myk3dcluster-server-0' INFO[0003] Creating LoadBalancer 'k3d-myk3dcluster-serverlb' INFO[0010] Cluster 'myk3dcluster' created successfully! INFO[0010] You can now use it like this: kubectl cluster-infodocker volume create local_registry_volumelocal_registry_volumedocker container run -d \ --net k3d-myk3dcluster \ --name registry.local \ -v local_registry_volume:/var/lib/registry \ --restart always \ -p 5000:5000 \ registry:27a343d43f1486db95b886a1985f83527dfea59e60bb25bb8da863dd3cee09dbe
docker psで確認します。
$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES7a343d43f148 registry:2 "/entrypoint.sh /etc…" About a minute ago Up About a minute 5000/tcp, 0.0.0.0:5000->5000/tcp registry.local4b1d863e3581 rancher/k3d-proxy:v3.0.1 "/bin/sh -c nginx-pr…" About a minute ago Up About a minute 80/tcp, 0.0.0.0:45627->6443/tcp k3d-myk3dcluster-serverlb589456e30bac rancher/k3s:v1.18.6-k3s1 "/bin/k3s server --t…" 2 minutes ago Up About a minute k3d-myk3dcluster-server-0
cluster生成時に自動的に..kube/config
にアクセス情報が記載されるようです。
下記のコマンドでkubernetesにアクセスできるか確認します。
kubectl cluster-info
- stop
k3d containerのdocker stop が行われます。
$ make stopk3d cluster stop myk3dclusterINFO[0000] Stopping cluster 'myk3dcluster'
- start
k3d containerのdocker run が行われます。
$ make startk3d cluster start myk3dclusterINFO[0000] Starting cluster 'myk3dcluster' INFO[0000] Starting Node 'k3d-myk3dcluster-server-0'
- down
cluster及びregistryを削除します。
$ make downk3d cluster stop myk3dclusterINFO[0000] Stopping cluster 'myk3dcluster' docker stop registry.localregistry.localdocker rm registry.localregistry.localdocker volume rm local_registry_volumelocal_registry_volumek3d cluster delete myk3dclusterINFO[0000] Deleting cluster 'myk3dcluster' INFO[0000] Deleted k3d-myk3dcluster-serverlb INFO[0000] Deleted k3d-myk3dcluster-server-0 INFO[0000] Deleting cluster network '8bc0fecee2ca0660c549c7630a1689fc0f90e1082fedb1f60ae5895b655f9f6e' INFO[0000] Deleting image volume 'k3d-myk3dcluster-images' INFO[0000] Removing cluster details from default kubeconfig... INFO[0000] Removing standalone kubeconfig file (if there is one)... INFO[0000] Successfully deleted cluster myk3dcluster!
skaffold config
make dev
が動作するようskaffoldを設定します。
- skaffold.yaml
apiVersion: skaffold/v2beta1kind: Configmetadata: name: service_namebuild: artifacts: - image: hello context: . tagPolicy: sha256: {} local: useBuildkit: truedeploy: kubectl: manifests: - manifests/hello.yaml
k8s manifests
skaffoldのdeployでkubectlがapplyするmanifestを配置します。
- manifests/hello.yaml
apiVersion: v1kind: Servicemetadata: name: hellospec: ports: - protocol: TCP port: 8080 targetPort: 8080 selector: app: hello type: NodePort---apiVersion: apps/v1kind: Deploymentmetadata: labels: app: hello name: hellospec: replicas: 1 selector: matchLabels: app: hello template: metadata: labels: app: hello spec: containers: - image: hello imagePullPolicy: "IfNotPresent" name: hello command: - "./server" ports: - containerPort: 8080 protocol: TCP
Hello, World Service
8080
でHello, World
を出力するHTTP ServerをGoで作ります。
package mainimport ( "fmt" "net/http")func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World")}func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil)}
Kubernetes上で動作するContainerImageを作ります。
FROM golang:1.15 as builder
WORKDIR /go/src/github.com/owner/repo
ENV GO111MODULE="on"
ENV CGO_ENABLED=0
COPY . .
RUN go build -o server main.go
FROM alpine
RUN apk add --no-cache ca-certificates
COPY --from=builder /go/src/github.com/owner/repo/server /go/bin/server
WORKDIR /go/bin/
install skaffold
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \sudo install skaffold /usr/local/bin/
ローカル開発
これでプログラムの準備ができました。
make dev
を実行します。
DEFAULT_REPO
はcluster作成時に一緒に作成したregistryのホスト名に置き換わります。
$ make devskaffold dev --port-forward --default-repo "registry.local:5000"Listing files to watch... - helloGenerating tags... - hello -> registry.local:5000/hello:latestChecking cache... - hello: Found LocallyTags used in deployment: - hello -> registry.local:5000/hello:df5353aa06239ed065f981abe3c1198ea7f2473694e6163f0256b44018c877ebLoading images into k3d cluster nodes... - registry.local:5000/hello:df5353aa06239ed065f981abe3c1198ea7f2473694e6163f0256b44018c877eb -> FoundImages loaded in 113.175619msStarting deploy... - service/hello created - deployment.apps/hello createdWaiting for deployments to stabilize... - deployment/hello is ready.Deployments stabilized in 3.433 secondsPress Ctrl+C to exitWatching for changes...Port forwarding service/hello in namespace default, remote port 8080 -> address 127.0.0.1 port 8080
node.jsでvue.jsやreactで開発された経験のある方はnpm run dev
などのコマンドでHotReloadを利用されているかと思います。skaffoldでもファイルの変更を検知して自動的にDocker build, kubectlでデプロイすることまでできます。port-forwardをしているので、portのバッティングなどがなければ、127.0.0.1:8080
にアクセスするとHello, World Serviceにアクセスできると思います。
きちんと動作しているかcurlコマンドを叩いてみましょう。
$ curl localhost:8080Hello, World
main.go
のHello, World
の部分をHello, k8s
の文字列に変えると再ビルド、デプロイが行われることを確認できます。また、curlコマンドを叩いてみましょう。
$ curl localhost:8080Hello, k8s
まとめ
必要最低限ですが、k3dとskaffoldを使ったローカル開発環境を構築しました。skaffoldをproduction,staging,develop環境でも使おうとするとprofileを分けたり、kustomizeやhelmを使って環境差異を埋めるようなことが必要になると思います。そのため、実用には遠いと思いますが、これからKubernetesでローカル開発環境を整えていこうという方にとって開発イメージがつかめて、一助になれば幸いです。
クラウドアンドビルド合同会社ではGoogle Cloudを使ったインフラ構築、システム開発、働き方改革ソリューションを提供しています。ご気軽に、ご相談・お問い合わせください。
