LoginSignup
6

More than 3 years have passed since last update.

Skaffoldを利用してGKEクラスタでの高速Docker開発を試す

Last updated at Posted at 2018-12-12

GKE (Google Kubernetes Engine) とは

Kubernetes (k8sと略す) という、Dockerコンテナのオーケストレーションツールが広く使われ始めています。
k8sをGCPのマネージドサービスとして提供されているものがGKEです。

当社では、GKEを2年以上本番運用しています。

当社の技術基盤に興味がある方は、詳しくはまず下記 Tech Blog の記事を参照いただければ幸いです。
https://allabout-tech.hatenablog.com/entry/2017/12/08/080800

Skaffoldとは

CI/CDワークフローを高速に回すk8s用のツールです。
ローカルで開発したアプリケーション用コードを、k8sクラスタにデプロイするまでの、
いずれの段階においても自動化を行ってくれるものです。

今はアルファからベータ版になったところですので、本番で実用できるのはまだ先かと思います。
https://github.com/GoogleContainerTools/skaffold

Docker環境で開発を行う際にいつも問題になるのが、コードを書いてから
サーバへ成果物を反映するまでの手順が、非常に煩雑だということです。

これには、以下のような手順を踏む必要があります。

・Dockerイメージをビルドしてコンテナを作成
・コンテナイメージをDockerリポジトリにプッシュする
・リポジトリのイメージをサーバにデプロイする

Skaffoldでは、skaffold devという開発用コマンドを1つ使うだけで、
上記の3ステップが自動実行されます。

本項での紹介内容

当社は現在、CI/CDツールの見直しを検討しております。
その一環として、Skaffoldを少し触ってみました。

基本的には公式のチュートリアルを少し変えてなぞりつつ、GKEのローンチを含めて
サービス用のクラスタを想定し、当社の運用ノウハウを一部取り入れながら、
ハンズオン形式で紹介していきます。

本項では、以下のことの確認を目指します。

git cloneしてきた想定の開発リポジトリのコードを変更すると、
開発GKEクラスタにコード変更結果が反映される。

ローカル作業環境

MacまたはLinuxを想定しています。

利用できるGCPアカウントがあり、Googleログイン認証されて、
そちらが使える状態となっている必要があります。
また、GKEに関する管理者権限/GCRへの開発者権限が必要です。

ビルド時に必要ですので、Dockerをインストール/起動して下さい。
こちらは、マルチステージビルドに対応したバージョンでないといけません。

また、Google Cloud SDK(コマンドベースの管理ツール)もインストールして下さい。
https://cloud.google.com/sdk/docs/?hl=ja

こちらをインストールすることで、gcloudkubectlといった
GKE運用に必要なコマンドが利用可能になります。

ハンズオン!

安定版のSkaffoldコマンドをインストール

  • MacならHomebrewが簡単!
$ brew install skaffold
==> Installing dependencies for git: gettext and pcre2
==> Installing git dependency: gettext
==> Downloading https://homebrew.bintray.com/bottles/gettext-0.19.8.1.el_capitan.bottle.tar.gz
Updating Homebrew...
  • Linuxならバイナリを配置!
$ curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
$ chmod +x skaffold
$ sudo mv skaffold /usr/local/bin

GKEクラスタの準備

  • ローカル環境で参照するGCPプロジェクトの向け先を変えます
    → 下記では、test01-01というプロジェクトIDと、台湾リージョンのbゾーンを選択する指定を行なっています。
$ export PROJECT_ID=test01-01
$ gcloud config set project ${PROJECT_ID}
$ gcloud config set compute/zone asia-east1-b
  • コマンドでGKEを構築します
    → 下記では、標準的インスタンスでクラスタ数が2、k8sのバージョンが1.9.7のクラスタが作成されます。
      クラスタ名はskaffold-test
$ gcloud container clusters create skaffold-test \
--machine-type=n1-standard-1 --num-nodes=2 --node-version=1.9.7
  • 構築したGKEを、skaffold作業の向け先にします
