仮想化通信

日本仮想化技術株式会社の公式エンジニアブログ

Spinnakerのパイプラインによる自動デプロイ

前回のエントリでKubernetesクラスタへSpinnakerをデプロイしました。

http://tech.virtualtech.jp/entry/2018/05/23/134107

Spinnakerのパイプラインを使用し、自動デプロイ環境を作成してみます。

筆者: コムシス情報システム株式会社 山本 聡

自動デプロイの全体図

自動デプロイ環境を行うにあたって、どういったデプロイの構造を実装するかを考えます。

  1. 開発環境と本番環境の2環境に対するデプロイを実施する。
  2. 開発環境はデプロイ対象物であるコンテナイメージの作成を検知後、自動的にデプロイする。デプロイされた環境は最新のものしか維持を行わない。
  3. 本番環境は開発環境での動作実績を元に人間がデプロイの可否判断する。
  4. デプロイが承認された場合は開発環境で動作したイメージを本番環境へデプロイする。デプロイされた環境は古いものも維持する。

f:id:virtualtech:20180625161637j:plain

前回構築したSpinnakerのトリガー設定を見ると、以下を設定できるようです。

  • CRON
  • Docker Registry
  • Git
  • Jenkins
  • Travis
  • Pipeline
  • Pub/Sub
  • Webhook

Spinnakerのパイプライン動作契機は様々ですが、動作的にわかりやすい コンテナイメージ作成(Docker Registry)からドライブさせてみたいと思います。

コンテナイメージ作成

コンテナイメージの作成はGithub、Dockerhubの機能を使用します。

https://github.com/

https://hub.docker.com/

上記のサービスを使用しますので、アカウントがない場合は作成を行いましょう。

Githubリポジトリの作成

Spinnakerを使用したKubernetesデプロイの動作確認を行うにあたって、実はデモ用としてのリポジトリが存在します。

https://github.com/lwander/spin-kub-demo

自身のアカウントへフォークし、リポジトリを複製します。

f:id:virtualtech:20180625161756p:plain

DockehubのAutomated Buildを設定

Dockerhub側でAutomated Buildを設定します。

https://docs.docker.com/docker-hub/builds/

ダッシュボードから以下の順でメニュを選択して設定します。

  1. Dockerhubとリンク  Profile > Settings > Linked Accounts & Services.
  2. Automated Build  Create > Create Automated Build

リポジトリは先程forkしたspin-kube-demoを選択します。

f:id:virtualtech:20180625161835p:plain

Build Settingsを開き、画面をスクロールしてBuild Triggers の Activate Triggersボタンを押し、生成されたTrigger Token, Trigger URL を控えます。

f:id:virtualtech:20180625161941p:plain

Webhook設定

Githubへ画面を戻し、Settings > Webhooks > Add Webhook と選択。

f:id:virtualtech:20180625162005p:plain

Payload URL, Secretへ先程控えたTrigger URL, Trigger Tokenを入力します。Update webhookを押すことで完了です。

f:id:virtualtech:20180625162023p:plain

ここまで設定すると、github上のリポジトリにcodeをpushするだけでdockerhubにてイメージのビルドが行われるようになるはずです。 content内等のhtmlをpushし、Build Detailsに変化があるか確認を行っておきましょう。

f:id:virtualtech:20180625162047p:plain

Spinnaker設定

Spinnakerは大きく分けて以下の4つを設定します。

  1. Applicationの作成
  2. LBの作成
  3. Clusterの作成
  4. Pipelineの作成

Applicationの作成

ここで言うApplicationは、クラウドプロバイダーによって管理されるリソースのグループです。kubernetes上でポッドの先頭からハイフンまで[hogehoge-]で表記される部分になります。

永続ボリュームのrook等が既にspinnakerのApplicationとして認識されているのがわかります。

f:id:virtualtech:20180625162110p:plain

Action > Create Applicationを選択し、新しいApplicationを作成し、必須項目の入力を行います。

f:id:virtualtech:20180625162126p:plain

