EKSにデプロイするためのSpinnakerセットアップ

# 構成
- デプロイターゲットとなるKubernetesクラスタはEKSで稼働している
- 上記のクラスタとは別のKubernetesクラスタでSpinnakerを稼働させる
- halコマンドは専用のEC2上で実行する(後述)

<img width="287" alt="spinnaker_on_eks.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269592/340daaa8-6718-a16e-dfbf-d01797576046.png">


# 前提条件
- 必要なKubernetesクラスタが作成済み

# halコマンド実行環境作成
## halコマンドとは
- halはHalyardをインストールすることで利用できるコマンド
- halconfigがSpinnakerの構成を管理しており、halコマンドによってhalconfigの内容をコントロールできる

## 環境作成について
- 複数人でSpinnakerを運用することを見据えてローカルPCではなく専用のEC2で環境を作る
- AMIはUbuntu16.04を選択
    - Amazon LinuxとUbuntu18.04だとHalyardのセットアップに失敗
    - そもそも[公式ドキュメント](https://www.spinnaker.io/setup/install/halyard/)でもUbuntu18.04はサポートされていない


## Halyardのインストール
- インストールスクリプトからダウンロード

```
$ curl -O https://raw.githubusercontent.com/spinnaker/halyard/master/install/debian/InstallHalyard.sh
$ sudo bash InstallHalyard.sh
$ hal -v
1.23.2-20190904152725
```

## AWS CLIのインストール(詳細触れません)
### pipのインストール

```
$ curl -O https://bootstrap.pypa.io/get-pip.py
$ python3 get-pip.py --user
$ export PATH=~/.local/bin:$PATH
$ pip3 --version
pip 19.2.3 from /home/ubuntu/.local/lib/python3.5/site-packages/pip (python 3.5)
```

### pipを使用してAWS CLIをインストール

```
$ pip3 install awscli --upgrade --user
$ aws --version
aws-cli/1.16.235 Python/3.5.2 Linux/4.4.0-1087-aws botocore/1.12.225
```

## kubectlのインストール

- バイナリをからダウンロード
- configの設定をまだしていないのでバージョン確認時のエラーは問題なし

```
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubectl
$ chmod +x ./kubectl
$ sudo mv ./kubectl /usr/local/bin/kubectl
Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.0", GitCommit:"ddf47ac13c1a9483ea035a79cd7c10005ff21a6d", GitTreeState:"clean", BuildDate:"2018-12-03T21:04:45Z", GoVersion:"go1.11.2", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?
```

# Kubernetes周りの設定
## Spinnaker稼働クラスタを操作するためのkubeconfigを作成
### IAM RoleをEKSクラスターに追加
- Spinnakerを稼働させるKubernetesクラスタのconfigmapの更新が必要
- halコマンド実行環境にアタッチされているIAM RoleをmapRolesに記載する
    - 下の例ではspinnakerというIAM Role名を追記している
    - groupをsystem:mastersにしてしまって良いのかは疑問です
- 以下のコマンドは、**Spinnakerが稼働しているKubernetesクラスタを操作できる環境から実行する**

```
$ kubectl edit -n kube-system configmap/aws-auth
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::XXXXXXXXXXX:role/eksctl-spinnaker-nodegroup-NodeInstanceRole-O5RXNXSZULZG
      username: system:node:{{EC2PrivateDNSName}}
    - groups:
      - system:masters
      rolearn: arn:aws:iam::XXXXXXXXXXX:role/spinnaker
      username: spinnaker
kind: ConfigMap
metadata:
  creationTimestamp: "2019-09-10T01:19:12Z"
  name: aws-auth
  namespace: kube-system
  resourceVersion: "4530"
  selfLink: /api/v1/namespaces/kube-system/configmaps/aws-auth
  uid: ff4b532a-d368-11e9-b497-0246cd314c16
```
### kubeconfig作成
- halコマンド実行環境のkubeconfigを作成

```
$ aws eks --region ap-southeast-1 update-kubeconfig --name spinnaker
Added new context arn:aws:eks:ap-southeast-1:XXXXXXXXXXX:cluster/spinnaker to /home/ubuntu/.kube/config
```

- kubectlコマンドが打てればOK

```
$ kubectl get pods --all-namespaces
NAMESPACE     NAME                      READY   STATUS    RESTARTS   AGE
kube-system   aws-node-jtwww            1/1     Running   0          50m
kube-system   coredns-84d599b4b-7tv7n   1/1     Running   0          63m
kube-system   coredns-84d599b4b-g6tmh   1/1     Running   0          63m
kube-system   kube-proxy-2tm2g          1/1     Running   0          50m
```
## ServiceAccountの作成
- 以下のコマンドを実行
- 内容は以下の通り
    - Spinnakerのコンポーネントがデプロイされるnamespaceを作成
    - Spinnakerが利用するServiceAccountを作成
    - クラスタロールバインディングによってcluster-adminの権限を付与
    - 作成したServiceAccountをkubeconfigに設定する

```
$ kubectl create namespace spinnaker
namespace/spinnaker created
$ kubectl create serviceaccount spinnaker-sa -n spinnaker
serviceaccount/spinnaker-sa create
$ kubectl create --save-config clusterrolebinding spinnaker-rb --clusterrole=cluster-admin --serviceaccount=spinnaker:spinnaker-sa
clusterrolebinding.rbac.authorization.k8s.io/spinnaker-rb created
```

## ServiceAccountの登録
- Kuberentesクラスタ名はデフォルトではEKSのarnを指定する必要がある

```
$ SPINNAKER_SECRET=$(kubectl get sa/spinnaker-sa -n spinnaker -o jsonpath='{.secrets[0].name}')
$ SPINNAKER_SA_TOKEN=$(kubectl get secrets -n spinnaker ${SPINNAKER_SECRET} -o jsonpath='{.data.token}' | base64 --decode)
$ kubectl config set-credentials spinnaker --token ${SPINNAKER_SA_TOKEN}
User "spinnaker" set.
$ kubectl config set-context spinnaker-admin --namespace spinnaker --user spinnaker --cluster [Kuberentesクラスタ名]
Context "spinnaker-admin" created.
$ kubectl config use-context spinnaker-admin
Switched to context "spinnaker-admin".
```

# Spinnakerの設定
## Kubernetesの有効化
- Spinnakerではデプロイするクラウドに応じてプロバイダを指定し、認証情報を設定する必要がある


```
$ hal config provider kubernetes enable
+ Get current deployment
  Success
+ Edit the kubernetes provider
  Success
Problems in default.provider.kubernetes:
- WARNING Provider kubernetes is enabled, but no accounts have been
  configured.

+ Successfully enabled kubernetes
```

## アカウントの追加
- SpinnakerのアカウントをKubernetesのサービスアカウントと関連付けることでKubernetes上にリソースを展開することができる
- ここでは、spinnakerというアカウントを、先ほど作成したContextと紐付けて定義

```
$ hal config provider kubernetes account add spinnaker --provider-version v2 --context $(kubectl config current-context)
+ Get current deployment
  Success
+ Add the spinnaker account
  Success
+ Successfully added account spinnaker for provider kubernetes.
```

## Spinnakerをデプロイするクラスタを指定
- 作成したアカウントSpinnakerを指定する

```
$ hal config deploy edit --type distributed --account-name spinnaker
```

## Artifactsを有効化
- KubernetesではマニフェストがArtifactsとして扱われる
- これはSpinnakerのパイプライン中で利用できる
- 今回はS3から取得したいのでS3を有効にする
- S3にアクセスするためにクレデンシャルが必要になるので、ユーザーを登録する

```
$ hal config features edit --artifacts true
+ Get current deployment
  Success
+ Get features
  Success
+ Edit features
  Success
+ Successfully updated features.
$ hal config artifact s3 enable 
+ Get current deployment
  Success
+ Edit the s3 provider
  Success
+ Successfully enabled s3
+ $ hal config artifact s3 account add spinnaker-s3 --aws-access-key-id XXXXXXXXXXXXXX  --aws-secret-access-key --region ap-northeast-1
Your AWS Secret Key.:
+ Get current deployment
  Success
+ Add the spinnaker-s3 artifact account
  Success
+ Successfully added artifact account spinnaker-s3 for artifact
  provider s3.
```



## 永続データストレージとしてS3を設定
- 2019年12月25日追記
    - Spinnakerが稼働しているクラスタのノードにS3へのアクセス権が必要になる
    - IAM Userのクレデンシャルを指定することも可。その場合は```hal config storage s3 edit ```コマンドにオプションを指定することで実現できる。

```
$ hal config storage s3 edit --region ap-northeast-1 --bucket [バケット名]
+ Get current deployment
  Success
+ Get persistent store
  Success
+ Edit persistent store
  Success
Problems in default.persistentStorage:
- WARNING Your deployment will most likely fail until you configure
  and enable a persistent store.

+ Successfully edited persistent store "s3".
$ hal config storage edit --type s3
+ Get current deployment
  Success
+ Get persistent storage settings
  Success
+ Edit persistent storage settings
  Success
+ Successfully edited persistent storage.
```

## タイムゾーンの設定

```
$ hal config edit --timezone Asia/Tokyo
+ Get current deployment
  Success
+ Get deployment configuration
  Success
+ Edit deployment configuration
  Success
Problems in default.security:
- WARNING Your UI or API domain does not have override base URLs
  set even though your Spinnaker deployment is a Distributed deployment on a
  remote cloud provider. As a result, you will need to open SSH tunnels against
  that deployment to access Spinnaker.
? We recommend that you instead configure an authentication
  mechanism (OAuth2, SAML2, or x509) to make it easier to access Spinnaker
  securely, and then register the intended Domain and IP addresses that your
  publicly facing services will be using.

+ Successfully edited your deployment configuration
```

# 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.13.12 (BirdBox):
   Changelog: https://gist.github.com/spinnaker-release/9ee98b0cbed65e334cd498bc31676295
   Published: Mon Jul 29 18:18:59 UTC 2019
   (Requires Halyard >= 1.17)
 - 1.14.14 (LoveDeathAndRobots):
   Changelog: https://gist.github.com/spinnaker-release/ad1e0eb6b6547b296c9103eb21d9beec
   Published: Wed Aug 14 13:37:51 UTC 2019
   (Requires Halyard >= 1.17)
 - 1.15.2 (ExtremelyWickedShockinglyEvilAndVile):
   Changelog: https://gist.github.com/spinnaker-release/e72cc8015d544738d07d57a183cb5404
   Published: Mon Aug 12 20:48:52 UTC 2019
   (Requires Halyard >= 1.17)
 - 1.15.3 (ExtremelyWickedShockinglyEvilAndVile):
   Changelog: https://gist.github.com/spinnaker-release/bed366b82e09498dbb536c098ac11f14
   Published: Mon Aug 26 21:09:21 UTC 2019
   (Requires Halyard >= 1.17)
 - 1.16.0 (SecretObsession):
   Changelog: https://gist.github.com/spinnaker-release/30e40f6278a7c1a1450c179a9a4bb92e
   Published: Mon Sep 09 14:52:21 UTC 2019
   (Requires Halyard >= 1.17)
```
## バージョン指定

```
$ hal config version edit --version 1.16.0 
+ Get current deployment
  Success
+ Edit Spinnaker version
  Success
+ Spinnaker has been configured to update/install version "1.16.0".
  Deploy this version of Spinnaker with `hal deploy apply`.
```

## デプロイ

```
$ hal deploy apply
```
- 全てのコンポーネントがRunningになっていればOK

```
$ kubectl get pods -n spinnaker
NAME                                READY   STATUS    RESTARTS   AGE
spin-clouddriver-768499bdfd-82s2l   1/1     Running   0          4m37s
spin-deck-7c9f59d6fd-5wtf4          1/1     Running   0          4m38s
spin-echo-57b8bffb87-45dbz          1/1     Running   0          4m39s
spin-front50-55699974cb-h9wkp       1/1     Running   0          4m39s
spin-gate-5d886965cd-dkcg4          1/1     Running   0          4m37s
spin-orca-c45994bf8-szlwd           1/1     Running   0          4m36s
spin-redis-6949bcc974-mgm9q         1/1     Running   0          4m40s
spin-rosco-7b99df4458-zgghg         1/1     Running   0          4m35s
```

# Spinnakerの外部公開
- デフォルトの設定だとホスト名がlocalhostで展開される
- ポートフォワーディングで外部から接続を行うことも可能だが、得策ではない
- ここではAWSのLoadBalancerと連携させてhttpsで公開させる
- 設定する必要があるServiceはspin-deckとspin-gateの2つ

## 変更内容
- `kubectl edit service spin-deck -n spinnaker`、`kubectl edit service spin-gate -n spinnaker`コマンドで以下を修正
    - metadata.annotationを3つ追加
    - spec.portsのnameをhttps、portを443に変更
    - spec.typeをLoadBalancerに変更

```
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: Service
metadata:
  annotations:
    # 上の3つを追加
    # aws-load-balancer-ssl-certには使用するACMのarnを記載する
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:{region}:{user id}:certificate/{id}
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"spin","cluster":"spin-deck"},"name":"spin-deck","namespace":"spinnaker"},"spec":{"ports":[{"port":9000,"protocol":"TCP","targetPort":9000}],"selector":{"app":"spin","cluster":"spin-deck"},"type":"ClusterIP"}}
  creationTimestamp: "2019-09-10T07:39:44Z"
  labels:
    app: spin
    cluster: spin-deck
  name: spin-deck
  namespace: spinnaker
  resourceVersion: "33532"
  selfLink: /api/v1/namespaces/spinnaker/services/spin-deck
  uid: 286a725c-d39e-11e9-b497-0246cd314c16
