Mercari Engineering Blog

We're the software engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

次世代Continuous DeliveryプラットフォームであるSpinnakerを体験してみよう!

Mercari Advent Calendar 2017 の17日目は SET(Software Engineer in Test)チームの @masudak がお送りします。

Spinnakerの登場

みなさんの会社では、どうやってサービスのデプロイを行っていますでしょうか。手で温かみのある配布しているという方もいるかもしれませんし、scp/rsyncでデプロイ、シンボリックリンクを駆使してデプロイ、botを使ってデプロイなど、色々な方法があるでしょう。

@deeeetが tech.mercari.com

に書いたように弊社ではマイクロサービスのデプロイを少しずつSpinnakerに寄せています。

RED/BLACKデプロイや、承認フローの追加、カオスモンキーなど数多くのマイクロサービスを、共通の基盤を使ってコントロールすることができるようになります。

この記事ではSpinnakerの構築を可能な限り分かりやすく、ハンズオン形式で覚えられるようかなり平易な内容で書いてみました。 環境はGCPですが、それ以外の環境でも極力自力でできるよう必要な概念を説明するようにしています。

また本文中では、Kubernetesをk8sと省略しています。ご了承ください。

是非実際手を動かして覚えて頂き、活用した結果をコミュニティに還元して頂ければと思います。

では、行きます。

インスタンスを用意

まず、 Ubuntu 14.04 のインスタンスを一台用意してください。Dockerイメージもあるのですが、gcloudツールを別途入れたりしないといけなくて面倒なので、普通に Ubuntu 14.04 を使うことをオススメします。

その際、サービスアカウントには Allow full access to all Cloud APIs を選択してください。

もちろん、 https://www.spinnaker.io/setup/install/halyard/ に従い、Dockerイメージなども使うことは可能ですので、その場合は上記ドキュメントをご参照ください。また、Macで動かすこともできるのですが、8〜9GB求められたという噂も聞いたので、その辺もお気をつけください。

必要なコマンドのインストール

インスタンス作ったら、SSHし、以下を叩きます。

$ curl -O https://raw.githubusercontent.com/spinnaker/halyard/master/install/stable/InstallHalyard.sh

$ sudo bash InstallHalyard.sh

以下を聞かれるので、自動補完したければそのままデフォルトのとおり進めてください。

Would you like to configure halyard to use bash auto-completion? [default=Y]:

Where is your bash RC? [default=/home/USER_NAME/.bashrc]:

インストールされました。

$ hal -v
0.38.0-20171207201947

読み直します。

$ . ~/.bashrc

GCSの設定

では、Spinnaker構築していきましょう。

https://www.spinnaker.io/setup/install/environment/ にあるとおり、

  • Local installations of Debian packages.
  • Distributed installations via a remote bootstrapping process.
  • Local git installations from github.

という3つの構築方法があります。ここでは、せっかくなのでGKEを使って、 Distributed installations する方法を書いてみたいと思います。

Note: We recommend having at least 4 cores and 8 GiB of RAM free in the cluster you are deploying to.

と書かれているので、適当にクラスタを作っとおいてください。

次からは以下のドキュメントに従います。 https://www.spinnaker.io/setup/providers/kubernetes/

$ gcloud iam service-accounts create spinnaker-account \
  --display-name spinnaker-account

以下で有効にしていいかと聞かれるので、そうしましょう。

API [iam.googleapis.com] not enabled on project [YOUR_PROJECT].
Would you like to enable and retry?  (Y/n)?  y

成功したら、以下のように叩いてアカウントができたか確認しましょう。この際自分が作ったアカウントの EMAIL をメモしておいてください。

$ gcloud iam service-accounts list
NAME                                    EMAIL
spinnaker-account                       spinnaker-account@YOUR_PROJECT.iam.gserviceaccount.com

そして、そのサービスアカウントに storage.admin の権限を付与します。

