19
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 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」を試してみてはいかがでしょうか。

19
5
0

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
  3. You can use dark theme
What you can do with signing up
19
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?