spec:
  clusterIP: 10.100.244.189
  ports:
  - port: 443  # portを443に変更
    protocol: TCP
    targetPort: 9000
    name: https  # nameを追加
  selector:
    app: spin
    cluster: spin-deck
  sessionAffinity: None
  type: LoadBalancer　# LoadBalancerに変更
status:
  loadBalancer: {}
```

- 成功するとEXTERNAL-IPが払い出される

```
$ kubectl get svc -n spinnaker
NAME               TYPE           CLUSTER-IP       EXTERNAL-IP                                                                    PORT(S)         AGE
spin-clouddriver   ClusterIP      10.100.11.170    <none>                                                                         7002/TCP        16h
spin-deck          LoadBalancer   10.100.244.189   xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.ap-southeast-1.elb.amazonaws.com   443:30646/TCP   16h
spin-echo          ClusterIP      10.100.141.174   <none>                                                                         8089/TCP        16h
spin-front50       ClusterIP      10.100.171.140   <none>                                                                         8080/TCP        16h
spin-gate          LoadBalancer   10.100.90.231    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.ap-southeast-1.elb.amazonaws.com    443:31869/TCP   16h
spin-orca          ClusterIP      10.100.66.123    <none>                                                                         8083/TCP        16h
spin-redis         ClusterIP      10.100.137.169   <none>                                                                         6379/TCP        16h
spin-rosco         ClusterIP      10.100.2.14      <none>                                                                         8087/TCP        16h
```

- 次にカスタムプロファイルの設定を行う
- ~/.hal/default/profiles/gate-local.yamlというファイルを以下の内容で作成する

```yaml:~/.hal/default/profiles/gate-local.yaml
server:
  tomcat:
    protocolHeader: X-Forwarded-Proto
    remoteIpHeader: X-Forwarded-For
    internalProxies: .*
