Tagbangers Blog

[Mac 版] 開発用ローカル Kubernetes 構築方法2種

Kubernetes 初心者の JK です

今回はローカルで Kubernetes の環境を作成して本番環境と同じようにドメインでアクセスして動作するかを確認する方法をご紹介します

なお、DNS の設定は Mac のやり方となるため他の OS では異なる設定が必要になる場合があります

サンプルリポジトリ

下記のリポジトリを使用します

https://github.com/koyama-tagbangers/k8s-web-app-sample

  • api ディレクトリには node.js の簡易 API サーバプロジェクトを用意しています
  • web ディレクトリには next.js を使用したプロジェクトを用意しています
  • k8s ディレクトリには Kubernetes で使用するマニフェストファイルを用意しています

開発環境で動作確認

node.js を事前にインストールしておいてください

brew install node

api ディレクトリと web ディレクトリそれぞれでサーバを起動します

cd api
npm ci
npm run start
cd web
npm ci
npm run dev

この状態で http://localhost:3000 にアクセスして動作確認をします

内容は http://localhost:8080/oranges を fetch してデータを表示しているだけです

API

Web

API の URL 設定は NODE_ENV 環境変数を見て切り替えています

const baseUrl = process.env.NODE_ENV === 'development' ? 'localhost:8080' : 'api.jk.test'

NODE_ENV は開発時は development ビルド後は production に自動設定される環境変数です

Buildpacks を使用して Docker Image を作成する

Kubernetes でアプリを使用するために Docker Image を用意する必要があります

brew install docker

Dockerfiles を作成する方法がありますが、自動で行ってくれる Buildpacks を今回使います

brew install buildpacks/tap/pack

pack に用いる Builder は heroku を指定します(Next.js においてこれ以外では正常に動作しませんでした)

(cd api && pack build jk/api --builder heroku/buildpacks --buildpack heroku/nodejs)
(cd web && pack build jk/web --builder heroku/buildpacks --buildpack heroku/nodejs)

実行には少し時間がかかります、出来上がると Image の確認ができます

$ docker images
REPOSITORY  TAG     IMAGE ID      CREATED       SIZE
jk/api      latest  97820836aac8  40 years ago  614MB
jk/web      latest  9ada7d1e8b20  40 years ago  713MB

作成日時がオーパーツと化してますが、Buildpacks の仕様です
https://github.com/buildpacks/pack/issues/931#issuecomment-719524493

試しに Docker Image を起動してみます

docker run --rm -p 8080:8080 jk/api
docker run --rm -p 3000:3000 jk/web

Buildpack で作成した Docker Image は起動時に npm start コマンドを発火します

http://localhost:8080 の API は正常に稼働していますが、http://localhost:3000 の Web は NODE_ENV が production のために http://api.jk.test/oranges に fetch を試みて失敗しています

このため実際の動作確認は次のローカルの Kubernetes 環境の構築が必要です

ローカルの Kubernetes 環境を構築する

方法① Docker Desktop の Kubernetes を使用する

brew cask install docker

Docker Desktop には Kubernetes 機能も同封されています

デフォルトでオフになっているので設定画面から機能を有効にします(時間がかかります)

有効にすると Kubernetes に必要な Docker Image が幾つか Pull されています

クラスタ名は docker-desktop となっています

$ kubectl config get-contexts
CURRENT   NAME             CLUSTER          AUTHINFO         NAMESPACE
*         docker-desktop   docker-desktop   docker-desktop   

Dashboard の有効化

必須ではありませんが、GUI で Kubernetes の状況を確認するために Dashboard をデプロイします

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.4/aio/deploy/recommended.yaml

下記のコマンドを打ち込むとブラウザで確認することができます

kubectl proxy

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ にアクセスします

トークンが必要となります、下記のコマンドで引っ張ってこれるのでこちらの値を入力してサインインします

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | awk '/^deployment-controller-token-/{print $1}') | awk '$1=="token:"{print $2}'

参考: https://stackoverflow.com/a/47761914

Ingress の有効化

NGINX Ingress Controller をデプロイすることで Ingress 機能を有効化します

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/cloud/deploy.yaml

DNS の設定

今回は .test で終わる次の2つのドメインを用意します

  • API → http://api.jk.test
  • Web → http://jk.test

何も設定していない状況だと当然 test ドメインに ping を打っても Unknown host になります

$ ping test
ping: cannot resolve test: Unknown host
$ ping jk.test
ping: cannot resolve jk.test: Unknown host

これらのドメインを localhost (127.0.0.1) に向けさせるため2つの設定が必要です

まず /etc/resolver ディレクトリに test ファイルを作成して下記を入力します

nameserver 127.0.0.1

これで test ドメインが localhost に向いたので ping でエラーは発生しなくなりましたがレスポンスは帰ってきません

次に開発用の DNS を設定するために Dnsmasq をセットアップします

brew install dnsmasq

/usr/local/etc/dnsmasq.conf に下記を追記します

address=/.test/127.0.0.1

サービスとして Dnsmasq を起動します(sudo のつけ忘れに気をつけてください)

sudo brew services start dnsmasq

これで test ドメインがワイルドカード付きで起動しました