$ gcloud projects add-iam-policy-binding "YOUR_PROJECT" \
  --role roles/storage.admin --member serviceAccount:"上記のEMAILアドレス"

以下のように聞かれるので、Yを押しましょう。

API [cloudresourcemanager.googleapis.com] not enabled on project
[YOUR_PROJECT]. Would you like to enable and retry?  (Y/n)?

サービスアカウントJSONを作成します。

$ gcloud iam service-accounts keys create ~/.gcp/gcs_account.json --iam-account "上記のEMAILアドレス"

Spinnakerのセットアップ

では以下のコマンドを叩いて、どのバージョンを使えるか確認しましょう。

$ hal version list
+ Get current deployment
  Success
+ Get Spinnaker version
  Success
+ Get released versions
  Success
+ You are on version "", and the following are available:
 - 1.4.2 (Dragons):
   Changelog: https://gist.github.com/spinnaker-release/c791562094c040e936776b501b42c7a6
   Published: Tue Oct 03 17:28:52 UTC 2017
   (Requires Halyard >= 0.34.0)
 - 1.5.0 (Atypical):
   Changelog: https://gist.github.com/spinnaker-release/d3d2ca93ebcc0fce546323723dee65ea
   Published: Wed Nov 08 18:52:38 UTC 2017
   (Requires Halyard >= 0.34.0)
 - 1.5.1 (Atypical):
   Changelog: https://gist.github.com/spinnaker-release/e884c78db5dead1a72c3f6b52c05738b
   Published: Thu Nov 30 21:50:14 UTC 2017
   (Requires Halyard >= 0.34.0)

最新版の 1.5.1 を使ってみたいと思います。

$ hal config version edit --version 1.5.1

以下のようなメッセージが出ますが、まだデプロイできないので、落ち着きましょう。

Deploy this version of Spinnaker with `hal deploy apply`.

パーシステントストレージの設定

https://www.spinnaker.io/setup/storage/ こちらのページにあるように、Spinnakerは保存された設定やパイプラインの情報をパーシステントストレージに保存します。

  • Azure Storage
  • Google Cloud Storage
  • Minio
  • Redis
  • S3

から選べるわけですが、ここでは無難にGCSにしましょう。

GSCの場合はドキュメントは以下になります。 https://www.spinnaker.io/setup/storage/gcs/

では、パーシステントストレージを有効にしていきます。まず設定を編集します。

$ hal config storage gcs edit --project "自分のプロジェクト名" \
  --json-path ~/.gcp/gcs_account.json

ドキュメントでは以下のように bucket-location を指定するよう書かれているのですが、

$ hal config storage gcs edit --project "自分のプロジェクト名" \
  --bucket-location "asia.gcr.io" \
  --json-path ~/.gcp/gcs_account.json

asia.gcr.io にしても、 jp にしても、

com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request

エラーが返ってきてしまいダメでした。

うまくいったら、有効化します。

$ hal config storage edit --type gcs

クラウドプロバイダーの設定

In Spinnaker, a Cloud Provider is an interface to a set of virtual resources that Spinnaker has control over. Typically, this is a IaaS provider, like AWS, or GCP, but it can also be a PaaS, like App Engine, or a container orchestrator, like Kubernetes.

https://www.spinnaker.io/setup/providers/ とあるように、クラウドプロバイダーはIaaSなどのコントロール対象の環境を抽象化したインターフェイスになります。

そのため、このクラウドプロバイダーを必要な環境に合わせて設定する必要があります。

2017/12/12時点で使えるプロバイダーは以下です。

  • App Engine
  • Amazon Web Services
  • Azure
  • DC/OS
  • Docker v2 Registry (Note: This only acts as a source of images, and does not include support for deploying Docker images)
  • Google Compute Engine
  • Kubernetes
  • Openstack
  • Oracle

今回はデプロイ先をGKEのKubernetesに、イメージのソースをDocker RegistryとしてのGCRにしてみましょう。

ちなみに、ドキュメントに

