AWS Service Operatorを使ってみた

# AWS Service Operator 
- AWS Service Operatorとは、AWSのリソースを管理するOperator
- CRD(CustomResourceDefinition)としてKuberenetesクラスタにデプロイする
- AWSのリソース(S3のBucketやDynamoDB等)を使用するサービスをKubernetesにデプロイする場合、Kubernetesのサービスはマニフェストファイルで記述できるが、AWSのリソースは別途CFn(CloudFormation)やTerraformで作成する必要がある。AWS Service Operatorを使用することで、AWSのリソースも含めてKubernetesのマニフェストファイルで管理することができる。
 
# Operatorとは
- Operatorとは、Kubernetesそのものが持つ機能を拡張し、人手などを介して実施していた作業をKubernetes上で管理することで自動化する仕組み
- もともとはCoreOS,IncがKubernetesの監視ツールであるPrometheusやetcdの運用管理を自動化するために作られた
- Operatorの仕組みを支えているのがControl Loop
    - Control Loopはクラスタの状態を監視し、あるべき状態との差分がある場合にはそれを調整するKubernetes上の仕組み
    - Control LoopはKubernetesが持つリソースとコントローラによって実現されている
        - 例えばDeploymentやReplicasetなどの既存リソースに対しては、DeploymentコントローラやReplicasetコントローラが稼働しており、これらのコントローラがAPIを経由して状態の監視、及びリソースの作成・削除等の指示を行なっている
- Cotrol Loopの仕組みを利用するため、Operatorで扱うリソースを対象とした「カスタムリソース」と「カスタムコントローラ」を作成する必要がある

## Custom Resource(カスタムリソース)
- PodやDeploymentなどのリソースは、コアリソースと呼ばれ、Kubernetes上で管理されている
- ユーザーが独自で追加できるリソースがカスタムリソース
- 既存のKubernetes APIを拡張してできる独自のデータ構造
- APIを拡張する方法の1つが、CRDによるもの

## Custom Controller
- カスタムコントローラは、カスタムリソースの状態を確認しながら、それによって管理されるオブジェクトを調整するコントローラ
- 開発自体はclient-goやkubebuilder、OpenSDKを利用して行われ、Deploymentとして展開される

# AWS Service Operatorのデプロイ
- AWS Service Operatorで対応しているサービスは以下の通り
    - DynamoDBのデーブル
    - S3のバケット
    - ECRのレポジトリ
    - SNSのトピックとサブスクリプション
    - SQSのキュー

## 環境
- Kubernetesクラスタ
    - EKS
- Kubernetesバージョン
    - v1.12.7

## IAM Role作成

- AWSのリソースにアクセスできるようにIAM Roleを作成
- 一旦AdministratorAccessのPolicyをアタッチする

## マニフェストファイル
- 提供されているマニフェストファイルをダウンロードする

```
$ wget https://raw.githubusercontent.com/awslabs/aws-service-operator/master/configs/aws-service-operator.yaml
```

- マニフェストファイルを見ると、一番下にDeploymentの記載があり、これがカスタムコントローラに対応

```yaml:aws-service-operator.yaml(抜粋)
kind: Deployment
apiVersion: apps/v1beta1
metadata:
  name: aws-service-operator
  namespace: aws-service-operator
spec:
  replicas: 1
  template:
    metadata:
      annotations:
        iam.amazonaws.com/role: arn:aws:iam::XXXXXXXXXXX:role/AWS-Operator
      labels:
        app: aws-service-operator
    spec:
      serviceAccountName: aws-service-operator
      containers:
      - name: aws-service-operator
        image: awsserviceoperator/aws-service-operator:v0.0.1-alpha4
        imagePullPolicy: Always
        args:
          - server
          - --cluster-name=operator-test
          - --region=ap-northeast-1
          - --account-id=XXXXXXXXXXX
```
- spec.template.spec.containers.argsの内容を自分の環境の値に設定
- 今回使用するクラスタではkube2iamがセットアップされているので、spec.metadata.annotationsに先ほど作成したIAM Roleのarnを指定している。これにより、Deploymentから作成されるPodにそのIAM Roleを付与することができる。kube2iamがセットアップされていない場合は、IAM Roleをノードグループへアタッチする必要がある。
    - 参考
        - https://github.com/jtblin/kube2iam
        
## デプロイ

```
$ kubectl apply -f aws-service-operator.yaml
```
- CRDが作成されていることが確認できる