```

- `--base-url`を指定してCORSの設定をする

```
$ hal config security api edit --override-base-url https://spin-gate.xxx.com
$ hal config security ui edit --override-base-url https://spin-ui.xxx.com
```
- 変更を反映する

```
$ hal deploy apply
```

- 上記で指定したURLをRoute53に登録すれば完了！
- 画面が見れるか確認する


<img width="1409" alt="スクリーンショット 2019-09-11 12.57.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269592/3191362c-41e2-5b01-cb52-f76c85a360eb.png">

# デプロイターゲットとなるKubernetesクラスタの追加
- 上記のままでもSpinnakerからデプロイすることは可能だが、Spinnakerが動いているKubernetesクラスタと同じクラスタにデプロイされてしまう(それでも良いという運用もあり得ると思います)
- 今回はデプロイターゲットとはまた別のKubernetesクラスタであることを想定しているので、その設定を行う

## デプロイターゲットとなるクラスタのConfigMap修正
- Spinnakerが稼働しているクラスタにアタッチされているIAM RoleをmapRolesに記載する
- 以下のコマンドは、**デプロイターゲットとなるKubernetesクラスタを操作できる環境から実行する**

```
$ kubectl edit -n kube-system configmap/aws-auth
configmap/aws-auth edited
```
## IAM Role修正
- halコマンド実行環境から対象のクラスタを参照できるようにする
- Action eks:DescribeClusterのResourceにターゲットとなるクラスタを追加する

## kubeconfigへの追記
- デプロイ対象となるKubernetesクラスタの認証情報を取得する
- halコマンド実行環境からkubeconfigの追記を行う

```
$ aws eks --region ap-northeast-1 update-kubeconfig --name deploy-target-cluster
```

## アカウントの追加
- 先ほどと同じ要領で追加するクラスタ上でServiceAccountの[作成](https://qiita.com/str416yb/items/a1a2c742b59747dfb11e#serviceaccount%E3%81%AE%E4%BD%9C%E6%88%90)・[登録](https://qiita.com/str416yb/items/a1a2c742b59747dfb11e#serviceaccountの登録)を行う
- ユーザー名とContext名が重複しないように注意

- 追加したContextに紐付いたアカウントを追加する

```
$ hal config provider kubernetes account add <アカウント名> --provider-version v2 --context <今作ったContext>
+ Get current deployment
  Success
+ Add the develop account
  Success
+ Successfully added account develop for provider kubernetes.
```

- 変更を反映

```
$ hal deploy apply
```

- Deployのステージでアカウントを選択することでどのKubernetesクラスタにデプロイするのかを指定できる
- 以下の例はdevelopというユーザーを足した場合
<img width="1421" alt="スクリーンショット 2019-09-12 11.54.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269592/1736f423-471b-7ab9-d1c1-1bb1e446b968.png">


# 気付きのメモ
- SpinnakerのコンポーネントはPodとしてデプロイされているが(Deploymentではなく)、変更後に`hal deploy apply`コマンドの後はローリングアップデートがかかるようになっている



# 参考
- [Kubernetes実践ガイド(インプレス)](https://book.impress.co.jp/books/1118101053)
    - Spinnakerについて最も分かりやすく解説されている和書です
- https://blog.mwed.info/posts/how-to-make-spinnaker.html
    - 同じようにEKSとSpinnakerを扱っている記事です
- https://www.spinnaker.io/setup/install/halyard/#install-on-debianubuntu-and-macos
    - halyardのインストール
- https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-linux.html
    - AWS CLIのインストール
- https://kubernetes.io/docs/tasks/tools/install-kubectl/
    - kubectlのインストール