Keep in mind that every Provider can have as many accounts added as desired - this will allow you to keep your environments (e.g. staging vs. prod) separate, as well as restrict access to sets of resources using Spinnaker’s Authorization mechanisms.

とあるように、これから作るアカウントはDEV/本番を意識した上で作成してください。

https://www.spinnaker.io/setup/providers/docker-registry/#adding-an-account に書いてあるとおりですが、サービスアカウントJSONを使って、GCRに登録をしていきます。

$ hal config provider docker-registry enable

以下のようにメッセージが出ているとおり、まだアカウントがないのでこれから作ります。

- WARNING Provider dockerRegistry is enabled, but no accounts have
  been configured.

以下でアカウントを作ります。

$ hal config provider docker-registry account add masudak-gcr-account \
      --address asia.gcr.io \
      --password-file ~/.gcp/gcs_account.json \
      --username _json_key

アカウントができたことを確認しましょう。WARNINGも出てますが、気にせず進めます。

$ hal config provider docker-registry account list
+ Get current deployment
  Success
+ Get the dockerRegistry provider
  Success
Problems in default.provider.dockerRegistry.masudak-gcr-account:
- WARNING Your docker registry has no repositories specified, and
  the registry's catalog is empty. Spinnaker will not be able to deploy any images
  until some are pushed to this registry.
? Manually specify some repositories for this docker registry to
  index.

+ Accounts for dockerRegistry:
  - masudak-gcr-account

では、レジストリのアカウントもできたので、クラウドプロバイダーのアカウントも作っていきます。

まず kubectl コマンドを使えるようにします。

$ sudo curl -L https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl

$ sudo chmod 755 /usr/local/bin/kubectl
$ cat <<EOF > ~/switch_cluster.sh 
# !/bin/sh
gcloud container clusters get-credentials masudak-cluster --project=[YOUR PROJECT] --zone=asia-east1-a
EOF

$ chmod 755 ~/switch_cluster.sh 
$ ./switch_cluster.sh 

以下を叩いてコンテクストが取得できることを確認してください。

$ kubectl config current-context

k8sをプロバイダーとして有効化。

$ hal config provider kubernetes enable

k8s用のアカウントを作ります。

$ hal config provider kubernetes account add masudak-k8s-account \
    --docker-registries [上記で作った自分のGCRアカウント] \
    --context $(kubectl config current-context)

作ったアカウントをセットします。

$ hal config deploy edit \
  --account-name masudak-k8s-account \
  --type distributed

ちなみにパイプラインの処理時にSlack通知をしたい場合は、以下を設定してください。もちろんマストではないので、やりたい人だけ。

$ hal config notification slack enable

https://www.spinnaker.io/setup/features/notifications/#slack を読むと、

$ hal config notification slack edit --bot-name $SPINNAKER_BOT --token $TOKEN_FROM_SLACK

とありますが、これでは動きません(変数になっている部分は、自分の環境に合わせて書き換えてください)。

以下のようにして、 --token の引数省略します。対話式になるので、そこで初めてトークンをいれましょう。画面にはトークン表示されないですが、入力していれば進めて大丈夫です。

$ hal config notification slack edit --bot-name $SPINNAKER_BOT --token

やっとここまで来ました。では、デプロイしてみましょう。5分ちょっとかかるでしょうが、待ちましょう。

$ hal deploy apply

以下のメッセージが出れば大丈夫です。

+ Run `hal deploy connect` to connect to Spinnaker.

以下のコマンドを叩いてください。

$ hal deploy connect

以下のメッセージが出ると思います。これでSpinnakerのためにこちらからアクセスすべきポートが、ローカルのポートにマッピングされます。

Forwarding from 127.0.0.1:9000 -> 9000
Forwarding from 127.0.0.1:8084 -> 8084

とはいえ、インスタンス内のポートですので、ブラウザからアクセスするために、以下を自分のPCなりで叩いてください。

