概要

今まではスクリプトにk8sのデプロイ処理を書いていましたが、Skaffoldが発表されたのでDraftと一緒に試してみます!
logo.png

環境

  • Skaffold: v0.3.x
  • Draft: v0.12.x
  • Helm: v2.8.x
  • Kubernetes(以後k8s): v1.9.x
  • DockerRegistry: Google Container Registry(以後GCR)

ソースコード

前提

  • helmを使う
    • 必須ではないため、適宜kubectl applyなどに読み替えてください
  • k8sのセットアップが完了している
  • asia.gcr.ioへのログイン(認証)が完了している

検証してみる内容

実際の運用環境に近い内容で検証していきます。

想定環境

  • デプロイ先の環境はlocalを含め、stageの2種類
    • 実際はprodなどの環境もあると思うので、増えても対応が可能な設定にする
  • 全ての環境は別々のClusterとなる
    • Namespaceで環境を分ける場合はちょっとコマンドが変わりますが実現は可能
  • local環境はminikubeDocker For (Windows|MAC)で構築
    • 記事中はDocker For MACを使用
  • stageはGKE(k8s)を使用
  • サンプルアプリケーションはGolang

実現したいこと

  • helmを利用したい(必須ではない)
  • localから未コミットのソースをコンパイルしてデプロイしたい
  • 複数のClusterにデプロイしたい
  • 生成するDockerイメージのタグを指定をしたい
  • プライベートなDocker Registryを使用したい

デプロイするソースコードの一部

全部入りソースはこちら

  • サンプルアプリはHello worldを繰り返し表示します
main.go
package main

import (
    "fmt"
    "time"
)

func main() {
    for {
        fmt.Println("Hello world")
        time.Sleep(time.Second * 1)
    }
}
  • Dockerファイルの内容はビルド環境と実行環境を兼ねています。
Dockerfile
FROM golang:1.9.4-alpine3.7

WORKDIR /go/src
COPY main.go .
RUN go build -o app

CMD ["./app"]
  • helmはよくあるDeploymentServiceの設定です
    • Draft用に同じような設定がcharts/appに配置されています
helm/Chart.yaml
apiVersion: v1
name: foo-app
version: 0.1.0
helm/templates/deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: foo-app
  labels:
    app: foo-app
spec:
  replicas: {{ .Values.replicas }}
  template:
    metadata:
      labels:
        app: foo-app
    spec:
      containers:
        - name: foo-app
          image: {{ .Values.image }}
helm/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: foo-app
  labels:
    app: foo-app
spec:
  type: NodePort
  ports:
    - port: 8080
      targetPort: 8080
      protocol: TCP
      name: foo-app
  selector:
    app: foo-app
helm/values.yaml
replicas: 1
image: asia.gcr.io/{gcp_project}/test/foo-app

local環境へのデプロイ

local環境へのデプロイを通じて基本的な操作を見ていきます。
どのツールも開発途中の状態(未コミット)でデプロイできますが、Gitコミットのハッシュ値を使用するのでGit管理されている必要があります。

スクリプト(Makefile)でデプロイ

まずはスクリプトだけでデプロイする方法を試します。

処理内容は以下です。

  • Dockerファイルを元にコンテナイメージを作成
  • helmを使ってコンテナイメージの差し替え(デプロイ)
Makefile
# Const
#===============================================================
gcp_project    := {gcp_project}
image_repo     := asia.gcr.io/$(gcp_project)/test/foo-app
tag            := $(shell date +'%Y-%m-%dT%H%M%S')

# Task
#===============================================================
plain-deploy: docker-build helm-apply

helm-apply:
    helm upgrade --install \
      --set image.tag=$(tag) \
      foo-app charts/app

docker-build:
    docker build -t $(image_repo):$(tag) .
    docker tag $(image_repo):$(tag) $(image_repo):latest

  • local環境へデプロイ
$ make plain-deploy

Skaffoldでデプロイ

Skaffoldを使えば設定ファイルの設置だけでデプロイ可能です。

処理内容は以下です。

  • Dockerファイルを元にコンテナイメージを作成
  • helmを使ってコンテナイメージの差し替え(デプロイ)