ロードバランサの作成

次にロードバランサを作成するため、Application画面の右上にある LOAD BALANCERS を選択し、create load balancerを行います。ロードバランサでは以下の項目を設定していきます。

  • 名前(スタック名)
  • ロードバランサの待受ポート
  • ロードバランス先の待受ポート(ポッドが通信を受け付けるポート)

f:id:virtualtech:20180625162145p:plain

ちなみにここではロードバランサの待受IPをWorker上に設定するため、NodePortで実行しています。

f:id:virtualtech:20180625162203p:plain

Createできました。

f:id:virtualtech:20180625162219p:plain

出来上がったロードバランサは、Kubernetesでserviceとして稼働します。

$ kubectl get service
NAME                                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                       AGE
demo-devel                            NodePort    10.152.183.109   <none>        80:30017/TCP                  3m

先程用意したのは開発環境用(debel)のロードバランサでしたので、同様の要領で本番環境用(production)のロードバランサも作成しておきます。

f:id:virtualtech:20180625162238p:plain

Server groupの作成

続いてロードバランサの下に配置するServer groupを定義します。CLUSTERSタブからCreateを行いましょう。

f:id:virtualtech:20180625162304p:plain

f:id:virtualtech:20180625162316p:plain

ここでトラブルが発生。Containersにてデプロイするコンテナイメージを指定するのですが、特定のイメージしか指定できません。調べたところ、helmチャートに同梱されているvalues.yamlに対してリポジトリを予め定義してspinnakerをデプロイしなければいけないようでした。

helm fetch を使用してhelmチャートファイルをダウンロードします。

$ helm fetch stable/spinnaker

spinnaker-0.4.1.tgz(バージョンは随時更新されます)がダウンロードされたので、これを解凍しvalues.yamlを編集します。

$ tar zxvf spinnaker-0.4.1.tgz

$ vim ./spinnaker-0.4.1/values.yaml
# Define which registries and repositories you want available in your
# Spinnaker pipeline definitions
# For more info visit:
#   https://www.spinnaker.io/setup/providers/docker-registry/

# Configure your Docker registries here
accounts:
- name: dockerhub
  address: https://index.docker.io
  repositories:
    - library/alpine
    - library/ubuntu
    - library/centos
    - library/nginx
    - yamamotosatoru/spin-kub-demo
# - name: gcr
...

参照するDockehubのリポジトリを書き足しました。 values.yamlを編集後、spinnakerを再デプロイします。 再デプロイ時には先程ダウンロードし、values.yamlを編集したチャートを指定することに注意。

$ helm list
NAME            REVISION    UPDATED                     STATUS      CHART           NAMESPACE
wishing-camel   1           Tue May 15 17:14:22 2018    DEPLOYED    spinnaker-0.4.1 default  

$ helm delete wishing-camel

$ helm install ./spinnaker-0.4.1

再デプロイ後、Dockerhubに登録されているイメージを検索し、指定できるようになりました。

f:id:virtualtech:20180625162429p:plain

このServer groupが使用するロードバランサを指定します。

f:id:virtualtech:20180625162445p:plain

コンテナが待ち受けるポートを指定します。

f:id:virtualtech:20180625162505p:plain

Server groupが健全であるかを確認するため、Readness Probeを設定します。

f:id:virtualtech:20180625162529p:plain

Createします。Clusterが作成され、ポッドが一つできあがりました。

f:id:virtualtech:20180625162546p:plain

kubernetesを確認してみます。

$ kubectl get pod
NAME                                                   READY     STATUS      RESTARTS   AGE
demo-devel-v000-srbrk                                  1/1       Running     0          2m

Application で指定したスタック名にてポッドが作成されています。

Pipelineの作成

ようやくパイプラインを動作させる準備が整いました。冒頭で示した構造を実現するためにSpinnakerで実装するパイプラインは次の三本です。

