前回のエントリでKubernetesクラスタへSpinnakerをデプロイしました。
http://tech.virtualtech.jp/entry/2018/05/23/134107
Spinnakerのパイプラインを使用し、自動デプロイ環境を作成してみます。
筆者: コムシス情報システム株式会社 山本 聡
自動デプロイの全体図
自動デプロイ環境を行うにあたって、どういったデプロイの構造を実装するかを考えます。
- 開発環境と本番環境の2環境に対するデプロイを実施する。
- 開発環境はデプロイ対象物であるコンテナイメージの作成を検知後、自動的にデプロイする。デプロイされた環境は最新のものしか維持を行わない。
- 本番環境は開発環境での動作実績を元に人間がデプロイの可否判断する。
- デプロイが承認された場合は開発環境で動作したイメージを本番環境へデプロイする。デプロイされた環境は古いものも維持する。
前回構築したSpinnakerのトリガー設定を見ると、以下を設定できるようです。
- CRON
- Docker Registry
- Git
- Jenkins
- Travis
- Pipeline
- Pub/Sub
- Webhook
Spinnakerのパイプライン動作契機は様々ですが、動作的にわかりやすい コンテナイメージ作成(Docker Registry)からドライブさせてみたいと思います。
コンテナイメージ作成
コンテナイメージの作成はGithub、Dockerhubの機能を使用します。
上記のサービスを使用しますので、アカウントがない場合は作成を行いましょう。
Githubリポジトリの作成
Spinnakerを使用したKubernetesデプロイの動作確認を行うにあたって、実はデモ用としてのリポジトリが存在します。
https://github.com/lwander/spin-kub-demo
自身のアカウントへフォークし、リポジトリを複製します。
DockehubのAutomated Buildを設定
Dockerhub側でAutomated Buildを設定します。
https://docs.docker.com/docker-hub/builds/
ダッシュボードから以下の順でメニュを選択して設定します。
- Dockerhubとリンク Profile > Settings > Linked Accounts & Services.
- Automated Build Create > Create Automated Build
リポジトリは先程forkしたspin-kube-demoを選択します。
Build Settingsを開き、画面をスクロールしてBuild Triggers の Activate Triggersボタンを押し、生成されたTrigger Token, Trigger URL を控えます。
Webhook設定
Githubへ画面を戻し、Settings > Webhooks > Add Webhook と選択。
Payload URL, Secretへ先程控えたTrigger URL, Trigger Tokenを入力します。Update webhookを押すことで完了です。
ここまで設定すると、github上のリポジトリにcodeをpushするだけでdockerhubにてイメージのビルドが行われるようになるはずです。 content内等のhtmlをpushし、Build Detailsに変化があるか確認を行っておきましょう。
Spinnaker設定
Spinnakerは大きく分けて以下の4つを設定します。
- Applicationの作成
- LBの作成
- Clusterの作成
- Pipelineの作成
Applicationの作成
ここで言うApplicationは、クラウドプロバイダーによって管理されるリソースのグループです。kubernetes上でポッドの先頭からハイフンまで[hogehoge-]で表記される部分になります。
永続ボリュームのrook等が既にspinnakerのApplicationとして認識されているのがわかります。
Action > Create Applicationを選択し、新しいApplicationを作成し、必須項目の入力を行います。
ロードバランサの作成
次にロードバランサを作成するため、Application画面の右上にある LOAD BALANCERS を選択し、create load balancerを行います。ロードバランサでは以下の項目を設定していきます。
- 名前(スタック名)
- ロードバランサの待受ポート
- ロードバランス先の待受ポート(ポッドが通信を受け付けるポート)
ちなみにここではロードバランサの待受IPをWorker上に設定するため、NodePortで実行しています。
Createできました。
出来上がったロードバランサは、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)のロードバランサも作成しておきます。
Server groupの作成
続いてロードバランサの下に配置するServer groupを定義します。CLUSTERSタブからCreateを行いましょう。
ここでトラブルが発生。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に登録されているイメージを検索し、指定できるようになりました。
このServer groupが使用するロードバランサを指定します。
コンテナが待ち受けるポートを指定します。
Server groupが健全であるかを確認するため、Readness Probeを設定します。
Createします。Clusterが作成され、ポッドが一つできあがりました。
kubernetesを確認してみます。
$ kubectl get pod NAME READY STATUS RESTARTS AGE demo-devel-v000-srbrk 1/1 Running 0 2m
Application で指定したスタック名にてポッドが作成されています。
Pipelineの作成
ようやくパイプラインを動作させる準備が整いました。冒頭で示した構造を実現するためにSpinnakerで実装するパイプラインは次の三本です。
- 開発環境はデプロイ対象物であるコンテナイメージの作成を検知し動作する。
- 本番環境は開発環境での動作実績を元に人間がデプロイの可否判断する。
- デプロイが承認された場合は開発環境で動作したイメージを本番環境へデプロイする。
Deploy to Devel パイプライン
まずは一本目、PIPELINE から新しいパイプラインを作成します。
Spinnakerのパイプラインはステージという段階毎にそのパイプラインが実行する動作を規定していきます。
最初のステージはそのパイプラインが何を契機に動作するかを設定します。 Automated Triggers セクションにAdd Triger を行い、Typeを指定します。
コンテナイメージ更新を契機とするため、Docker Registryを選択。
どのイメージ更新を契機にするかを指定します。
次のステージの設定に移ります。
Add Stageを選択し、Type をDeployに設定、するとDeploy Configurationが現れるので、Add Server groupを行って、どのServer GroupにDepoyを行うか指定します。
予め作成しておいた demo-develを使用し、AddでServer groupを追加します。
Deploy Configuration にServer groupが設定されたことを確認します。
次のステージはDeploy後の動作を設定します。開発環境へのDeployであるため、古いServer groupはその都度削除します。
Type はDestroy Server Groupを指定、Target はPrevious Server Group とすることで、直前まで使用していたServer Groupを破棄できます。
一本目のパイプラインが完成しました。
Manual Judgement パイプライン
続いて二本目のパイプラインを作成します。
このパイプラインの動作は、別のパイプラインの完了を契機として動作させます。
Automated Triggers のTypeをPipelineに設定し、一本目のパイプラインの情報を入力します。
ステージは一つだけとし、Type を Manual Judgement に設定し、人間にパイプライン続行の判定を行わせます。
Deploy to Production パイプライン
最後に三本目のパイプラインを作成します。
このパイプラインの動作も、別のパイプラインの完了を契機として動作させます。Manual Judgementのパイプライン情報を入力しましょう。
ステージ作成に移ります。ここでTypeに注目、一本目パイプラインのようにDeployを指定するのではなくFind image from clusterというのを使用している点です。
この設定では既にDeploy済みのイメージをServer Group内から検索します。demo-develから一番新しいイメージを検索するように設定します。
次のステージでType に Deploy を指定し、前段のステージで検索したイメージを使用するようにします。
Deploy 先の Server Groupを指定します。production用のServer Groupはまだ作成していないので、demo-devel をコピーしproduction用として再設定します。
ロードバランサの指定を一旦解除し、production 用として作成したものに差し替えます。
コンテナイメージを前段のステージで検索したイメージを使うように設定します。
ClusterをAddします。
次のステージを追加し、Type に Disable Cluster を指定します。Destroy Clusterとの違いは古いServer Groupを破棄せず、Load Balancer の通信先から除外するという動き方をします。
これにより、新しくDeployしたアプリケーションに問題があった場合においても、過去動作していたServer Groupへ安全にロールバックできるという利点があります。
以上でパイプラインの設定が終わりました。実際に動かしてみましょう。
パイプラインの駆動
Githubの設定ではCode pushと同時にDockehubでイメージが作成される設定を行っていました。コマンドやWEB画面からcontets内のindex.htmlを編集し、コミットしてみましょう。
Dockerhubでイメージのビルドが始まります。
イメージのビルドが完了したタイミングで、一本目のパイプライン「Deploy to Devel」が起動し、Kubernetesへ自動的にDeployが行われます。
ちなみにDocker Tag Nameが変更されない場合は、Spinnakerのパイプラインが動作しませんでした。Code pushの前にTagを更新しておきましょう。
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となっていますが、パイプライン中に人のマークがあり、以降の処理を続けるか・止めるかを選択させる吹き出しが出現します。
Continueボタンを押すとManual Judgeパイプラインが完了し、それを受けて「Deploy to Production」が実行されます。
Server Groupの状態を見てみると、demo-productionロードバランサにポッドが稼働しているのがわかります。
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した状態を作成します。
Devel環境とは異なり、ProductionはクラスタをDisableする設定としていました。したがって、古いServer Groupを残したままの状態です。
Server Group ActionからRollbackを選択します。
一つ前のバージョンに戻します。
無事Rollbackされました。
まとめ
長くなりましたが、Spinnakerの基本的なパイプラインを動かすことができました。メリットとして感じたことは
- Deploy自動化を比較的容易に実装できる
- Clusterの状態を可視化し、サービスが健全であるかを把握できる
- PipelineによってDeployの安全性を高め、進捗を管理できる
- Rollbackも安全に実施できる
今回は実施できませんでしたが、マルチクラウド環境においてのDeploy一元化というのも魅力を感じる人もいるのではないでしょうか。
反面、導入方法が難しかったり、パイプラインの設計をきちんと考えなければいけなかったり、或いはパイプラインのメンテナンス作業が増えてしまったりという課題もあると感じます。
まだまだ開発が続いていますので、今後も継続して注視していきたいと思います。
※2018/07/31 追記 Spinnakerパイプラインデプロイの様子を動画にしました。 https://youtu.be/ejcJXjhfG2I