PyGMTをAWS Fargateで使えるようにする

# はじめに
[ローカル環境でPyGMTを使えるようにはなった](https://qiita.com/ckw-1227/items/73e33e3a86e10c13f334)が、せっかくなのでAWS上で使えるようにしたいと思った。…思ってしまった。ので、試行錯誤してみた

# 構成
当初、できるだけ簡単な構成にしたくてPyGMTをコンテナLambdaで実行してみたのだが、PyGMTは実行時に/.gmtフォルダを作成するらしく、/tmp/以下にしか一時ファイルを保存できないLambdaではエラーが起きてしまった。
そのため搭載先をFargateに変更し、Fargate上で描いたテスト用の地図をファイルとして出力し、S3へ保存するプログラムを作成した。
![005_lambda_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/fe499106-6530-afd2-7672-9dd5c5b1b488.png)


# S3バケットの作成
事前に画像ファイル格納用のS3バケットを作成しておく。バケット名以外はデフォルトでOK。

# ソースコード
テスト用に出力する地図の構成は[PyGMT-HOWTOの基本の地図](https://tktmyd.github.io/pygmt-howto-jp/basic_map.html#id3)から拝借したものを使用。tmp領域にpngファイルを出力後、S3へ送信する処理を行っている。
（出力ファイルを/tmp以下に置こうとしているあたりにLambdaの痕跡が見える）
```python
import pygmt
import boto3

print('Exe Start')
fig = pygmt.Figure()

fig.coast(
    projection  = 'N138/12c',
    region      = (128, 146, 30, 46),
    shorelines  = 'default,black', 
    area_thresh = 100,
    resolution  = 'f',
    land        = '249/243/202',
    water       = '190/220/250',
    map_scale   = '142.5/32/32/400',
    frame       = ['WSen+t"Figure"', 'xafg', 'yafg']
)

fig.savefig('/tmp/pygmt-coastline.png')

s3 = boto3.resource('s3')
s3.Object(
    '[S3バケット名]', 'pygmt-coastline.png').upload_file('/tmp/pygmt-coastline.png')

print('Completed!')

```

# Dockerfile
[このページ](https://forum.generic-mapping-tools.org/t/an-official-docker-image-for-gmt/2003/10) や [このページ](https://three-ai.com/?p=188)を参考にDocerfileを準備した。
Dockerfileにクレデンシャル情報を入れてるのがとてもアレだが、テスト用の仮置きということでご容赦を、、
```Dockerfile
FROM continuumio/miniconda3

WORKDIR /app

ENV AWS_ACCESS_KEY_ID "<AWSアクセスキーID>"
ENV AWS_SECRET_ACCESS_KEY "<AWSシークレットアクセスキー>"

# Create the environment:
COPY environment.yml .
RUN conda env create -f environment.yml && conda clean --all

# Copy script 
COPY app.py ./
COPY app.sh ./

# activate pygmt env
CMD ["sh", "./app.sh"]
```
environment.ymlファイルは以下。
```yml
name: pygmt
channels:
  - conda-forge
dependencies:
  - python=3.9
  - pandas
  - packaging 
  - gmt=6.4.0
  - pygmt=0.8.0
  - boto3
```
最後にapp.sh。
```bash
#!/bin/bash
. /root/.bashrc
conda activate pygmt
python app.py
```

# ECRリポジトリの作成
AWSマネジメントコンソールを開いて、Dockerコンテナを保存するための準備をする。
ECR（Amazon Elastic Container Registry）のページを開き、「リポジトリの作成」からリポジトリ名を入力＞「リポジトリを作成」をクリックする。
今回はリポジトリ名：pygmt-testで作成。
![005_lambda_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/2f120661-74b9-ada9-f115-08043bbbc407.png)

# ECRへのイメージプッシュ
作成したECRリポジトリの画面＞プッシュコマンドの表示ボタンから表示されるコマンドをコピーして実行する。

１.docker login（東京リージョンの例）
```bash
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin {AWSaccountID}.dkr.ecr.ap-northeast-1.amazonaws.com
```
２.docker build
全部で2分ほどかかる。
※"pygmt-test"は作成したECRリポジトリ名に置き換えて実行してください。
```bash
docker build -t pygmt-test .
```
３.イメージにタグを付与
```bash
docker tag pygmt-test:latest {AWSaccountID}.dkr.ecr.ap-northeast-1.amazonaws.com/pygmt-test:latest
```
４.ECRにイメージをプッシュ
```bash
docker push {AWSaccountID}.dkr.ecr.ap-northeast-1.amazonaws.com/pygmt-test:latest
```

# ECSタスク定義の作成
１.マネジメントコンソールでECSの画面を開き、左メニューで「タスク定義」をクリック。
タスク定義ファミリー（タスク定義名）、コンテナ名を入力。
イメージURIの欄は、ECRに登録したイメージのURIをコピーして入力する。
ポートマッピングは今回必要ないので削除。
あとはデフォルトで「次へ」をクリック。
![006_fargate_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/fb235ad2-0bc7-4ea0-dc88-42efee3f57bf.png)

２.次の画面ではデフォルトのまま、もう一度「次へ」。
確認画面でひと通り確認ができたら「作成」をクリック。

３.作成したタスク定義を選択し、最新のリビジョンを選んでクリック。
表示されたタスク定義画面で、「タスク実行ロール」に表示されているロール名をクリックする。
![006_fargate_002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/66779d0f-63d1-62ad-4812-d8a787959bdf.png)
IAMの画面が開くので、「許可を追加」＞「ポリシーをアタッチ」を選択する。
![006_fargate_003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/60a3f116-2470-b98a-ffc8-6d41e8328545.png)

ポリシーの選択画面が開くので、"S3"で検索して「AmazonS3FullAccess」をアタッチする。
![006_fargate_004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/93fd82b0-b275-2278-fd7a-4a9c1b762723.png)


# ECSクラスターの作成
１.マネジメントコンソールのECS画面から、クラスター＞「クラスターの作成」をクリック。
クラスター名を入力、VPCとサブネットは適当なものを選び、インフラストラクチャはAWS Fargateを指定して「作成」をクリック。
（VPCとサブネット周りまで設定していると長くなってしまうので、ここではデフォルトのものを使用しています。ネットワーク周りについては別途設定してください。）
![006_fargate_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/87d3beaf-52f8-b16a-cad2-95039a1280b4.png)

２.しばらく待つとクラスターが作成される。

# ECSタスクの作成
１.上記で作成したクラスター名を選択して、サービスの「作成」をクリックする。
![006_fargate_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/4efb09ba-1a78-bff9-7ebc-4e073bfdafb0.png)

２.今回はタスク分散等は必要ないので、コンピューティングオプションは「起動タイプ」を選ぶ。
起動タイプは「FARGATE」、バージョンは「LATEST」を指定する。
![006_fargate_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/a02c9d1e-5532-8052-2a5b-569a79956cd3.png)

デプロイ設定のアプリケーションタイプは「タスク」を選び、タスク定義のファミリーに先ほど作成したタスク定義名を設定する。
ここまで入力したらあとはデフォルトで「作成」をクリック。
![006_fargate_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/d2a1deb6-c0ea-1516-23c6-a22a4317bf69.png)

３.タスクの実行が始まるので、実行完了を待つ。タスクのステータスが「停止済み」になるまで約1分半～2分かかる。

４.実行完了後、S3バケットを確認する。画像ファイルが保存されていればプログラムは正常終了している。
![006_fargate_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/bd151fe8-a728-9537-969a-b5cc1c080212.png)

出力された画像はこんな感じ。あとはPythonコードやデータをいろいろと弄れば様々な図を描くことができる。
![pygmt-coastline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1402694/d2c8e51f-1ee8-56a2-529e-3cfe5ba85c10.png)