f:id:virtualtech:20180625180436j:plain

  1. 開発環境はデプロイ対象物であるコンテナイメージの作成を検知し動作する。
  2. 本番環境は開発環境での動作実績を元に人間がデプロイの可否判断する。
  3. デプロイが承認された場合は開発環境で動作したイメージを本番環境へデプロイする。

Deploy to Devel パイプライン

まずは一本目、PIPELINE から新しいパイプラインを作成します。

f:id:virtualtech:20180625162615p:plain

f:id:virtualtech:20180625162624p:plain

Spinnakerのパイプラインはステージという段階毎にそのパイプラインが実行する動作を規定していきます。

最初のステージはそのパイプラインが何を契機に動作するかを設定します。 Automated Triggers セクションにAdd Triger を行い、Typeを指定します。

f:id:virtualtech:20180625162645p:plain

コンテナイメージ更新を契機とするため、Docker Registryを選択。

f:id:virtualtech:20180625162713p:plain

どのイメージ更新を契機にするかを指定します。

f:id:virtualtech:20180625162733p:plain

次のステージの設定に移ります。

Add Stageを選択し、Type をDeployに設定、するとDeploy Configurationが現れるので、Add Server groupを行って、どのServer GroupにDepoyを行うか指定します。

f:id:virtualtech:20180625162752p:plain

予め作成しておいた demo-develを使用し、AddでServer groupを追加します。

f:id:virtualtech:20180625162809p:plain

f:id:virtualtech:20180625162819p:plain

f:id:virtualtech:20180625162832p:plain

f:id:virtualtech:20180625162843p:plain

f:id:virtualtech:20180625162856p:plain

Deploy Configuration にServer groupが設定されたことを確認します。

f:id:virtualtech:20180625162943p:plain

次のステージはDeploy後の動作を設定します。開発環境へのDeployであるため、古いServer groupはその都度削除します。

Type はDestroy Server Groupを指定、Target はPrevious Server Group とすることで、直前まで使用していたServer Groupを破棄できます。

f:id:virtualtech:20180625163005p:plain

一本目のパイプラインが完成しました。

f:id:virtualtech:20180625163025p:plain

Manual Judgement パイプライン

続いて二本目のパイプラインを作成します。

f:id:virtualtech:20180625163048p:plain

このパイプラインの動作は、別のパイプラインの完了を契機として動作させます。

Automated Triggers のTypeをPipelineに設定し、一本目のパイプラインの情報を入力します。

f:id:virtualtech:20180625163110p:plain

ステージは一つだけとし、Type を Manual Judgement に設定し、人間にパイプライン続行の判定を行わせます。

f:id:virtualtech:20180625163128p:plain

Deploy to Production パイプライン

最後に三本目のパイプラインを作成します。

f:id:virtualtech:20180625163202p:plain

このパイプラインの動作も、別のパイプラインの完了を契機として動作させます。Manual Judgementのパイプライン情報を入力しましょう。

f:id:virtualtech:20180625163224p:plain

ステージ作成に移ります。ここでTypeに注目、一本目パイプラインのようにDeployを指定するのではなくFind image from clusterというのを使用している点です。

この設定では既にDeploy済みのイメージをServer Group内から検索します。demo-develから一番新しいイメージを検索するように設定します。

f:id:virtualtech:20180625163243p:plain

次のステージでType に Deploy を指定し、前段のステージで検索したイメージを使用するようにします。

f:id:virtualtech:20180625163302p:plain

Deploy 先の Server Groupを指定します。production用のServer Groupはまだ作成していないので、demo-devel をコピーしproduction用として再設定します。

f:id:virtualtech:20180625163329p:plain

f:id:virtualtech:20180625163342p:plain

f:id:virtualtech:20180625163404p:plain

ロードバランサの指定を一旦解除し、production 用として作成したものに差し替えます。

f:id:virtualtech:20180625163420p:plain

f:id:virtualtech:20180625163431p:plain

コンテナイメージを前段のステージで検索したイメージを使うように設定します。

f:id:virtualtech:20180625163452p:plain

ClusterをAddします。