skaffold.yaml
apiVersion: skaffold/v1alpha2
kind: Config
build:
  tagPolicy:
    sha256: {}
  artifacts:
  - imageName: asia.gcr.io/{gcp_project}/test/foo-app
    workspace: .
  local: {}
deploy:
  helm:
    releases:
    - name: skaffold-app
      chartPath: helm
      namespace: default
      values:
        image: asia.gcr.io/{gcp_project}/test/foo-app
  • local環境へデプロイ
$ skaffold run

Draftでデプロイ

Draftも設定ファイルの設置だけでデプロイ可能です。

処理内容は以下です。

  • Dockerファイルを元にコンテナイメージを作成
  • DockerRegistryへコンテナイメージをpush
  • helmを使ってコンテナイメージの差し替え(デプロイ)

他のツールと違う点はDockerRegistryへのプッシュが強制されます。
local環境の場合、DockerRegistryへのプッシュは不要な作業となってしまいます。
コンテナイメージのサイズが大きいと、このデメリットは大きなストレスになりそうです。

draft createでも作成可能ですが、内容をシンプルにしたいため今回は手動で作成します。

draft.toml
[environments]
  [environments.local]
    name = "foo-app"
    namespace = "default"
    wait = false
    watch = false
    auto-connect = false
    registry = "asia.gcr.io/{gcp_project}/test"
  • local環境へデプロイ
    • environments.localenvironments.developmentに変更すると-eオプションが不要になります。通常はenvironments.developmentを利用するほうが楽できます。
$ draft up -e local

複数環境の対応

次はGKEに構築されているstage環境にデプロイする方法を見ていきます。
GKEの場合、以下のコマンドでコンテキストファイルを作成できます。

$ gcloud container clusters get-credentials {cluster_name} --project {gcp_project} --zone {gcp_zone}

以後のコンテキストの切り替えは、以下のコマンドで実施します。

# コンテキストの一覧取得
$ kubectl config get-contexts
CURRENT   NAME                                            CLUSTER                                         AUTHINFO                                        NAMESPACE
*         docker-for-desktop                              docker-for-desktop-cluster                      docker-for-desktop
          gke_{project-name}_{zone}_{cluster-name}        gke_{project-name}_{zone}_{cluster-name}        gke_{project-name}_{zone}_{cluster-name}

# `local`環境に切り替え
$ kubectl config use-context docker-for-desktop

# `stage`環境に切り替え
$ kubectl config use-context gke_{project-name}_{zone}_{cluster-name}

スクリプト(Makefile)

複数環境への対応は、helmオプションの--kube-contextを追加するだけで対応できます。

MafileV2.patch
--- MakefileV1.mk   2018-04-09 18:41:24.000000000 +0900
+++ MakefileV2.mk   2018-04-09 18:40:50.000000000 +0900
@@ -1,9 +1,18 @@
+# Option
+#===============================================================
+ENV := local
+
 # Const
 #===============================================================
 gcp_project    := {gcp_project}
 image_repo     := asia.gcr.io/$(gcp_project)/test/foo-app
+context        := docker-for-desktop
 tag            := $(shell date +'%Y-%m-%dT%H%M%S')

+ifeq ($(ENV),stage)
+  context := gke_{gcp_project}-{gcp_zone}_{custer_name}
+endif
+
 # Task
 #===============================================================
 plain-deploy: docker-build docker-push helm-apply
@@ -11,6 +20,7 @@ plain-deploy: docker-build docker-push h
 helm-apply:
    helm upgrade --install \
      --set image.tag=$(tag) \
+     --kube-context $(context) \
      foo-app charts/app

 docker-build:

以下コマンドでstage環境にデプロイできます。

$ make ENV=stage plain-deploy

環境ごとにパラメータを変えたいは場合、さらに以下のパッチを当てます。

MafileV2-2.patch
--- MakefileV2.mk   2018-04-09 18:40:50.000000000 +0900
+++ MakefileV2-2.mk 2018-04-09 18:29:17.000000000 +0900
@@ -21,6 +21,7 @@ helm-apply:
    helm upgrade --install \
      --set image.tag=$(tag) \
      --kube-context $(context) \
