【DevOps】【ETL】とあるOSS CICD環境のGCPマネージド化 -前編-【GCP】

<img width="1057" alt="スクリーンショット 2020-06-13 0.48.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/7de2e432-a9ab-2318-324e-d2bdfb039341.png">


今回、構築してみたETLのCI/CD環境はこんな感じです。最後に簡単なETLのワークフロー流す時にBigQueryやCloudSQLは必要になるので図に書き込んでいます。

これらのリソースがどんなものでどういった連携をしているのかをまとめていきます。


# はじめに

最近こんな状況なのでオンライン勉強会が開きまくってますが非常にいいですね！参加のしやすさといい運営の方に感謝の日々です。
その中でビッグデータ基盤を運用している方のCI/CI環境について話を聞く機会がありました。
CI/CD環境はAWS上にOSSですべて構成されており、シームレスなETLソリューションを提供できているとのことでした。
<img width="687" alt="スクリーンショット 2020-06-21 0.43.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/0f9cbb7d-5851-6641-2b30-ec652a355108.png">
(資料は載せることはできないので利用されていたOSSを載せます)

流れとしては、GitBucket → Jenkins → DockerRegistry → Kubernetesという感じでした。
Kubernetsはコンテナ実行環境みたいでした。

「これってすべてGCPサービスで置き換えてたらどうなるのだろう？」と思ったのがこの記事のきっかけです。
GCPのDevOpsやETLワークフローの構築の仕方の参考になれば幸いです。

# 環境構築
ETLに必要なCI/CD環境およびジョブ実行環境をGCPのサービスで構築していきます。
サービスの概要や主なコマンドを記載していますが、「大体知ってるぜ！」って方は今回の設定をおってもらえれば同様の環境を構築できるかと思います。

## Cloud Source Repositories　
* フルマネージドのプライベートなgitリポジトリ
* ソースコードの管理に使用 
* GitHubやBitBucketからコードをミラーリングすることも可能
* gitコマンドでソースコードを管理

### 主なコマンド
* ```gcloud source repos create [リポジトリ名]```<br>
 - [リポジトリ名]でリポジトリを作成します
 - GoogleSDKに設定したプロジェクトに作成されます
* ```gcloud source repos clone [リポジトリ名]```<br>
 - リポジトリをクローンします

### 今回の設定
* まず上記のコマンド使用してqiita-practiceという名前でリポジトリを作成します
* ```gcloud source repos create qiita-practice```<br>
 ![スクリーンショット 2020-06-12 0.21.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/13be31a8-367d-6b9c-11bb-d838d59c24f7.png)
* ```gcloud source repos clone qiita-practice```<br>
 - qiita-practiceリポジトリをクローンします
* 後に必要となるのでDockerfile用のディレクトリを作っておきます

![スクリーンショット 2020-06-12 9.03.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/37baea4b-a851-d0d7-a44f-f02954cf1b28.png)

