タクシーアプリ「GO」のバックエンドを開発している柳浦です。最近リリースされたSkaffold v2がCloud Runに対応したので、調べて使ってみました。
この記事はMobility Technologies Advent Calendar 2022の17日目の記事です。
SkaffoldはGoogleが開発した、コンテナ化されたアプリケーションの継続的な開発とデリバリーをサポートするコマンドラインツールです。2018年に公開されました。元々はKubernetesをターゲットとしたツールでしたが、2022年10月に発表されたv2でCloud Runに対応したので、Cloud Runでの使い方を紹介します。
公式ドキュメント曰く、
Skaffold is a command line tool that facilitates continuous development for container based & Kubernetes applications. Skaffold handles the workflow for building, pushing, and deploying your application, and provides building blocks for creating CI/CD pipelines. This enables you to focus on iterating on your application locally while Skaffold continuously deploys to your local or remote Kubernetes cluster, local Docker environment or Cloud Run project.
<抄訳> Skaffoldはコンテナベースの開発とKubernetesアプリケーションの継続的開発を円滑に行うための手助けをするコマンドラインツールです。ビルド、プッシュそしてデプロイを行うワークフローを管理し、CI/CDパイプラインを構築するためのビルディングブロックを提供します。ローカルあるいはリモートKubernetesクラスタ、ローカルDocker環境またはCloud Runプロジェクトへ継続敵にデプロイしながら、ローカルでのイテレーションに集中することができます。
との事です。これを鵜呑みにすれば、開発以外の面倒な作業を良い感じにやってくれる(ものを構築できる)可能性をひしひしと感じます。もっと具体的に見ていきましょう。
Skaffoldのfeatureは以下の通りに喧伝されています。
なるほど。幾分特徴が明確になった気がします。特に気になったものを列挙します。
opinionatedな構成というのがどれほどのものなのか気になります。ここを受け入れられるかが肝ではなかろうかと推測します。そしてここまで見てきた上で公式ドキュメントにあるデモ動画を見ると得心が行きました。動画上ではskaffold devコマンドを使ってローカル環境への継続的な自動デプロイを実現していますが、更にそれを推し進めて行くのが想像できます。更にドキュメントを読み進めてみます。
skaffold devを実行すると以下の一連の作業を(一部並行して)Skaffoldは行います。またいずれのステップもスキップ可能です。
もう一つ重要な概念としてprofilesがあります。詳しくは後述しますが、このprofilesを使うと各ステージで利用するツールをフラグによって切り替えることが容易にできます。例えば、開発時にはローカルのDockerを使ってビルドし、kubectlを使ってminikubeへデプロイして、プロダクション環境へはCloud BuildでビルドしたイメージをHelmでリモートのKubernetesクラスタにデプロイするという具合にツールを切り替える事が可能です。各ステージ毎に利用可能なツールを示した図を公式ドキュメントから引用します。
なるほど、正直この図だけで今までの話はよかったのではないかという位わかりやすいですね。少し気分を入れ替えてskaffoldコマンドを見てみます。
何はともあれskaffoldコマンドを叩いてみます。
❱ skaffold
A tool that facilitates continuous development for Kubernetes applications.
Find more information at: <https://skaffold.dev/docs/getting-started/>
End-to-end Pipelines:
run Run a pipeline
dev Run a pipeline in development mode
debug Run a pipeline in debug mode
Pipeline Building Blocks:
build Build the artifacts
test Run tests against your built application images
deploy Deploy pre-built artifacts
delete Delete any resources deployed by Skaffold
render Generate rendered Kubernetes manifests
apply Apply hydrated manifests to a cluster
verify Run verification tests against skaffold deployments
Getting Started With a New Project:
init Generate configuration for deploying an application
Other Commands:
completion Output shell completion for the given shell (bash or zsh)
config Interact with the global Skaffold config file (defaults to `$HOME/.skaffold/config`)
diagnose Run a diagnostic on Skaffold
fix Update old configuration to a newer schema version
schema List JSON schemas used to validate skaffold.yaml configuration
survey Opens a web browser to fill out the Skaffold survey
version Print the version information
Usage:
skaffold [flags] [options]
Use "skaffold <command> --help" for more information about a given command.
Use "skaffold options" for a list of global command-line options (applies to all commands).
なるほど。パイプラインを実行するサブコマンドがrun/dev/debugで、build、deployなどでパイプラインを構成するステップを実行できるようです。また、initサブコマンドが設定を自動生成してくれるようです。ここから始めてみます。
まずはskaffold initから始めます。
❱ skaffold init
one or more valid build configuration must be present to build images with Skaffold; please provide at least one build config and try again, or run `skaffold init --skip-build`
怒られました。ヘルプを参照します。
❱ skaffold init --help
Generate configuration for deploying an application
Options:
--analyze=false: Print all discoverable Dockerfiles and images in JSON format to stdout
-a, --artifact=[]: '='-delimited Dockerfile/image pair, or JSON string, to generate build artifact
(example: --artifact='{"builder":"Docker","payload":{"path":"/web/Dockerfile.web"},"image":"gcr.io/web-project/image"}')
--assume-yes=false: If true, skaffold will skip yes/no confirmation from the user and default to yes
--compose-file='': Initialize from a docker-compose file
--default-kustomization='': Default Kustomization overlay path (others will be added as profiles)
-f, --filename='skaffold.yaml': Path or URL to the Skaffold config file
--force=false: Force the generation of the Skaffold config
--generate-manifests=false: Allows skaffold to try and generate basic kubernetes resources to get your project
started
-k, --kubernetes-manifest=[]: A path or a glob pattern to kubernetes manifests (can be non-existent) to be added to
the kubectl deployer (overrides detection of kubernetes manifests). Repeat the flag for multiple entries. E.g.: skaffold
init -k pod.yaml -k k8s/*.yml
-m, --module=[]: Filter Skaffold configs to only the provided named modules
--remote-cache-dir='': Specify the location of the git repositories cache (default $HOME/.skaffold/repos)
--skip-build=false: Skip generating build artifacts in Skaffold config
--sync-remote-cache='always': Controls how Skaffold manages the remote config cache (see `remote-cache-dir`). One
of `always` (default), `missing`, or `never`. `always` syncs remote repositories to latest on access. `missing` only
clones remote repositories if they do not exist locally. `never` means the user takes responsibility for updating remote
repositories.
Usage:
skaffold init [flags] [options]
Use "skaffold options" for a list of global command-line options (applies to all commands).
go.modがあればいいんでしょうか。単純なWebアプリケーションxを作ってgo.modを追加します。x/go.modを用意して再チャレンジです。
one or more valid Kubernetes manifests are required to run skaffold
やっぱり怒られたので心を落ち着かせてソースコードを読みます。……現在はまだCloud Run対応していないようですので一旦skaffold initを使うのは諦めます。skaffold.yamlを準備しましょう。skaffold.yamlのスペックを見てやっていきます。とりあえずskaffold.yamlはこうしました。
apiVersion: skaffold/v3
kind: Config
metadata:
name: skaffold-test
labels: {}
annotations: {}
requires:
- configs: []
profiles:
- name: dev
manifests:
rawYaml:
- run-dev.yaml
build:
tagPolicy:
sha256: {}
artifacts:
- image: x
ko:
dir: ./x
main: .
dependencies:
paths:
- "**/*.go"
deploy:
cloudrun:
projectid: PROJECT_ID_YOU_USE
region: asia-northeast1
とりあえずこんな感じで簡単に作ってみました。Goのソースは極めて単純なHTTPサーバです。metadata などはとりあえず適当に、profilesもとりあえずdevだけで作りました。ビルドには時間がかかると嫌なのでkoを使っています。また、tagPolicyはとりあえずsha256にして、latestをpushしてそのdigestを使ってイメージを指定する形にしました。run-dev.yamlはこんな感じです。
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: x
spec:
template:
spec:
containers:
- image: x
また、現在のファイルツリーはこんな感じです。
.
├── run-dev.yaml
├── skaffold.yaml
└── x
├── go.mod
└── main.go
実際のところ、xというコンテナイメージはGoogle Cloud Artifact Registryにpushした上で、更にそれをデプロイしたいです。SkaffoldドキュメントのImage Repository Handlingページに解決策が書いてあり、default-repo機能というものを使えばよさそうです。default-repoを使うには3つ方法があり、skaffoldコマンドにフラグとして渡すか、SKAFFOLD_DEFAULT_REPO環境変数で指定するか、またはSkaffoldのグローバルコンフィグ(skaffold configコマンドで管理される)を使うかです。最終的に書き換えられるイメージ名は、default-repoで指定したドメイン名とイメージの名前によって生成戦略が変わります。詳しくは先述のドキュメントを参照してください。さて、今回はdirenvを使って手軽にdefault-repoを指定します。.envrcに以下の内容を書き込みます。
export PROJECT_ID=PROJECT_ID_YOU_USE
export KO_DOCKER_REPO=asia-northeast1-docker.pkg.dev/${PROJECT_ID}/docker-private
export SKAFFOLD_DEFAULT_REPO=${KO_DOCKER_REPO}
koも同様の設定を渡す必要があるのでついでに設定しました。Go言語でアプリケーションを作成している場合には爆速でDockerイメージが作成できるのでkoはおすすめです。なお、Cloud Artifact Registryのリポジトリは事前に作っておく必要があります。これでskaffold run --profile=devを実行してみれば、Cloud Runへのデプロイまでが実行されるはずです。
❱ skaffold run --profile=dev
WARN[0000] failed to detect active kubernetes cluster node platform. Specify the correct build platform in the `skaffold.yaml` file or using the `--platform` flag subtask=-1 task=DevLoop
Generating tags...
- x -> asia-northeast1-docker.pkg.dev/*****/docker-private/x:latest
Checking cache...
- x: Not found. Building
Starting build...
Building [x]...
Using base gcr.io/distroless/static:nonroot@sha256:ed05c7a5d67d6beebeba19c6b9082a5513d5f9c3e22a883b9dc73ec39ba41c04 for github.com/yanana/x
Using build config x for github.com/yanana/x
Building github.com/yanana/x for linux/amd64
Publishing asia-northeast1-docker.pkg.dev/*****/docker-private/x:latest
Published asia-northeast1-docker.pkg.dev/*****/docker-private/x@sha256:1ccfff297255c31e78041987b3504f7fb5317d48c0f32b18d6f78fdf21649e3d
Build [x] succeeded
Starting test...
Tags used in deployment:
- x -> asia-northeast1-docker.pkg.dev/*****/docker-private/x:latest@sha256:1ccfff297255c31e78041987b3504f7fb5317d48c0f32b18d6f78fdf21649e3d
Starting deploy...
Deploying Cloud Run service:
x
x: Service starting: Deploying Revision. Waiting on revision x-qvmng.
x: Service starting: Deploying Revision. Waiting on revision x-qvmng.
Cloud Run Service x finished: Service started. 0/1 deployment(s) still pending
You can also run [skaffold run --tail] to get the logs
確認するとちゃんとデプロイされていました! なお、Cloud Runにデプロイされるマニフェストはskaffold renderコマンドで確認出来ます。
❱ skaffold render --profile=dev
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: x
namespace: default
spec:
template:
spec:
containers:
- image: asia-northeast1-docker.pkg.dev/*****/docker-private/x:latest@sha256:1ccfff297255c31e78041987b3504f7fb5317d48c0f32b18d6f78fdf21649e3d
ちゃんとダイジェストまで指定してくれていますね。ソースを修正して再度skaffold runを実行すれば、新しいイメージをlatestタグでプッシュして、そのダイジェスト値を指定してデプロイを行ってくれます。
これまで見てきたようにSkaffoldはCloud Runの開発において大変有用のものですが、追い切れていない部分もあります。
実は2022年9月のアップデートでCloud Deployを使ってCloud Runをデプロイすることができ、なおかつCloud Deployはその裏でSkaffoldを使います。したがって今回の内容を踏まえて更に踏み込んだCDをCloud Deployを使って行う事ができます。Cloud Deploy(とSkaffold)を使ったCDパイプラインの構築については改めて紹介させてください。
興味のある方は 採用ページ も見ていただけると嬉しいです。
Twitter @mot_techtalk のフォローもよろしくお願いします!