Cloud Composerをスケールさせたい時のアプローチ検討

## はじめに

この記事は[DMMグループ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/dmm) 22日目の記事です

DMM.com マーケティングテクノロジー部の[津久井](https://qiita.com/kkkdev)です。
Advent Calendar参加は今回で2回目です。

以前から所属していたアドテクノロジー部が領域を広げ、今年4月からMarTech(日本だと最近はマーケティングテックとも呼ばれています)でDMMを成長させてゆく組織となりました! 

## ワークフローエンジンのトレンド(?)

digdag(弊社でもよく利用されています)やLuigiなど、所謂ワークフローエンジンというものにはいくつかOSSが存在しますが、その1つとしての[Apache Airflow](https://airflow.apache.org/)は、GCPでのCloud Composerとしての提供 + AWSではSagemakerとの統合などで、各クラウドサービスで採用をされています。

その中でもCloud Composerは、Airflow自体がマイクロサービスとして構成されているのを活かし、Google Kubernetes Engineのkubernetesで構成されたサービスの提供が行われ、WebUI・スケジューラ・ワーカーノードなどがGKE上で動いています。

さらにAirflowには[KubernetesPodOperator](https://cloud.google.com/composer/docs/how-to/using/using-kubernetes-pod-operator)というk8sでコンテナ実行を行うオペレータがあり、このオペレータをCloud Composer(あるいは手を加えて他のクラスタ)のk8s上で実行させることにより、マネージドかつ言語を問わない(Airflowの実行環境自体をカスタマイズしなくとも良い)
ワークフローの実行が可能となります。
[(Airflow on Kubernetes)](https://kubernetes.io/blog/2018/06/28/airflow-on-kubernetes-part-1-a-different-kind-of-operator/)

そんな感じでほぼほぼワークフローエンジン管理の手間から開放されるCloud Composerですが、
それでもプロダクションで長期運用していくにあたって、いくつか課題はあります。

## 課題の1つ

GKE上ではあるもののCloud Composer自体は現在 ノードのオートスケールはせず、起動時などにノード数を調整する必要があります。
(将来的には公式に提供されるようです)[^1]
深夜の数時間のバッチを毎時/毎月で実行させようとした時に無駄なくコストを節約するためには、
計算リソースを負荷に応じて動的にスケールさせたいところです。

## アプローチの検討

方法としてはいくつか考えられますが、Cloud ComposerそのもののAirflow workerを
スケールさせるか、各クラウドサービスのサーバレスのコンテナ実行環境に実行を委譲する
アプローチの2通りが考えられます。


A. Cloud ComposerのGKEノードをスケール対応させる方法
B. 各クラウドサービスのサーバレスのコンテナ実行環境を***Operator経由で使う


### A. Cloud ComposerのGKEノードをスケール対応させる方法

Cloud Composer自身のクラスタがAutoscalingするようにし、バッチを実行するパターンです。

Cloud Composerのクラスタ自体はGKE製なので、Autoscaling自体の設定は可能ですが、
実際に下記のようにGKE/k8s/設定それぞれに変更をかける必要があります。 [^2]

・1. GKEのAutoscalingを有効にする
・2. podのスペック変更
・3. k8sのHPAの設定を反映
・4. Airflowのパラメータの調整

(参考: https://medium.com/traveloka-engineering/enabling-autoscaling-in-google-cloud-composer-ac84d3ddd60)

#### 0. (事前)Cloud ComposerのGKEクラスタのcontextへの切り替え

```
$ gcloud container clusters list
NAME                                  LOCATION       MASTER_VERSION  MASTER_IP        MACHINE_TYPE   NODE_VERSION    NUM_NODES  STATUS
us-central1-tsukui-test-a457fa05-gke  us-central1-a  1.13.11-gke.14  104.198.185.117  n1-standard-1  1.13.11-gke.14  3          RUNNING
```

```
$ gcloud container clusters get-credentials us-central1-tsukui-test-a457fa05-gke --zone=us-central1-a
Fetching cluster endpoint and auth data.
kubeconfig entry generated for us-central1-tsukui-test-a457fa05-gke.
```

Cloud Composerはk8sの他にCloudSQL/GCS等で構成されていますが、GKE部分としては以下のように
workerの他にもSQL ProxyなどのServiceなどが使われています。

```
$ kubectl get pod,service,deployment,namespace
NAME                                                            READY   STATUS      RESTARTS   AGE
pod/airflow-monitoring-8566957bdb-7t4td                         1/1     Running     0          150m
pod/airflow-redis-0                                             1/1     Running     0          150m
pod/airflow-sqlproxy-8744845d9-9tqmm                            1/1     Running     0          150m
pod/composer-agent-a457fa05-2d98-4b7d-b15f-246972e54929-44h5w   0/1     Completed   0          151m
pod/composer-agent-ba19564d-aa0c-4dc0-9129-57289eeb073f-hmjqc   0/1     Completed   0          140m
pod/composer-fluentd-daemon-59s6q                               1/1     Running     0          150m
pod/composer-fluentd-daemon-5csrl                               1/1     Running     0          150m
pod/composer-fluentd-daemon-8bxqb                               1/1     Running     0          150m

NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/airflow-monitoring-service   ClusterIP   10.59.245.253   <none>        8125/UDP   150m
service/airflow-redis-service        ClusterIP   10.59.250.253   <none>        6379/TCP   150m
service/airflow-sqlproxy-service     ClusterIP   10.59.250.190   <none>        3306/TCP   150m
service/kubernetes                   ClusterIP   10.59.240.1     <none>        443/TCP    159m

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/airflow-monitoring   1/1     1            1           150m
deployment.extensions/airflow-sqlproxy     1/1     1            1           150m

NAME                                              STATUS   AGE
namespace/composer-1-8-3-airflow-1-9-0-a457fa05   Active   155m
namespace/default                                 Active   159m
namespace/kube-public                             Active   159m
namespace/kube-system                             Active   159m
```

#### 1. GKEのAutoscalingを有効にする

GUIでもCUIでも行えます。

```
gcloud container clusters update (クラスタ名) --enable-autoscaling
--min-nodes 1 --max-nodes 20 --project (プロジェクト名) --node-pool=default-pool
--zone (ゾーン名)
```

#### 2. podのスペック変更
Podに割り当てるCPU/メモリを必要に応じて大きくします。(kubectl patch)

```
spec:
  template:
    spec:
      containers:
      - name: airflow-worker
        resources:
          requests:
            memory: 2Gi
          limits:
            memory: 2Gi
      - name: gcs-syncd
        resources:
          requests:
            cpu: 10m
            memory: 512Mi
          limits:
            cpu: 10m
            memory: 512Mi
```

```
kubectl patch deployment airflow-worker -n (AirFlowのnamespace) --patch "$(cat バッチのyaml)"
```

#### 3. k8sのHPAの設定を反映
k8s側のPodをスケールさせるために、HorizontalPodAutoscalerを反映させます。

```
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: airflow-worker
  namespace: #AirFlowのnamespace
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: airflow-worker
  minReplicas: 1
  maxReplicas: 40 #お好みで
  metrics: #お好みで
  - type: Resource
    resource:
      name: memory
      targetAverageValue: (お好みで)
```

#### 4. Airflowのパラメータの調整
core-dag_concurrency(composer-1.1.1) などの、変更可能な制限を調整します。

(gcloud composer environments update で可能)

#### B. 各クラウドサービスのサーバレスのコンテナ実行環境を***Operator経由で使う

こちらはスケールというよりは、自身の外にコンテナ実行を逃がすパターンです。


Airflowに豊富なOperatorが存在するのですが、KubernetesPodOperatorの他にも
[ECSOperator(Fargateも指定可)](https://github.com/apache/airflow/blob/master/airflow/contrib/operators/ecs_operator.py)なども提供されており、ジョブをFargateで実行させることが可能です。[^2]

GPUを使いたい場合は[Azure Container InstancesのOperator](https://github.com/apache/airflow/blob/master/airflow/contrib/operators/azure_container_instances_operator.py)も用意されています。

※ なお、microsoftの[Virtual Kubelet](https://github.com/virtual-kubelet/virtual-kubelet)という Podの実行を各クラウドのコンテナ実行環境に委譲させるOSSも存在するのですが、Fargate対応のプラグイン[AWS Fargate Provider](https://github.com/virtual-kubelet/aws-fargate)は「This project is currently inactive and is seeking maintainers」だそうです.....


## どうするのが

いくつかのアプローチを並べましたが、結局は「Cloud ComposerそのもののAirflow Workerを
スケール」させつつ、処理と導入クラウドサービスに応じて「サーバレスのコンテナ実行環境で実行させる」のが
どのパターンにも対応が可能と思われます。

## おわりに

今回は本格的に利用する前の検討の意味合いが強く 気がついたら文章ばかりになってしまったので、
今後色々続きのアウトプットを充実させていきたく思います。 ~~Azure無料環境がほしいです~~

マーケティングテクノロジー部は クラウドを活用してガシガシ 0→1 や 1→10 を行っていく
モチベーションを持った仲間を引き続き募集しています! 

明日 12/23は、@masa-m さんです。

[^1]: 出典: https://cloud.google.com/blog/products/gcp/cloud-composer-is-now-in-beta-build-and-run-practical-workflows-with-minimal-effort


[^2]: 参考: https://labs.gree.jp/blog/2018/12/17564/