### 参考
* [GCP Cloud Source Repositoriesを利用する手順のメモ](https://qiita.com/rubytomato@github/items/72983ec37d7539253f38)

## Cloud Build 

* フルマネージドCI/CDプラットフォーム
* ソースコードのビルドやテストの自動化に利用
* SourceRepositoriesやGitHubでの変更をトリガーにビルドやテストを実行

### 今回の設定
* 上記に書いたようにCloud Buildにはトリガーを設定することができます、今回はdockerfileの自動ビルドを設定します
* トリガーの作成から下記の設定をします
* 今回は簡単にdockerfileの編集がmasterにpushされたらビルドが走るのような設定をします
* イメージ名のタグはpullの時に楽なのでlastestに変更しています
![re11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/316ff37a-718b-689e-69c8-6fbc507f6ff3.png)
![スクリーンショット 2020-06-12 9.12.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/d63b6195-4604-dffa-3564-48767d65db0e.png)

### 参考
* [Cloud BuildでCloud SQLのマイグレーションをしたい](https://qiita.com/kshibata101/items/fd8f5f10ff4c99504b9a)
* [Firebase デプロイに Cloud Build を使う](https://qiita.com/zaru/items/509123b2fbf9c95ed434)

## Cloud Container Registry 

* 高速でプライベートなDockerイメージリポジトリ
* dockerイメージの保管に利用
* 上記で設定したトリガーによってビルドされると自動で格納される

### 今回の設定
* dockerfileを下記のように書きます、ベースはgoogle-sdkが使えるalpineのイメージです

```dockerfile:dockerfile
FROM loblaw/google-sdk-docker

RUN apk update && \
    apk add git && \
    apk add py-pip && \
    apk add --virtual .build-deps gcc python-dev musl-dev postgresql-dev && \
    pip install --upgrade google-cloud-storage psycopg2 google-cloud-bigquery
```

* このファイルをqiita-practiceリポジトリにpushします、するとCloud Buildが実行されます

![スクリーンショット 2020-06-12 18.57.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/14542b55-5e21-f3ea-e069-f98370808e0b.png)


* ビルドが成功するとContainer Registryにリポジトリが自動的に作成されます

![re22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/e7e89cd2-8ac2-71be-aa1f-a417ad20952f.png)

はい！ここまででSourceRepositories, Build, ContainerRegistryの連携が完了しました。主にBuildを設定することでSourceRepositoriesと連携がとれ、dockerfileのビルドがあれば自動的にContainerRegistryに格納されるといった形です。

### 参考
* [プライベートDocker RegistryをGoogle Container Registry で構築](https://qiita.com/zum/items/1904ccd280879ca481c5)
* [Google Container Registry を Google Container Engine から使う方法](https://qiita.com/hakobera/items/fe049ab0e9c1fdd2c568)

では、次にCloud ComposerとGKEを利用したジョブ実行環境を構築します。

## Cloud Composer

* マネージドワークフローオーケストレーションサービス
* Airflowを基に構築されている
* Composer環境を作成すると自身のプロジェクトに作成されるものとGoogle側で管理されるものができる
    * 自身のプロジェクトに作成されるもの
        * GKEクラスタ：ジョブ実行環境
        * CloudStorageバケット：Airflowに関連するファイル(Ex. AiflowConfig, dags, logs etc)
    * Google側で管理されるもの
        * Airflowウェブサーバー：Airflow管理システム
        * CloudSQL：メタデータの管理


![スクリーンショット 2020-06-12 19.39.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/9a316192-e858-bd4c-4b02-f2f298797f33.png)

* Composer環境の作成時にGKEクラスタのデプロイオプションを選択可能(今回はプライベート環境を設定します)

### 主なコマンド
* ```gcloud composer environments create [環境名] --location=asia-northeast1 --disk-size=20GB --machine-type=n1-standard-1 --node-count=3 --python-version=2 --service-account=[サービスアカウント名] --network=[プライベートVPC名] --subnetwork=[サブネット名] --zone=asia-northeast1-b --project=[プロジェクト名] --async --enable-private-environment　--enable-private-endpoint　--enable-ip-alias```
  * Composer環境を作成します
  * ただ、--enable系のオプションが読み込まれずエラーがでました・・・(今回はConsoleから作成しました)
* ```gcloud composer environments describe [環境名] --location asia-northeast1```
  * Composer環境の詳細な説明を取得できる
  * AirflowのウェブUIのURLを取得できる

### 今回の設定
* 名前：qiita-practice, ノード数：3, ロケーション:asia-northeast1, ゾーン：asia-northeast1-b
* マシンタイプ：n1-standard-1, ディスクサイズ：20, サービスアカウント：[任意のサービスアカウント]
* イメージバージョン：composer-1.10.4-airflow-1.10.6, pythonバージョン：2, VPCネイティブの有効化：チェック
* ネットワーク：[プライベートVPC名], サブネットワーク：[サブネット名], プライベートIPを有効にする

![スクリーンショット 2020-06-12 22.14.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/cb0267a9-b91f-40ee-73c2-370420b98349.png)

* 下記、```environments describe```コマンドで取得できる結果です。

```
chan-p$ gcloud composer environments describe qiita-practice --location asia-northeast1
config:
  airflowUri: https://xxxxxxx.appspot.com
  dagGcsPrefix: gs://asia-northeast1-qiita-pract-xxxxxx-bucket/dags
  gkeCluster: projects/xxxxxxx/zones/asia-northeast1-b/clusters/asia-northeast1-qiita-pract-e9686d5d-gke
  nodeConfig:
    diskSizeGb: 20
    ipAllocationPolicy:
      useIpAliases: true
    location: projects/xxxxxx/zones/asia-northeast1-b
    machineType: projects/xxxxxx/zones/asia-northeast1-b/machineTypes/n1-standard-1
    network: projects/xxxxxxx/global/networks/[プライベートVPC名]
    oauthScopes:
    - https://www.googleapis.com/auth/cloud-platform
    serviceAccount: [サービスアカウント名]
    subnetwork: projects/xxxxxxx/regions/asia-northeast1/subnetworks/[サブネット名]
  nodeCount: 3
  privateEnvironmentConfig:
    enablePrivateEnvironment: true
    privateClusterConfig:
      enablePrivateEndpoint: true
  softwareConfig:
    imageVersion: composer-1.10.4-airflow-1.10.6
    pythonVersion: '2'
name: projects/xxxxxx/locations/asia-northeast1/environments/qiita-practice
state: RUNNING
updateTime: '2020-06-12T13:14:07.010Z'
```

* ```airflowUri: https://xxxxxxx.appspot.com```にアクセスするとAirflowのウェブUIにとびます
![スクリーンショット 2020-06-12 22.20.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/1447d96c-f651-584b-b4ec-256bc8e1c7a8.png)

### 注意点
* 環境を作成する際にGoogle側で管理するリソース群とGKEクラスタのVPCをピアリングするみたいなのですが、ネットワークに関する権限をCloud Composerのサービスアカウントに付与していないとピアリングできないといったエラーが発生しますので注意してください

### 参考
* [Cloud Composerの（よく見るとドキュメントに書いている）注意点](https://qiita.com/kurtosis/items/610c3496cccb1c64b3a8)
* [Google Cloud ComposerでBigqueryへデータをロードする](https://qiita.com/HagaSpa/items/df63f8642fe509ce6033)
* [【Airflow】最近よく聞くAirflowに入門！EC2で動かしてみた【CI/CD】](https://qiita.com/chan-p/items/526bbed95fdc73142c59)
* [【Kubernetes】Airflow on Kubernetesで最強ETL基盤【Airflow】](https://qiita.com/chan-p/items/8d31346dbaa0ee90bccf)

これでAirflowのデプロイとGKEクラスタの作成というComposer環境の構築が完了します。ジョブスケジューラーとジョブ実行環境が整ったので次の記事で実際のETLフローを構築しながら開発していきますが、ETLシステムに必要なリソースも下記で設定しておきます。

## Cloud SQL 

* フルマネージドデータベース
* 今回はETLのステータス管理として使用します(詳細は後編で説明します)

### 今回の設定

* PostgreSQLのマネージド
* プライベートIPを有効化
* 重要なのはGKEクラスタと同一のプライベートVPCを指定
→ CloudSQLにプライベートな接続をしようとするとCloudSQL Proxyの設定が必要になってくるのですが、今回は簡単に利用していきたいためジョブ実行環境であるGKEクラスタと同一のプライベートVPCを指定することでGKEクラスタからPythonのライブラリのみで接続を可能にします
→ CloudSQLはGoogle側にデプロイされますが、プライベートIPを有効化すると指定したVPCとのデプロイされたVPCをピアリング接続するようです
![スクリーンショット 2020-06-13 19.17.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148317/95d6a8fc-8298-75b1-0d86-a49628bf64f7.png)

* ユーザー名：qiita, パスワード：qiita, データベース： qiita-practice, ホスト：**.**.**.**
* スキーマ作成：```CREATE SCHEMA etl```
* テーブル作成：```CREATE TABLE etl.bulk_list (id SERIAL ,row_start integer,row_end integer,state text);```

### 参考
* [Cloud Composer からCloud SQL へ接続しQueryを投げる方法](https://qiita.com/ttakagi/items/235c97e438f5580d645d)
* [Cloud SQL Proxyをサービスアカウントキーなしで使う](https://qiita.com/kaedemalu/items/8329de8a23578352ae75)

## 最終的なdockerfile

```dockerfile:dockerfile
FROM loblaw/google-sdk-docker

# Required Tools
RUN apk update && \
    apk add git && \
    apk add py-pip && \
    apk add --virtual .build-deps gcc python-dev musl-dev postgresql-dev && \
    pip install --upgrade google-cloud-storage psycopg2 google-cloud-bigquery

RUN echo '[]' >> /root/sa.json # Not Recommend

# Cloud SQL Config
ENV USER qiita
ENV DATABASE qiita-practice
ENV PORT 5432
ENV PASSWORD qiita
ENV HOST **.**.**.**

# Update Google SDK
RUN gcloud components update && \
    gcloud components update kubectl 

# Google SDK Config
RUN gcloud config set project [プロジェクト名] && \
    gcloud config set account [サービスアカウント名] && \
    gcloud auth activate-service-account [サービスアカウント名] --key-file=/root/sa.json && \
    gcloud source repos clone qiita-practice && \
    gcloud info

```
# おわりに
今回はGCPのCI/CD周りのサービスを紹介しました。
SourceRepositories, Build, ContainerRegistry + コンテナ実行環境(Composer+KubernetesPodOperator, Cloud Run etc)を組み合わせるだけでもスムーズな開発できそうですね。

後半ではCI/CDを回しながら実際にETLフローを構築しようと思います。
[【DevOps】【ETL】とあるOSS CICD環境のGCPマネージド化 -後編-【GCP】](https://qiita.com/chan-p/items/d369a4ec80a4c1b11764)