$ gcloud config set container/cluster skaffold-test
$ gcloud container clusters get-credentials skaffold-test
  • 構築したノードの状態を確認
    → 名前は、ハッシュやGCP側で付加される修飾子がつくので長くなります。
$ kubectl get nodes
NAME                                           STATUS    ROLES     AGE       VERSION
gke-skaffold-test-default-pool-7cc93770-fvdx   Ready     <none>    21h       v1.9.7-gke.11
gke-skaffold-test-default-pool-7cc93770-x5bv   Ready     <none>    21h       v1.9.7-gke.11

skaffoldするためのyaml準備

  • 適当な作業ディレクトリにskaffoldをクローンして、デプロイ元を作ります
    → getting-startedというディレクトリにチュートリアルが入っているので、
      主旨に沿うように変更していきます。
$ git clone https://github.com/GoogleContainerTools/skaffold
$ cd skaffold/examples/getting-started
$ ls -l
total 40
-rw-r--r--  1 numa_numa  staff  144 12 12 19:30 Dockerfile
-rw-r--r--  1 numa_numa  staff  734 12 12 19:30 README.adoc
-rw-r--r--  1 numa_numa  staff  153 12 12 19:30 k8s-pod.yaml
-rw-r--r--  1 numa_numa  staff  128 12 12 19:30 main.go
-rw-r--r--  1 numa_numa  staff  158 12 12 19:30 skaffold.yaml

・Dockerfile
 → 変更しません、参考までに内容を記載しておきます。

FROM golang:1.10.1-alpine3.7 as builder
COPY main.go .
RUN go build -o /app main.go

FROM alpine:3.7
CMD ["./app"]
COPY --from=builder /app .

・k8s-pod.yaml
 → 消してください。単一のpod(サーバのコンテナ)をデプロイするための設定内容になっています。
   今回は当社の本番構成を模したような環境としたいので、podの冗長(クラスタ)を実装します。

・main.go
 → Global IPを付与し、ブラウザで文字列が確認できる最低限の内容に変えておきます。
   ブラウザ上にHello, World!を表示します。

main.go
package main

import (
  "fmt"
  "net/http"
)

func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":8080", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello, World!")
}

・skaffold.yaml
 → skaffoldのメインコンフィグです。GCR(Google Container Registry)をイメージの格納先とするよう、
   内容を変更しておきます。

skaffold.yaml
apiVersion: skaffold/v1beta1
kind: Config
build:
  artifacts:
  - image: asia.gcr.io/test01-01/skaffold-example
deploy:
  kubectl:
    manifests:
      - k8s-*
profiles:
  - name: gcb
    build:
      googleCloudBuild:
        projectId: test01-01

・k8s-deployment.yaml
 → 新規作成します。k8s-pod.yamlの代わりであり、クラスタ構成のWEBサーバpodを構築します。
   当社の本番運用しているyamlを、最低限まで単純化したような内容としました。
   通常、本番用のdeployment.yamlでは、podが持つべきCPU/Memのリソースなども定義します。

k8s-deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: go-pod
spec:
  replicas: 2
  revisionHistoryLimit: 5
  template:
    metadata:
      labels:
        name: go-pod
    spec:
      containers:
      - name: go-pod-container
        image: asia.gcr.io/test01-01/skaffold-example
        ports:
        - containerPort: 8080

・k8s-service.yaml
 → 新規作成します。podが利用するport情報を定義します。
   こちらも、当社の本番運用しているyamlを、最低限まで単純化したような内容としました。

k8s-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: go-service
spec:
  type: NodePort
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    name: go-pod

・k8s-ingress.yaml
 → 新規作成します。現状、当社では利用していないリソースです。
   Ingressは、Serviceに関連付けるロードバランサを自動作成し、サービス公開してくれるリソースです。
   当社ではGLB(Google Load Balancer)を使用していますが、今回はWEB画面がすぐに確認できるよう、
   最も単純なものを書きました。

k8s-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: go-ingress
spec:
    backend:
      serviceName: go-service
      servicePort: 8080