$ gcloud compute ssh [作ったインスタンス名] --project [プロジェクト名] --zone [ゾーン] --ssh-flag="-L" --ssh-flag="9000:localhost:9000" --ssh-flag="-L" --ssh-flag="8084:localhost:8084"

こうすると、自分のPCにトンネルされます。もちろん、自分でSSHトンネルを貼っても構いません。

https://www.spinnaker.io/reference/architecture/ にもありますが、9000ポートで動いている Deck と、その Deck からブラウザ経由でアクセスされる Gate の2つをともにトンネルしなければなりません。 Deck だけトンネルすると、ブラウザ経由で繋ぎに行けないので、それを忘れないようにしてください。

Macであれば、以下でリッスンできてるか確認できますね。

$ lsof -i -P | grep "LISTEN"|egrep "9000|8084"
ssh        4430 masudak    6u  IPv6 0x57758a6751d      0t0  TCP localhost:9000 (LISTEN)
ssh        4430 masudak    7u  IPv4 0x57758a677d3      0t0  TCP localhost:9000 (LISTEN)
ssh        4430 masudak    8u  IPv6 0x57758a678fc      0t0  TCP localhost:8084 (LISTEN)
ssh        4430 masudak    9u  IPv4 0x57758a678cf      0t0  TCP localhost:8084 (LISTEN)

あとは、 http://localhost:9000/ にアクセスしましょう。

ベースとなるイメージの作成

のちのち必要になるので、適当にDockerfileを持ってきて、イメージをGCRにあげておきましょう。

https://github.com/nginxinc/docker-nginx/tree/master/mainline/alpine などをローカルに持ってきて、イメージを作ります。

$ docker build -t asia.gcr.io/[プロジェクト名]/nginx:latest .

以下でイメージができたか確認しましょう。

$ docker images|grep nginx

pushしておきます。

$ gcloud docker -- push "asia.gcr.io/[プロジェクト名]/nginx:latest"

https://console.cloud.google.com/gcr にアクセスしてイメージがpushされたことを確認しておきましょう。

GUIの操作

http://localhost:9000/#/applications にアクセスして、まずアプリケーションを作成しましょう。右上の Actions から Create Application します。

必須となっている項目を埋めて、Createします。 たとえば、分かりやすくnginxでも動かしてみましょう。

http://localhost:9000/#/applications/nginx/clusters にリダイレクトされたら、次に右上の LOAD BALANCERS からLBの設定をします。

Create Load Balancer で、

  • Account: ↑で作ったk8sのアカウント
  • Namespace: とりあえずdefault
  • Stack: dev
  • Name: nginx
  • Type: LoadBalancer

でCreateしてください。 Type: LoadBalancer なので、Ingressを使わず、サービスにグローバルIPをもたせます。

次に、左上にある PIPELINES を選び、パイプラインを作ります。 Pipiline Name は適当にnginxとかにして、進みましょう。

パイプラインの設定画面になるので、まず Automated Triggers の項目で、 Add Triggers します。

  • Type: Docker Registry
  • Registry Name: masudak-gcr-account
  • Organization: YOUR PROJECT
  • Image: [YOUR PROJECT]/nginx
  • Tag: ^.*$
  • Trigger Enabled: チェック入れる

ちなみに、ここで何もImageなどが現れない場合は、以下の Google Cloud Resource Manager API が有効になっているか確認してください。 https://console.developers.google.com/apis/library/cloudresourcemanager.googleapis.com/

上述したSlackの設定をしていれば、ここでnotificationの設定をしておきましょう。

Notifications の項目から、 Slack を選び、通知したいチャンネルを入力してください。その際そのチャンネルに、対象のボットをちゃんとinviteしておいてください。

ちなみにチャンネル名の接頭辞として # は別にいれなくても大丈夫です。

そしたら、設定をSaveし、次にまた画面上部に戻り、次に Add stage をクリックして、デプロイの設定をしましょう。

