本連載ではKubernetesやマイクロサービスを活用するにあたりどんな準備を進めておけばいいか整理します。第2回は開発効率を向上させることができるSkaffoldを紹介します。
1. はじめに
アプリケーションの開発においてKubernetesを活用する事により様々な恩恵を受けられる一方で、コンテナのビルドやレジストリへのイメージの登録など開発の手順は増えてしまいます。開発作業の中ですべての手順に手動で対応してしまうと、開発効率が悪く非常に面倒に感じることでしょう。連載2回目は、Kubernetessを活用したアプリケーション開発における問題を解決し、開発効率を向上させるSkaffoldを紹介します。
本記事では各技術要素(Kubernetes/Docker/Minikube等)の基本的な概要などについては記載しておりません。適宜各公式ドキュメントを参照してください
2. Skaffold
Skaffoldは継続的、効率的にKubernetes-nativeなアプリケーション開発を行うためにGoogleによって開発された開発ツールです。2021年9月時点での最新バージョンは1.31.0です。
2.1 開発の流れとSkaffoldの自動化
以下の図はローカル環境でコードの編集からアプリケーションをデプロイして動作確認するまでの主な流れにります。Skaffoldはソースコードの変更を自動検知して、図中の青枠の処理を開発者に代わって実行してくれます。開発者は煩雑なコマンドの実行などから開放され、コードの開発に集中し、開発効率を向上させることができます。
図中の各ステップについて以下に説明します。
コンテナイメージのビルド
開発したソースコードからコンテナイメージのビルドを行います。SkaffoldはDockerfileによるビルド実行だけでなく、JibやBazelなどのビルドツールをサポートしています。独自スクリプトを実行することも可能で、必要に応じてイメージビルドの前処理など、ユーザーのニーズに合わせたビルドを実行することができます。テスト
テストの実行を行います。Skaffoldでのテストは2種類定義されています。一つはGoogleのcontainer-structure-testを利用したコンテナのテストの実行です。container-structure-testの詳細については公式を参照してください。もう一つはCustome Testと呼ばれ、アプリケーションのソースコードのテストの実行をサポートします。コンテナイメージのpush
設定ファイルに定義されているコンテナレジストリにビルドしたイメージをpushします。ローカルでMinikubeを利用して開発している場合はMinikubeのDockerレジストリを利用することもできます。デプロイ
Kubernetesのマニフェストファイルを適用しビルドしたコンテナイメージをデプロイします。SkaffoldはHelm、Kustomizeをサポートしているので、柔軟なマニフェストの管理と実行ができます。複数のマニフェストをまとめて管理することができるので、例えばローカルで開発を行う場合にAWSのlocalstackをアプリケーションと合わせてデプロイすることもできます。リソースの掃除
Skaffoldを停止すると、Skaffoldは自身がデプロイしたリソースを削除します。動作確認のためデプロイしたコンテナなどを手動で削除する必要がなくなります。
2.2 利用シーン
2.1章に記載した通り、コードの編集から動作確認までの一連の開発作業をローカル環境で繰り返し行う場合にSkaffoldを最大限活用することができます。そのため、実装、単体テストフェーズなどでは利用することはなく、単体テストより後のフェーズ、例えば以下のシーンで利用します。
ローカル環境にアプリケーションをデプロイして
・機能の疎通確認を行う場合
・障害調査・対応を行う場合
・デバッグを行う場合
・機能改修を行う場合
2.3 Skaffoldの実行とモード
Skaffoldは以下のコマンドを実行することで利用することができます。起動時のパラメータを変更することで、開発目的に応じた制御を行う事ができます。
- skaffold init ・・・実行ディレクトリ配下を解析してSkaffoldの定義ファイルskaffold.yamlを生成します。
- skaffold run ・・・ビルドからデプロイまでを1回実行します。
- skaffold dev ・・・ソースコードの変更を検知して、ビルドからデプロイを繰り返し実行します。
- skaffold debug ・・・デバッグポートを有効化してデプロイする。他は
skaffold dev
と同じ。
以下はskaffold dev
を実行した結果になります。コンテナのイメージビルドからデプロイまで実行し、最後にソースコードの変更の監視を開始しています。この状態でソースコードを変更すると自動的にビルドからデプロイまで実行されます。また、Ctrl+C
を実行すると、デプロイ済みのコンテナを削除してからSkaffoldは停止します。
$ skaffold dev Listing files to watch... - skaffold-example Generating tags... - skaffold-example -> skaffold-example:v1.29.0-31-ga45330606 Checking cache... - skaffold-example: Not found. Building Starting build... Found [minikube] context, using local docker daemon. Building [skaffold-example]... Sending build context to Docker daemon 3.072kB Step 1/8 : FROM golang:1.15 as builder 1.15: Pulling from library/golang 627b765e08d1: Pulling fs layer ~略(DockerImageのpull)~ 5946d17734ce: Pull complete Digest: sha256:e23b6a841e3491a06d4513667b158a0cba90e313da3deea1cbe021fda417444f Status: Downloaded newer image for golang:1.15 ---> 40349a2425ef Step 2/8 : COPY main.go . ---> 9a7e8353b545 ~略(Docker build)~ Step 8/8 : COPY --from=builder /app . ---> 8f7d0b413ea7 Successfully built 8f7d0b413ea7 Successfully tagged skaffold-example:v1.29.0-31-ga45330606 Starting test... Tags used in deployment: - skaffold-example -> skaffold-example:8f7d0b413ea78528606b984556cdf04e0508a05c904b5e8e04a684f874ec11a8 Starting deploy... - pod/getting-started created Waiting for deployments to stabilize... Deployments stabilized in 126.71ms Press Ctrl+C to exit Watching for changes...
2.4 Skaffoldの定義ファイル
Skaffoldは起動ディレクトリ直下にあるskaffold.yaml定義ファイルを読み込み、設定内容に従って動作します。ここでは設定の中核となる一部の項目についてしか触れていませんが、Skaffoldにはたくさんの便利な設定を行う事ができます。詳細は公式ドキュメントで確認してください。
apiVersion: skaffold/v2beta20 kind: Config build: artifacts: - image: skaffold-example # Docker image名。この名称でビルドしタグ付けします。 # Dockerfileの指定を省略した場合はSkaffoldがDockerファイルを自動的に検出します。 deploy: kubectl: manifests: - k8s-* # デプロイ時に実行するマニフェストを指定します。ワイルドカード指定可。
2.5 便利な機能
Port-fowardオプション
Skaffoldのコマンド引数にport-forwardオプションを付与して実行すると、Skaffoldがアプリケーションのポートやデバッグポートを自動でport-forwardしてくれます。複数のPODを起動した場合などポート番号が重複する場合はSkaffoldが自動的にポート番号をインクリメントして重複しないように調整してから公開してくれます。Debugモード
skaffold debug
コマンドでSkaffoldを起動することで、デバッグを有効にしてアプリケーションを起動することができます。ただし、debugオプションを付与するだけでは、デバッグモードが有効になりません。例えばJavaの場合は特定の環境変数が設定されていないとデバッグが有効にならないなど利用する言語によって前提条件があるので注意が必要です。 詳細についてはこちらを参照してください。Profile
複数の環境を利用する場合などにプロファイルを活用して接続先の変更や環境差異を吸収することなどができます。環境変数、Kubernetesの接続先、ビルドパラメータなど様々な定義を行う事ができます。詳細については公式を参照してください。
Skaffoldには今回紹介した機能以外にもたくさんの機能があります。皆さんの開発内容・スタイルに合う機能を探してみてください。開発における問題の解決や開発効率を向上させることができるかもしれません。
2.6 Skaffoldを使ってみよう!
それでは実際にSkaffoldを使ってみましょう。今回は機能改修を行う利用シーンを想定して、Golangで「Hello World!」を出力するWebアプリケーションを準備、Skaffoldをdevモードで起動、最後に機能改修して動作確認するところまで実践してみましょう。なお、マニフェストの管理にはkustomizeを利用します。
環境準備
Skaffoldを利用するための環境準備を行います。今回はコンテナの実行環境としてMinikube、コンテナレジストリはMinikube付属のdockerレジストリを利用します。Kubectl、Minikube、Skaffoldを以下の公式ドキュメントの手順に従いインストールしてください。
ソースコードの準備
- コードとフォルダ構成
skaffold-hands-on ├ manifest # Kubernetesのマニフェスト │ ├ k8s-deploy.yaml # Deploymentのマニフェスト │ ├ k8s-svc.yaml # Serviceのマニフェスト │ └ kustomization.yaml # アプリケーションのマニフェストを管理するkustomizeの設定ファイル ├ Dockerfile # コンテナイメージをビルドするためのDockerfile。 ├ main.go # Hello Worldを応答するGolangのWebアプリケーションコード。 └ skaffold.yaml # Skaffoldの設定ファイル。skaffold initで自動生成。
- コードの内容
main.go
パス「/」にhttpでアクセスすると「Hello World!」を返却するGolangのWebアプリケーションコードです。
package main import ( "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) }
Dockerfile
GolangのWebアプリケーションをコンテナイメージとしてビルドするためのDockerfileです。
FROM golang:1.17-buster as builder WORKDIR /app COPY main.go ./ RUN go mod init app COPY . ./ RUN go build -v -o server FROM debian:buster-slim RUN set -x && apt-get update COPY --from=builder /app/server /app/server CMD ["/app/server"]
k8s-deploy.yaml
コンテナイメージ「skaffold-hands-on」のPODを1つ起動するデプロイメントの定義です。ポート番号は8080を公開しています。
apiVersion: apps/v1 kind: Deployment metadata: labels: app: skaffold-hands-on name: skaffold-hands-on spec: replicas: 1 selector: matchLabels: app: skaffold-hands-on template: metadata: labels: app: skaffold-hands-on spec: containers: - image: skaffold-hands-on # コンテナイメージはskaffold-hands-on name: skaffold-hands-on ports: - containerPort: 8080 # 8080ポートを公開する
k8s-svc.yaml
デプロイされたskaffold-hands-onをServiceとして公開する定義です。
apiVersion: v1 kind: Service metadata: labels: app: skaffold-hands-on name: skaffold-hands-on spec: ports: # 8080ポートを公開 - port: 8080 protocol: TCP targetPort: 8080 selector: app: skaffold-hands-on # skaffold-hands-onのPODを指定。
kustomization.yaml
アプリケーションで利用するマニフェストを管理します。このハンズオンではkustomizeの機能については掘り下げませんが、環境差異の吸収ができるなど、便利な機能がたくさんあります。興味がある方は公式を見てください。
resources: - k8s-deploy.yaml # Deploymentのマニフェスト - k8s-svc.yaml # Serviceのマニフェスト
skaffold initの実行
skaffold-hands-onディレクトリにてinitコマンドを実行して設定ファイルskaffold.yamlを生成します。SkaffoldがDockerfile、マニフェストファイルを探して設定ファイル(skaffold.yaml)を生成してくれます。今回は生成された定義ファイルをそのまま利用します。
$ skaffold init apiVersion: skaffold/v2beta20 kind: Config metadata: name: skaffold-hands-on build: artifacts: - image: skaffold-hands-on docker: dockerfile: Dockerfile deploy: kustomize: paths: - manifest ? Do you want to write this configuration to skaffold.yaml? Yes Configuration skaffold.yaml was writtenon to skaffold.yaml? (y/N) y You can now run [skaffold build] to build the artifacts or [skaffold run] to build and deploy or [skaffold dev] to enter development mode, with auto-redeploy
skaffold devの実行
skaffold-hands-onディレクトリにてskaffold dev
コマンドを実行してskafflodを実行します。また、--porg-foward
オプションをつける事でMinikubeにデプロイしたServiceをPort-forwardします。
$ skaffold dev --port-forward Listing files to watch... - skaffold-hands-on ~略~ Tags used in deployment: - skaffold-hands-on -> skaffold-hands-on:3805bf074eba16d126c72d5a6cd79776f27647af0c55822f4ce1a6562266bf88 Starting deploy... - service/skaffold-hands-on created - deployment.apps/skaffold-hands-on created Waiting for deployments to stabilize... - deployment/skaffold-hands-on is ready. Deployments stabilized in 2.375 seconds Press Ctrl+C to exit Watching for changes... Port forwarding service/skaffold-hands-on in namespace default, remote port 8080 -> http://127.0.0.1:8080
Minikubeに「skaffold-hands-on」のPodとServiceがデプロイされている事を確認しましょう。
$ kubectl get all NAME READY STATUS RESTARTS AGE pod/skaffold-hands-on-7f74bd5444-67vgd 1/1 Running 0 57s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/skaffold-hands-on ClusterIP 10.97.94.104 <none> 8080/TCP 57s ~略~
動作確認のためcurlでアプリケーションにアクセスしてみましょう。「Hello World!」が表示されたら、Skaffoldによってアプリケーションがビルドからデプロイまで実行し、port-fowardされたエンドポイントにアクセスしてアプリケーションの動作確認ができたことになります。
$ curl localhost:8080 Hello World!
アプリケーションの機能改修
それでは、main.goファイルの「Hello World!」を「Hello Skaffold!」に編集して保存してみましょう。Skaffoldが変更を検知して、再ビルドしてデプロイまで実行してくれます!
~略~ Press Ctrl+C to exit Watching for changes... Generating tags... - skaffold-hands-on -> skaffold-hands-on:latest Some taggers failed. Rerun with -vdebug for errors. Checking cache... - skaffold-hands-on: Not found. Building Starting build... Found [minikube] context, using local docker daemon. Building [skaffold-hands-on]... Sending build context to Docker daemon 10.24kB ~略~ Successfully built 23ee30dbb341 Successfully tagged skaffold-hands-on:latest Starting test... Tags used in deployment: - skaffold-hands-on -> skaffold-hands-on:23ee30dbb341d2ff7a24b506bf7a444c04a4caeecc8c12625aa77f4b154231cd Starting deploy... - deployment.apps/skaffold-hands-on configured Waiting for deployments to stabilize... - deployment/skaffold-hands-on is ready. Deployments stabilized in 2.314 seconds Port forwarding service/skaffold-hands-on in namespace default, remote port 8080 -> http://127.0.0.1:8080 Watching for changes...
curlでアプリケーションにアクセスしてみましょう。「Hello World!」ではなく、「Hello Skaffold!」が表示されたら、修正後のアプリケーションの動作確認ができた事になります。
$ curl localhost:8080 Hello Skaffold!
Skaffoldの停止
最後にCtrl+C
でSkaffoldを停止してみましょう。ServiceとDeploymentを削除してからSkaffoldが停止しています。
~略~ Starting deploy... - service/skaffold-hands-on created - deployment.apps/skaffold-hands-on created Waiting for deployments to stabilize... - deployment/skaffold-hands-on is ready. Deployments stabilized in 1.297 second Port forwarding service/skaffold-hands-on in namespace default, remote port 8080 -> http://127.0.0.1:8080 Press Ctrl+C to exit Watching for changes... Cleaning up... - service "skaffold-hands-on" deleted - deployment.apps "skaffold-hands-on" deleted
3. おわりに
今回はKubernetesを基盤としたシステムを開発していく上で課題となる開発環境の複雑化や手順が増えることに対して、Skaffoldを活用した開発方法を紹介しました。Kubernetesを活用していくにはアプリケーションのコンテナ化やローカル環境構築の難しさなど、これまでにはなかった課題も出てきますが、今回のようなプロダクトをうまく活用していけば負荷の軽減は可能です。
次回はサービス間の疎通やテスト、デバッグなどを効率よく行うためのツールであるTelepresenceを紹介します。Skaffoldだけでなく、必要に応じて適切なツールを活用し、Kubernetesを基盤としたシステムの開発をより簡単に、より快適に行えるようになりましょう。