f:id:virtualtech:20180625163503p:plain

次のステージを追加し、Type に Disable Cluster を指定します。Destroy Clusterとの違いは古いServer Groupを破棄せず、Load Balancer の通信先から除外するという動き方をします。

これにより、新しくDeployしたアプリケーションに問題があった場合においても、過去動作していたServer Groupへ安全にロールバックできるという利点があります。

f:id:virtualtech:20180625163539p:plain

以上でパイプラインの設定が終わりました。実際に動かしてみましょう。

パイプラインの駆動

Githubの設定ではCode pushと同時にDockehubでイメージが作成される設定を行っていました。コマンドやWEB画面からcontets内のindex.htmlを編集し、コミットしてみましょう。

Dockerhubでイメージのビルドが始まります。

f:id:virtualtech:20180625163629p:plain

イメージのビルドが完了したタイミングで、一本目のパイプライン「Deploy to Devel」が起動し、Kubernetesへ自動的にDeployが行われます。

ちなみにDocker Tag Nameが変更されない場合は、Spinnakerのパイプラインが動作しませんでした。Code pushの前にTagを更新しておきましょう。

f:id:virtualtech:20180625163755p:plain

Kubernetesでデプロイ状態を確認してみます。

$ kubectl get pod
NAME                                                   READY     STATUS      RESTARTS   AGE
demo-devel-v002-xb5vd                                  1/1       Running     0          1m

ポッドの詳細を表示します。

$ kubectl describe pod demo-devel-v002-xb5vd
Name:           demo-devel-v002-xb5vd
Namespace:      default
Node:           node03/172.17.30.200
Start Time:     Fri, 22 Jun 2018 10:33:29 +0900
Labels:         app=demo
                cluster=demo-devel
                demo-devel-v002=true
                load-balancer-demo-devel=true
                replication-controller=demo-devel-v002
                stack=devel
                version=2