+     --values charts/app/env/$(ENV).yaml \
      foo-app charts/app

 docker-build:

以下コマンドで環境ごとの設定ファイルを作成します。

# 環境用に上書きファイルを追加
$ mkdir -p charts/app/env

# localは空ファイルで作成
$ touch charts/app/env/local.yaml

# stageではreplicasを2に変更
$ echo 'replicas: 2' >> charts/app/env/stage.yaml

以下コマンドでstage用パラメーターを使い、stage環境にデプロイできます。

$ make ENV=stage plain-deploy

Skaffold

残念ながらSkaffoldは、コンテキストの指定ができないのでスクリプトで対応する必要があります。
またkubectl config use-contextでコンテキストを切り替えると、その後の操作(session)も切り替わった状態になるので注意してください。

MafileV3.patch
--- MakefileV2-2.mk 2018-04-09 18:29:17.000000000 +0900
+++ MakefileV3.mk   2018-04-09 18:30:22.000000000 +0900
@@ -15,6 +15,10 @@ endif

 # Task
 #===============================================================
+skaffold-deploy:
+   kubectl config use-context $(context)
+   skaffold run
+
 plain-deploy: docker-build docker-push helm-apply

 helm-apply:

以下コマンドでstage環境にデプロイできます。

$ make ENV=stage skaffold-deploy

環境ごとにパラメータを変えたい場合は、設定ファイルを複数用意する必要があります。

# local用にサフィクスを追加
$ mv skaffold.yaml skaffold-local.yaml

# workaround for skaffold
# 指定ありでもskaffold.yamlの空ファイルが必要
$ touch skaffold.yaml

# stage用の設定ファイルを作成
$ cat > skaffold-stage.yaml
apiVersion: skaffold/v1alpha2
kind: Config
build:
  tagPolicy:
    sha256: {}
  artifacts:
  - imageName: asia.gcr.io/{gcp_project}/test/foo-app
    workspace: .
  local: {}
deploy:
  helm:
    releases:
    - name: foo-app
      chartPath: helm
      namespace: default
      valuesFilePath: helm/env/stage.yaml
      values:
        image: asia.gcr.io/{gcp_project}/test/foo-app

# 環境用に上書きファイルを追加
$ mkdir -p helm/env

# localは空ファイルで作成
$ touch helm/env/local.yaml

# stageではreplicasを2に変更
$ echo 'replicas: 2' >> helm/env/stage.yaml

作成した設定ファイルを使用できるようにタスクも書き換えます。

MafileV3-2.patch
--- MakefileV3.mk   2018-04-09 18:30:22.000000000 +0900
+++ MakefileV3-2.mk 2018-04-09 18:32:41.000000000 +0900
@@ -8,16 +8,18 @@
 gcp_project    := {gcp_project}
 image_repo     := asia.gcr.io/$(gcp_project)/test/foo-app
 context        := docker-for-desktop
 tag            := $(shell date +'%Y-%m-%dT%H%M%S')
+skaffold_yaml  := skaffold-local.yaml

 ifeq ($(ENV),stage)
   context := gke_{gcp_project}_asia-northeast1-a_stage-cluster
+  skaffold_yaml := skaffold-stage.yaml
 endif

 # Task
 #===============================================================
 skaffold-deploy:
    kubectl config use-context $(context)
-   skaffold run
+   skaffold run -f $(skaffold_yaml)

 plain-deploy: docker-build docker-push helm-apply

以下コマンドでstage用パラメーターを使い、stage環境にデプロイできます。

$ make ENV=stage skaffold-deploy

Draft

DraftHelmと同じ開発元だけあって--kube-contextオプションを指定すればコンテキストの切り替えができます。
とはいえ、コンテキストの名前が長いので他のツールと同じくMakefileにタスクを作成します。

MafileV4.patch
--- MakefileV3-2.mk 2018-04-09 18:32:41.000000000 +0900
+++ MakefileV4.mk   2018-04-09 18:35:56.000000000 +0900
@@ -17,6 +17,9 @@ endif

 # Task
 #===============================================================
+draft-deploy:
+   draft --kube-context $(context) up --environment $(ENV)
+
 skaffold-deploy:
    kubectl config use-context $(context)
    skaffold run -f $(skaffold_yaml)