skaffold devをためす!

  • ためす前に必要な1ステップ
$ echo {} > ~/.docker/config.json

上記のコマンドを打ちましょう。
この点、かなりハマりました…

Skaffoldは標準でDockerhubの認証を行うようで、その認証設定を保持しておくのがこのファイルです。
今回はGoogleの認証を使用して、イメージのプッシュ先としてもGCR(Google Container Registry)を
使用するので、このファイルは実質不要です。

しかし、json形式と認識され、かつファイルが存在する状態でないと、skaffoldは以下のようなエラー状態に!

$ skaffold dev
2018/12/12 11:46:15 Unable to read "/Users/numa_numa/.docker/config.json": open /Users/numa_numa/.docker/config.json: no such file or directory
2018/12/12 11:46:17 Unable to read "/Users/numa_numa/.docker/config.json": open /Users/numa_numa/.docker/config.json: no such file or directory
  • skaffold dev
$ skaffold dev
Starting build...
Building [asia.gcr.io/test01-01/skaffold-example]...
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM golang:1.10.1-alpine3.7 as builder
 ---> 52d894fca6d4
Step 2/6 : COPY main.go .
 ---> 6eb6ec18c4f5
Step 3/6 : RUN go build -o /app main.go
 ---> Running in f48712f45d21
 ---> d5b140e4c533
Step 4/6 : FROM alpine:3.7
3.7: Pulling from library/alpine
c67f3896b22c: Pulling fs layer
c67f3896b22c: Verifying Checksum
c67f3896b22c: Download complete
c67f3896b22c: Pull complete
Digest: sha256:a52b4edb6240d1534d54ee488d7cf15b3778a5cfd0e4161d426c550487cddc5d
Status: Downloaded newer image for alpine:3.7
 ---> 34ea7509dcad
Step 5/6 : CMD ["./app"]
 ---> Running in 344b58b3b879
 ---> 2224d665c6f9
Step 6/6 : COPY --from=builder /app .
 ---> e48445fc4fc5
Successfully built e48445fc4fc5
Successfully tagged 559b2b15a7fb390531d89955043fdef7:latest
The push refers to repository [asia.gcr.io/test01-01/skaffold-example]
e55d98a8a253: Preparing
ebf12965380b: Preparing
ebf12965380b: Layer already exists
e55d98a8a253: Pushed
19097cfc-dirty-e48445f: digest: sha256:0381c9fb158f36984988e3bcd90d00538b323d6d7fead0d27cbe976cd1c97a9d size: 739
Build complete in 19.327532727s
Starting test...
Test complete in 6.753µs
Starting deploy...
kubectl client version: 1.10
kubectl version 1.12.0 or greater is recommended for use with skaffold
deployment.apps "go-pod" created
ingress.extensions "go-ingress" created
service "go-service" created
Deploy complete in 1.962841729s
Watching for changes every 1s...
Port Forwarding go-pod-f8f84fd5f-lqrjx 8080 -> 8080
Port Forwarding go-pod-f8f84fd5f-f7gsv 8080 -> 8080

ローカルのkubectlのバージョンが低く怒られていますが、実行は問題ないようです。

  • サービス状態の確認
    → podが生存し、サービスポートが設定され、IngressにGlobal IPが割り当てられていることを確認。
$ kubectl get pod,svc,ingress
NAME                         READY     STATUS    RESTARTS   AGE
pod/go-pod-5875ffdc6-7v54b   1/1       Running   0          4m
pod/go-pod-5875ffdc6-frpxl   1/1       Running   0          4m

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/go-service   NodePort    10.59.243.30   <none>        8080:31194/TCP   55m
service/kubernetes   ClusterIP   10.59.240.1    <none>        443/TCP          21h

NAME                            HOSTS     ADDRESS        PORTS     AGE
ingress.extensions/go-ingress   *         35.244.132.7   80        55m
  • WEB画面を見てみる
    → ブラウザで見てみます… WEB_01.png

見事、サービスデプロイが行えています!!

コードを変更してみる