```
$ kubectl get customresourcedefinitions
NAME                                           CREATED AT
alertmanagers.monitoring.coreos.com            2019-08-06T10:48:53Z
cloudformationtemplates.service-operator.aws   2019-08-06T11:27:25Z
dynamodbs.service-operator.aws                 2019-08-06T11:27:25Z
ecrrepositories.service-operator.aws           2019-08-06T11:27:25Z
elasticaches.service-operator.aws              2019-08-06T11:27:25Z
prometheuses.monitoring.coreos.com             2019-08-06T10:48:54Z
prometheusrules.monitoring.coreos.com          2019-08-06T10:48:54Z
s3buckets.service-operator.aws                 2019-08-06T11:27:25Z
servicemonitors.monitoring.coreos.com          2019-08-06T10:48:54Z
snssubscriptions.service-operator.aws          2019-08-06T11:27:25Z
snstopics.service-operator.aws                 2019-08-06T11:27:25Z
sqsqueues.service-operator.aws                 2019-08-06T11:27:26Z
```

- これにより、定義したリソースをkubectlコマンドで扱えるようになった
- まだリソースは作成していないが、例えばs3bucketsの情報を取得する場合は以下の通り

```
$ kubectl get s3buckets
No resources found.
```

- コントローラは以下のように確認

```
$ kubectl get pod -n aws-service-operator
NAME                                   READY   STATUS    RESTARTS   AGE
aws-service-operator-5b65c8545-pvmv2   1/1     Running   0          35h
```

# サンプルアプリケーションのデプロイ
## マニフェストファイル
- サンプルアプリケーションのマニフェストファイルを取得する

```
$ wget https://gist.githubusercontent.com/christopherhein/1cb7da812a197ac3cc547ed2495faf9d/raw/4909e8ad6b35843e1696735e9f62301e9bde7ff9/dynamodb-app.yaml
```
- このマニフェストファイルでは、DynamoDB、Service、Deploymentが作成され、PodからDynamoDBのテーブルを参照する作りになっている

- DynamoDBの記述は以下の通り

```yaml:dynamodb-app.yaml(抜粋)
apiVersion: service-operator.aws/v1alpha1
kind: DynamoDB
metadata:
  name: dynamo-table
spec:
  hashAttribute:
    name: name
    type: S
  rangeAttribute:
    name: created_at
    type: S
  readCapacityUnits: 5
  writeCapacityUnits: 5
```

- PodからDynamoDBを参照するため、aws-service-operator.yamlと同様にDeploymentのspec.metadata.annotationsにIAM Roleのarnを指定する必要がある

## デプロイ

```
$ kubectl apply -f dynamodb-app.yaml
dynamodb.service-operator.aws/dynamo-table created
service/frontend created
deployment.apps/frontend created
```
- 以下のようにDynamoDBのリソースを確認できる

```
$ kubectl get dynamodb
NAME           AGE
dynamo-table   10m
```

- また、AWSのマネージメントコンソールからも、DynamoDBのテーブルが作成されていることが確認できる<img width="1104" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269592/2bd197cf-a147-c17c-56b5-bafe5273d983.png">

## アプリが起動しない問題
- DynamoDBなどの作成は問題なくできたもののデプロイしたPodが正常に起動していない

```
$ kubectl get pod
frontend-9f44cbb5b-4777b               0/1     CreateContainerConfigError   0          44m
```

- describeコマンドを打つとconfigmapsが見つからないというエラーが出ている

```
Events:
  Type     Reason     Age                    From                                                        Message
  ----     ------     ----                   ----                                                        -------
  Normal   Scheduled  58m                    default-scheduler                                           Successfully assigned default/frontend-9f44cbb5b-4777b to ip-192-168-106-94.ap-northeast-1.compute.internal
  Normal   Pulled     56m (x8 over 58m)      kubelet, ip-192-168-106-94.ap-northeast-1.compute.internal  Successfully pulled image "christopherhein/dynamoapp:latest"
  Warning  Failed     56m (x8 over 58m)      kubelet, ip-192-168-106-94.ap-northeast-1.compute.internal  Error: configmaps "dynamo-table" not found
  Normal   Pulling    3m13s (x222 over 58m)  kubelet, ip-192-168-106-94.ap-northeast-1.compute.internal  pulling image "christopherhein/dynamoapp:latest"
```
- ConfigMapからDynamoDBのテーブル名を参照しているため、その参照ができずにエラーが出ている模様
- Issueにあげました
    - https://github.com/awslabs/aws-service-operator/issues/203

# まとめ

- カスタムリソースの例として、AWSのリソースを扱えるものをデプロイした
- これにより、DynamoDBなどのAWSリソースをKubernetesのリソースとして扱うことができるようになった
- Operatorの自作もやってみたい

# 参考
- https://aws.amazon.com/jp/blogs/opensource/aws-service-operator-kubernetes-available/
- [Kubernetes実践ガイド(インプレス)](https://book.impress.co.jp/books/1118101053)