また環境ごとにパラメータを変えたい場合は、環境ごとの設定をdraft.tomlに追加します。

draft.toml
... // 追加
  [environments.stage]
    name = "foo-app"
    namespace = "default"
    wait = false
    watch = false
    auto-connect = false
    registry = "asia.gcr.io/{gcp_project}/test"
    set = ["replicas=2"]

以下コマンドでstageにデプロイできます。

$ make ENV=stage draft-deploy

タグの指定

CI/CDのパイプラインでは、Gitで発行したタグをDockerイメージのタグとして使用したいケースが多いと思います。
通常はhelmの設定ファイルや、各ツールの設定ファイルを書き換えれば実現できす。
ただしそれでは、パイプラインやワークフローが複雑になるので動的にコマンドラインから変更する方法を確認していきます。

スクリプト(Makefile)

タグの対応くらいはスクリプトでも複雑な処理は不要ですね。

MafileV5.patch
--- MakefileV4.mk   2018-04-09 18:35:56.000000000 +0900
+++ MakefileV5.mk   2018-04-09 18:36:26.000000000 +0900
@@ -1,13 +1,13 @@
 # Option
 #===============================================================
 ENV := local
+TAG :=

 # Const
 #===============================================================
 gcp_project    := {gcp_project}
 image_repo     := asia.gcr.io/$(gcp_project)/test/foo-app
 context        := docker-for-desktop
-tag            := $(shell date +'%Y-%m-%dT%H%M%S')
 skaffold_yaml  := skaffold-local.yaml

 ifeq ($(ENV),stage)
@@ -15,6 +15,12 @@ ifeq ($(ENV),stage)
   skaffold_yaml := skaffold-stage.yaml
 endif

+ifeq ($(TAG),)
+  tag := $(shell date +'%Y-%m-%dT%H%M%S')
+else
+  tag := $(TAG)
+endif
+
 # Task
 #===============================================================
 draft-deploy:

以下コマンドでタグを指定してデプロイできます。

$ make ENV=stage TAG=0.3.0 plain-deploy

Skaffold

--tagオプションを使うことで実現できます。

MafileV6.patch
--- MakefileV5.mk   2018-04-09 18:36:26.000000000 +0900
+++ MakefileV6.mk   2018-04-09 18:37:40.000000000 +0900
@@ -17,10 +17,14 @@ endif

 ifeq ($(TAG),)
   tag := $(shell date +'%Y-%m-%dT%H%M%S')
+  skaffold_option :=
 else
   tag := $(TAG)
+  skaffold_option := --tag $(tag)
 endif

+
+
 # Task
 #===============================================================
 draft-deploy:
@@ -28,7 +32,7 @@ draft-deploy:

 skaffold-deploy:
    kubectl config use-context $(context)
-   skaffold run -f $(skaffold_yaml)
+   skaffold run -f $(skaffold_yaml) $(skaffold_option)

 plain-deploy: docker-build docker-push helm-apply

以下コマンドでタグを指定してデプロイできます。

$ make ENV=stage TAG=0.3.0 skaffold-deploy

Draft

残念ながら対応していないようで、関連するIssueがありますが実装はされていないようです。

Draftは開発時のツールとして使い、本番環境へのデプロイなどは別の方法を検討したほうが良いかもしれません。

Conclusion

SkaffoldDraftを比べてみましたが、現状でも十分便利だと感じました。
ただSkaffoldはhelmのtemplate構成に制限があったりするので使用するにはWorkaroundな対応が必要です。
同様にDraftもまだstableではなく、experimentalな状態のため、(draftdの削除などの)大きな変更も入るでしょう。

それでも秘伝のスクリプトがないチームには検討の価値があると思います。

また、すでに秘伝のスクリプトがあるチームでは物足りなさがあると思うので、スクリプトと合わせて使う方法が良さそうです。

スクリプト Skaffold Draft
デプロイ
複数環境へのデプロイ
環境ごとのカスタムパラメータ
タグの指定 ×

また今回の記事にはないですが、keelも良い選択肢ですので、合わせて検討してみてください!
気が向いてたらこの記事にも追記します。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.