getting-startedディレクトリは、gitからチェックアウトしてきたアプリケーションリポジトリを模しています。
Docker開発では、リポジトリ内にデプロイ用のマニュフェストファイル(yaml)を持ちます。

ただ、ここでのいわゆるプログラムコードはmain.goなので、こちらを変更することで本項の目的である
Docker開発が自動化されていることを確認します。

  • main.goの変更
    → 下記のように表示文字列を変えてみましょう。
      skaffold devを実行した画面とは別にターミナルを立ち上げて作業します。
main.goの変更
func handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello, World!")
}

 ↓ ↓ ↓ ↓ ↓

func handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello, All About!")
}

これを保存すると、Skaffoldはコードの変更を自動検知し下記のような画面出力が追加されます。

Starting build...
Building [asia.gcr.io/test01-01/skaffold-example]...
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM golang:1.10.1-alpine3.7 as builder
 ---> 52d894fca6d4
Step 2/6 : COPY main.go .
 ---> e3025641eb41
Step 3/6 : RUN go build -o /app main.go
 ---> Running in 4d01949d36cd
 ---> a33cf4369023
Step 4/6 : FROM alpine:3.7
 ---> 34ea7509dcad
Step 5/6 : CMD ["./app"]
 ---> Using cache
 ---> 2224d665c6f9
Step 6/6 : COPY --from=builder /app .
 ---> 89f696d81aeb
Successfully built 89f696d81aeb
Successfully tagged 24552841ff2fd2e063dbc6f46de39c89:latest
The push refers to repository [asia.gcr.io/test01-01/skaffold-example]
1264c561e5c7: Preparing
ebf12965380b: Preparing
ebf12965380b: Layer already exists
1264c561e5c7: Pushed
19097cfc-dirty-89f696d: digest: sha256:c7270a126fd8d5db4e2a383a6c2c758a7dfdb0c502dfbdf70e8f4e36d4a9e3d4 size: 739
Build complete in 15.270532362s
Starting test...
Test complete in 7.824µs
Starting deploy...
kubectl client version: 1.10
kubectl version 1.12.0 or greater is recommended for use with skaffold
deployment.apps "go-pod" configured
Deploy complete in 4.216971279s
Watching for changes every 1s...
Port Forwarding go-pod-5cf4d974bc-dj9gg 8080 -> 8080
Port Forwarding go-pod-5cf4d974bc-k4scw 8080 -> 8080
  • 再度、WEB画面を見てみる
    → コマンドで再度サービス状態を確認してから見てみます…

WEB_02.png

podへの更新デプロイが行えているようです。

このツールを使えば、開発コードへ変更を加える場合に、ビルド・プッシュ・デプロイという手順なしに、
開発内容をほぼリアルタイムに確認できそうです。

ステージングや本番へのデプロイの想定

skaffold devはあくまで開発環境の確認用ですので、[Ctrl]+[C]でプロセスを停止すると、
同時にpodは消滅してしまいます。

消滅しないようにpodデプロイを行う場合は、

$ skaffold run

を使用します。

なお、GCRにイメージプッシュを行う場合に -t オプションを付けることで任意のタグ付けも可能です。
(skaffold devで変更を繰り返した場合は、イメージにはランダムなハッシュをもつタグが付きます。)

$ skaffold run -t atonce

※GCRのWEB画面から参照したプッシュイメージ
GCR.png

おわりに

いかがでしたでしょうか。
もう一度書きますと、SkaffoldではDocker開発を回すうえでの非常に煩雑な手順、

・Dockerイメージビルド
・コンテナイメージのリポジトリプッシュ
・イメージをサーバにデプロイ

を全て自動化してくれます。

また、今回は少しハードルを上げてGKEクラスタにデプロイする例を紹介しましたが、
Minikube(ローカルで運用するk8s)やローカルのDockerでも使用することもでき、
そちらは、より単純な手順で実践可能です。

開発環境からDockerを採用する事例は、より増えてきていると思います。
みなさまもお手元の環境で、「Kubernetes + Skaffold」を試してみてはいかがでしょうか。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6