バイセル Tech Blog

バイセル Tech Blogは株式会社BuySellTechnologiesのエンジニア達が知見・発見を共有する技術ブログです。

SpinnakerをTLS(SSL)対応させる

テクノロジー開発部の村上です。

弊社で現在開発中のシステムはKubernetesを用いてマイクロサービスで構築しており、そのデプロイの一部にSpinnakerを使用しています。
Spinnakerを使用するにあたって認証を追加するので当然TLS(SSL)対応が必要となるのですが、公式サイトにはServer-terminated SSLの方法しか掲載されていなく、その方法がやや面倒です。
ですので今回は、Load Balancer-terminated SSLの方法を紹介したいと思います。

f:id:nmu0:20190611110214p:plain

Spinnakerとは

本記事に興味を持つ方にはそもそも説明不要だとは思いますが、簡単に説明させていただければと思います。
SpinnakerはNetflixによって開発された、デプロイ(ビルドも)をパイプラインで管理する、Continuous Deploymentのためのシステムとなります。
GCPやAWSなどに対応していて、Slackへの通知も設定できて、とても便利です。
公式ではbakeと呼ばれるビルド部分もSpinnakerで行うことも可能ですが、弊社では以下の流れでデプロイしています。

  1. GitHubでmasterブランチにマージ
  2. Cloud Buildでビルド
  3. Container Registryにpush
  4. pushされたことを検知してSpinnakerによってGKEクラスタにデプロイ

上記の手順でデプロイしているのは、KubernetesのDeploymentとJobです。

TSL(SSL)対応手順

Load Balancer-terminated SSLということで、Ingressを作成することになります。
また、今回はGKEでSpinnakerを構築していることを想定しています。(基本的な手順はAWSなどでも同様だと思います)

  1. Try out public Spinnaker on GKE の手順を一部変更して実施
  2. Ingressを作成

ちなみに、1のページをそのまま実行すると、LoadBalancerとしてKubernetesのServiceを作成することになります。
DeckとGateという、UI用とAPI用の2つのマイクロサービスのコンポーネント用のServiceが必要となるので、
それぞれにTSL対応をすれば、Server-terminated SSLが実現出来ます。

事前準備

まず、手順1と2のために以下の2つを事前準備として実行しておきます

  1. Load Balancer用のstatic ipを作成する
  2. ドメインを取得しDNSサーバーを設定する
  3. Deck用とGate用に、1で作成したIPアドレスを指す用にDNSサーバーのAレコードをそれぞれ作成する

Aレコードは、"Try out public Spinnaker on GKE"では、それぞれspinnaker.$DOMAINspinnaker.$DOMAINとなっています。
($DOMAINは取得したドメインかそのサブドメイン)

1. Try out public Spinnaker on GKEの手順を一部変更して実施

公式ページからの変更点は以下の2点です。

  1. ServiceのtypeをNodePortにする
  2. httpでURLを設定している部分をhttpsに変更する
  3. Serviceのポート番号を変更しない

2と3は文字通りなので、1について説明させて頂きます。
NodePortにするとだけ聞くと単純なのですが、少し工夫が必要となります。
その工夫とは、

  1. 手動でServiceのマニフェストを変更しないで済むように、設定ファイルをローカルに配置する
    • ~/.hal/default/service-settings/deck.yml~/.hal/default/service-settings/gate.yml
  2. Load Balancer作成時のhealth checkに通るように、1の設定ファイルでhealth checkの種類を変更する

となります。
1については、指定のYAMLファイルにkubernetes.serviceType: NodePortと書けばSpinnakerへの設定反映時にServiceがNodePortになります。(デフォルトはCluterIP)
2については、私もLoadBalancer作成後にhealth checkが通らずにエラーが出ていることで気がついたのですが、デフォルトではhttpによるhealth checkになっていません。
公式ページのuseExecHealthCheckの項に書かれているのですが、
Istioとの互換性向上のために、デフォルトではexecベースのhealth checkを行うようになっているようです。
よって、kubernetes.useExecHealthCheck: falseをYAMLファイルに記述すれば大丈夫です。
まとめると以下のようなYAMLファイルをdeck.ymlとgate.ymlとして作成することになります。

kubernetes:
  serviceType: NodePort
  useExecHealthCheck: false

2. Ingressを作成

以下のYAMLを使用して、Ingressを作成します。
このYAMLでは、spinnaker-ingress-certというKubernetesのsecretに証明書と秘密鍵を保管しています。
今回私はCloudflareで証明書と秘密鍵を作成し(それぞれcert.pemとkey.pemとする)、以下のコマンドでsecretを作成しました。

kubectl create -n spinnaker secret tls spinnaker-ingress-cert --key key.pem --cert cert.pem

それとは別にGoogleのmanaged certを使用する方法もあり、その場合はtls部分を消して 、annotationsにingress.gcp.kubernetes.io/pre-shared-cert: "GOOGLE_MANAGED_CERT"を足すこととなります。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: spinnaker-ingress
  namespace: spinnaker
  annotations:
    kubernetes.io/ingress.global-static-ip-name: <事前に作成したStatic IPの名前>
    kubernetes.io/ingress.allow-http: "false"
spec:
  tls:
  - secretName: spinnaker-ingress-cert
  rules:
  - host: <UI用のドメイン名>
    http:
      paths:
        - backend:
            serviceName: spin-deck
            servicePort: 9000
  - host: <API用のドメイン名>
    http:
      paths:
        - backend:
            serviceName: spin-gate
            servicePort: 8084

まとめ

いかがでしたでしょうか。自分自身で設定する際に、あまりウェブ上にも参考資料がなかったので今回紹介させて頂きました。
弊社では上記に加えてIP制限も足すことで、セキュアにSpinnakerを運用しています。
まだ開発中のシステムで使用しているだけなので自動デプロイの恩恵しか受けていませんが、今後はManual Judgeパイプライン等も活用していけたらと思っているので、その際はまた何か紹介出来ればと思います。

参考資料