Annotations:    <none>
Status:         Running
IP:             10.1.69.204
Controlled By:  ReplicaSet/demo-devel-v002
Containers:
  yamamotosatoru-spin-kub-demo:
    Container ID:   docker://c3172c71ebc96e50ae6c93c37e49377dc14fdf65eaac516500e952750b820f67
    Image:          index.docker.io/yamamotosatoru/spin-kub-demo:v0.0.11-33
    Image ID:       docker-pullable://yamamotosatoru/spin-kub-demo@sha256:bb728f4c0dbc952eb44000765abd70fda7ea3eab19d8f57eb393810af10521e1
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 22 Jun 2018 10:33:47 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-kjlr5 (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:
  default-token-kjlr5:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-kjlr5
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason                 Age   From               Message
  ----    ------                 ----  ----               -------
  Normal  Scheduled              3m    default-scheduler  Successfully assigned demo-devel-v002-xb5vd to node03
  Normal  SuccessfulMountVolume  3m    kubelet, node03    MountVolume.SetUp succeeded for volume "default-token-kjlr5"
  Normal  Pulling                2m    kubelet, node03    pulling image "index.docker.io/yamamotosatoru/spin-kub-demo:v0.0.11-33"
  Normal  Pulled                 2m    kubelet, node03    Successfully pulled image "index.docker.io/yamamotosatoru/spin-kub-demo:v0.0.11-33"
  Normal  Created                2m    kubelet, node03    Created container
  Normal  Started                2m    kubelet, node03    Started container

Dockerhubでビルド完了したイメージがちゃんとデプロイされていました。

また、Deploy to Develパイプライン完了と連動して「Manual Judge」パイプラインが動作しています。StatusがRunningとなっていますが、パイプライン中に人のマークがあり、以降の処理を続けるか・止めるかを選択させる吹き出しが出現します。

f:id:virtualtech:20180625163823p:plain

Continueボタンを押すとManual Judgeパイプラインが完了し、それを受けて「Deploy to Production」が実行されます。

f:id:virtualtech:20180625163843p:plain

Server Groupの状態を見てみると、demo-productionロードバランサにポッドが稼働しているのがわかります。

f:id:virtualtech:20180625163900p:plain

kubernetesからも確認してみましょう。

$ kubectl get pod
NAME                                                   READY     STATUS      RESTARTS   AGE
demo-devel-v002-xb5vd                                  1/1       Running     0          6m
demo-production-v000-82bl9                             1/1       Running     0          1m
$ kubectl describe pod demo-production-v000-82bl9
Name:           demo-production-v000-82bl9
Namespace:      default
Node:           node02/172.17.30.224
Start Time:     Fri, 22 Jun 2018 10:38:57 +0900
Labels:         app=demo
                cluster=demo-production
                demo-production-v000=true
                load-balancer-demo-production=true
                replication-controller=demo-production-v000
                stack=production
                version=0
Annotations:    <none>
Status:         Running
IP:             10.1.84.222
Controlled By:  ReplicaSet/demo-production-v000
Containers:
  yamamotosatoru-spin-kub-demo:
    Container ID:   docker://e3df6eb0783829040e9fd1a5702df85a89240b632c7a3a106c9e34a790bb9315
    Image:          index.docker.io/yamamotosatoru/spin-kub-demo:v0.0.11-33
    Image ID:       docker-pullable://yamamotosatoru/spin-kub-demo@sha256:bb728f4c0dbc952eb44000765abd70fda7ea3eab19d8f57eb393810af10521e1
    Port:           8000/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 22 Jun 2018 10:39:08 +0900
    Ready:          True
    Restart Count:  0
    Readiness:      http-get http://:8000/ delay=0s timeout=1s period=10s #success=1 #failure=3
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-kjlr5 (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:
  default-token-kjlr5:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-kjlr5
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason                 Age   From               Message
  ----    ------                 ----  ----               -------
  Normal  Scheduled              2m    default-scheduler  Successfully assigned demo-production-v000-82bl9 to node02
  Normal  SuccessfulMountVolume  2m    kubelet, node02    MountVolume.SetUp succeeded for volume "default-token-kjlr5"
  Normal  Pulled                 1m    kubelet, node02    Container image "index.docker.io/yamamotosatoru/spin-kub-demo:v0.0.11-33" already present on machine
  Normal  Created                1m    kubelet, node02    Created container
  Normal  Started                1m    kubelet, node02    Started container

開発環境でDeployしたのと同じイメージで起動しています。

Rollback

最後にApplicationのRollbackを行ってみます。

再度パイプラインを動作させ、Production環境へ新しいバージョンのイメージをDeployした状態を作成します。

f:id:virtualtech:20180625163935p:plain

Devel環境とは異なり、ProductionはクラスタをDisableする設定としていました。したがって、古いServer Groupを残したままの状態です。

f:id:virtualtech:20180625163951p:plain

Server Group ActionからRollbackを選択します。

f:id:virtualtech:20180625164013p:plain

一つ前のバージョンに戻します。

f:id:virtualtech:20180625164031p:plain

無事Rollbackされました。

f:id:virtualtech:20180625164047p:plain

まとめ

長くなりましたが、Spinnakerの基本的なパイプラインを動かすことができました。メリットとして感じたことは

  • Deploy自動化を比較的容易に実装できる
  • Clusterの状態を可視化し、サービスが健全であるかを把握できる
  • PipelineによってDeployの安全性を高め、進捗を管理できる
  • Rollbackも安全に実施できる

今回は実施できませんでしたが、マルチクラウド環境においてのDeploy一元化というのも魅力を感じる人もいるのではないでしょうか。

反面、導入方法が難しかったり、パイプラインの設計をきちんと考えなければいけなかったり、或いはパイプラインのメンテナンス作業が増えてしまったりという課題もあると感じます。

まだまだ開発が続いていますので、今後も継続して注視していきたいと思います。

※2018/07/31 追記 Spinnakerパイプラインデプロイの様子を動画にしました。 https://youtu.be/ejcJXjhfG2I