$ ping test
PING test (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.025 ms
$ ping jk.test
PING jk.test (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.027 ms

ワイルドカードのため、test でおわるドメインは全てレスポンスが帰ってきます

補足

一部の環境では 127.0.0.1 だと Pod 内で設定したドメインにつながらないケースがあったため、その場合は IP アドレスを代わりに入れてください
ワンライナーで取得するコマンドは下記です

ifconfig en0 | awk '/inet / { print $2 }'

マニフェストファイルのデプロイ

k8s ディレクトリにある全てのマニフェストファイル(.yml)を Kubernetes にデプロイします

kubectl apply -f k8s

ダッシュボードからワークロードの状態が全て緑色であれば問題ありません

問題がある場合は Pods から該当の Pod のログを確認します

問題ない場合は http://jk.test からアプリが正常に動作していることを確認できます

クラスターの一時停止・削除

使用しない場合は Docker Desktop の設定からチェックボックスを外すことで一時停止できます
また、Reset Kubernetes Cluster から Kubernetes をリセットすることができます

一時停止する際は Dnsmasq の停止もしておきます

sudo brew services stop dnsmasq

方法② Minikube with HyperKit を使用する

brew install minikube hyperkit

Minikube は VM モード(HyperKit)で起動する必要があります

minikube start --vm --driver=hyperkit

デフォルトプロファイルの minikube で Kubernetes クラスターが作成されます

$ kubectl config get-contexts
CURRENT   NAME             CLUSTER          AUTHINFO         NAMESPACE
          docker-desktop   docker-desktop   docker-desktop   
*         minikube         minikube         minikube         default

Minikube 内の docker daemon で docker image を作成

Minikube 内部の docker daemon は Docker Desktop 使用時の localhost の docker daemon とは別にあります

このため先ほど作成した Docker Image は Minikube で扱うことができません

Minikube 内部の docker daemon を docker コマンドで利用するために下記のコマンドを入力します

eval $(minikube docker-env)

このコマンド以降は docker コマンドでは Minikube 上の docker daemon を利用するため再度 Docker Image のビルドを行います
なお、上記のコマンドはターミナルを再起動するたびに必要です

eval $(minikube docker-env)
(cd api && pack build jk/api --builder heroku/buildpacks --buildpack heroku/nodejs)
(cd web && pack build jk/web --builder heroku/buildpacks --buildpack heroku/nodejs)

Dashboard の有効化

下記のコマンドを打つだけで Dashboard 機能が有効になり、ログインなしで Dashboard にアクセスできます

minikube dashboard

Ingress の有効化

下記のコマンドを打つだけです

minikube addons enable ingress

DNS の設定

下記のコマンドを打つことで、Dnsmasq を使用せずに DNS の設定を行ってくれます

minikube addons enable ingress-dns

/etc/resolver/test の nameserver を Minikube の IP アドレスに変える必要があります

IP アドレスは下記から取得できます

minikube ip

なお、この地点では ping を飛ばしても Unknown host になります

マニフェストファイルのデプロイ

kubectl apply -f k8s

同様の手順で http://jk.test からアプリが正常に動作していることを確認できます

なお、Minikube では ingress.yml で指定したドメイン以外は Unknown host となります

$ ping test
ping: cannot resolve test: Unknown host
$ ping jk.test
PING jk.test (192.168.64.5): 56 data bytes
64 bytes from 192.168.64.5: icmp_seq=0 ttl=64 time=0.240 ms

クラスターの一時停止・削除

それぞれコマンド上で行います

minikube stop
minikube delete

HyperKit を使用しない場合の問題

minikube start 時にデフォルトでは docker driver を使用しますが、Ingress の設定の時に怒られてしまいます

$ minikube addons enable ingress

❌  Exiting due to MK_USAGE: Due to networking limitations of driver docker on darwin, ingress addon is not supported.
Alternatively to use this addon you can use a vm-based driver:

        'minikube start --vm=true'

To track the update on this work in progress feature please check:
https://github.com/kubernetes/minikube/issues/7332

それぞれのメリット・デメリット

Docker Desktop

メリット

  • localhost で Kubernetes が起動されるため、普段ビルドした Docker Image をそのまま利用できる
  • GUI ベースでリソースの設定、コンテナの可視化が行えるため確認が楽

デメリット

  • DNS の設定が少し面倒、場合によっては /etc/hosts を直接編集する方が楽

Minikube

メリット

  • Dashboard, DNS の設定が非常に楽
  • プロファイル機能で複数のクラスターの管理が可能
    • 複数のプロジェクトを横断する場合などに便利
  • Kubernetes や Docker のバージョンなどを指定できる

デメリット

  • ローカルの Docker Image が利用できないため Image をリポジトリにプッシュするか Minikube 上で再ビルドする必要がある
    • eval $(minikube docker-env) の実行を忘れてビルドする事故が起きやすい
  • リソースの設定もローカルの Docker とは別にする必要がある
  • Minikube を使っている間に他の開発環境がおかしくなる時がある(環境が汚れているだけ?)

まとめ

Minikube は特に DNS の設定周りが非常に楽ですが、Docker Image ビルド前の eval $(minikube docker-env)  を忘れることがたびたびあります

Docker Desktop は制限がありますが、リソースなどを共有できるのが便利です

どちらも気軽に使えるツールのため、状況に応じて使い分けていくと良いと思います