Type: Deploy を選択し、 Add Server Group

  • Account: masudak-k8s-account
  • Namespace: default
  • Stack: dev
  • Container: asia.gcr.io/[YOUR PROJECT]/nginx:^.*$
  • Strategy: Red/Black
  • Deployment: チェック入れる
  • Load Balancers: nginx-dev

Containers の項目に行き、

Name: nginx にして、Addしましょう。

そして、 Save Changes します。

そしたら、 http://localhost:9000/#/applications/nginx/executions にアクセスして、右側にある Start Manual Excution してみましょう。

あとは Load Balancers の画面に戻り、 nginx-dev の項目から、 Ingress のIPを探してください。そのGIPがグローバルからアクセスできる値になります。 Cluster IP は単にクラスタ内のIPですので、そちらではないので、ご注意を。

人間による承認

ここまでで、イメージがプッシュされたら、適当なGKEクラスタにデプロイされるところまでは見てきました。最後に「人間による承認」たとえば、プロデューサーによる承認フローの追加を想定して、設定してみたいと思います。

PIPELINES の画面に行き、上記で設定したパイプラインを選び、 Configure を選択してください。

PIPELINEにはConfigurationとDeployが既に設定されていると思いますので、Deployをクリックし、 Add stage してください。 そして、以下の設定をします。

  • Type: Manual Judgement
  • Stage Name: Judgement by producer
  • Depends On: Deploy
  • Instrunctions: 「問題なければ承認お願いします」みたいななんでもいいので、分かりやすいメッセージ
  • If stage fails: halt the entire pipeline

そして、あとは Start Manual Execution なり、イメージをプッシュするなりしてください。

Configuration -> Deployとパイプラインが進み、しばらくすると Status: RUNNING のまま止まっているかと思います。

そしたら、 Details をクリックし、 Judgement by Producer をクリックしてください。

Continue するか Stop するか選べますので、 Continue をしましょう。

印象まとめ

ここまで書いた内容以外にも色々ありますが、自分が構築してみた印象として、

  • PROS
    • PIPELINEによってCDのフローを定義できるので、承認フロー含めたデプロイを一つのツールで完結できる
    • また、誰が行ったなどのログが残る
    • カオスモンキーなどの機能も使うことで、マイクロサービスの要件として必要な項目を一つのツールでテストしやすい
  • CONS
    • WebUIで設定をしないといけない。よって、設定が消えると復元が極めて困難(GCSにデータはあるので、もしかしたら何か復旧することが可能なのかもしれない)
    • ドキュメントを見ても躓くところがある、またドキュメントをまず理解するのがなかなか簡単ではない
    • k8s deploymentのバージョン管理とは別の管理システムを持つため、相性がよくない
    • k8s ingressに対応してない。今回のように Type: LoadBalancer の場合はよいが、ingressを使う場合は Type: nodeType にしつつ、別途自分でapplyしてingressを作る必要がある
    • エラーが発生した場合に、Spinnaker自体もマイクロサービスなので、慣れていないとトラブルシューティングがしにくい

と言った感じで、正直まだ人類には早いのではないかという印象もあります。

終わりに

ということで、長丁場でしたが、概念や用語も踏まえてここまで可能な限り丁寧に説明していきました。

k8sの概念のみならず、クラウドプロバイダーなどのSpinnakerの概念も出てきて大変だったかと思います。

とは言え、ここまで一つ一つやっていけば、全体像もかなり見えやすくなるでしょう。

あとは、DEV/本番の使い分けや、カオスモンキーなどの新しい機能など、色々是非試していただいて、日本の大事な情報源として、色々発表して頂ければと思います。

弊社では、マイクロサービスや自動化が好きなエンジニアを常に募集しています。優秀な人が多い上に、色々新しい技術も使え、刺激に溢れていますので、興味ある方は是非 @masudak までご連絡ください。メンションでもなんでも構いません。

明日18日目の執筆担当は @Hiraku です。引き続きお楽しみください。ではでは!