Kaggleの学習から投稿までをAWS, GitHub Actionsを使って自動化する
金山(@tkanayama_)です。先日終了したKaggleの"M5 Forecasting"というコンペに参加した際、クラウドやCI/CDの勉強も兼ねて、AWS, GitHub Actionsを使って遊んでみました。
免責
- N番煎じだったらすみません。一応、同じことをやっているネット記事は見つかりませんでした。
- 私はクラウドなど勉強中の身分ですので、もっといいやり方がある or 説明が間違っている、などありましたら教えてください。
- 私がこのシステムを使って参加したコンペの順位は5,558チーム中1,000,000,000位だったので、Kaggleで勝てるかどうかは別問題のようです :pien:
この記事のゴール
下記のようなシステムを構築することをゴールとします。
ユーザーがやることは2つ(図中でユーザーから伸びている黄色矢印)で、
- 実装したコードをgit pushし、
- AWSコンソールからEC2の実行ボタンを押す
です。その他はシステムによって自動化されていることを目指します。また、実行中のログやメモリ使用率・CPU使用率などはCloudWatchを用いてリアルタイム*1で可視化します。
この記事で扱うこと・扱わないこと
扱うこと
扱わないこと
- 細かい実現方法(AWSコンソールの操作方法など)は、すぐに変わってしまう可能性が高い・他の多くのブログですでに紹介されていることから、適宜省略します。特に、AWS CLI周りの設定は済んでいる前提で話を進めていきます。
本題
必要なことは、
- クラウド上で実行したいコードを用意する
- Dockerfileを書く
- s3 bucketを用意する
- ECRのレポジトリを用意する
- GitHub Actionsを設定する
- AMIを作成する
- EC2の起動テンプレートを作成する
です。順を追って説明していきます。
1. クラウド上で実行したいコードを用意する
まずは、submitしたら優勝間違いなしの最強のアルゴリズムを実装します。
実装できましたでしょうか。例として、Titanicのsample submissionを出力するだけのコードを置いておきます。(GitHub)
このコードではMLパイプラインとしてgokartを使っています。(所属組織の影響を受けています。)
gokartは出力先としてAWS s3やGoogle Cloud strageのバケットのurlを下記のように指定するだけで、中間ファイルや最終結果ファイルの出力先を変えることができるので、今回実現したいパイプラインには都合が良いです。
TASK_WORKSPACE_DIRECTORY=s3://kaggle-titanic/
2. Dockerfileを書く
Dockerfileは下記のように記述しました。
FROM python:3.6.8-stretch COPY ./Pipfile /app/Pipfile COPY ./Pipfile.lock /app/Pipfile.lock WORKDIR /app RUN pip install --upgrade pip &&\ pip install pipenv &&\ pipenv install --system --deploy &&\ rm -rf ~/.cache WORKDIR / COPY ./conf /app/conf COPY ./titanic /app/titanic COPY ./main.py /app/main.py COPY ./script /app/script WORKDIR /app VOLUME "/app" ENV TASK_WORKSPACE_DIRECTORY s3://titanic-example/ CMD ["bash", "script/endpoint.sh"]
最後の2行で
- 必要な環境変数のセット
- taskを実行するためのshell scriptの指定
を行なっていますが、これはdokcer-composeを使ってdocker-compose.yaml側に書くほうが綺麗かもしれないなと今思いました。
3. s3 bucketを用意する
- のDockerfileに記述した出力先bucketを作成します。特筆事項はありません。
4. ECRのレポジトリを用意する
AWSのECRにアクセスし、レポジトリを作成します。これもコンソール上で10秒でできるので特に言及すべきポイントはありません。今回は 'titanic' という名前で作ります。
5. GitHub Actionsを設定する
GitHub Actionsは、公式が用意しているtemplateがとてもわかりやすいです。今回は、"Deploy to Amazon ECS" というテンプレートを元に下記のように作成しました。
on: push: branches: [ master ] name: Deploy to Amazon ECS jobs: deploy: name: Deploy runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-2 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: Build, tag, and push image to Amazon ECR id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: titanic IMAGE_TAG: latest run: | # Build a docker container and # push it to ECR so that it can # be deployed to ECS. docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
ここで、secrets.AWS_ACCESS_KEY_ID
とsecrets.AWS_SECRET_ACCESS_KEY
は別途GitHubのSecretsに登録しておく必要があります。(外部に公開しないように注意です!)
また、ECR_REPOSITORY: titanic
の部分は、「4. ECRのレポジトリを用意する」で作成したレポジトリ名に読み替えてください。
ここまで設定した上でgit pushすると、GitHub Actions上でdocker buildとdocker pushが走り、めでたくECRにdocker imageがpushされるはずです。
5. AMIを作成する
次に、AWSコンソールからec2を起動します。baseとなるAMIは Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type
を指定します。
起動したら、ssh接続でサーバー内に入ります。その後、シェル上で下記3つの設定を行います。
docker関連
baseのimageにはdockerが入っていないので、dockerをinstallしておきます。
sudo yum update -y sudo yum install -y docker sudo usermod -a -G docker ec2-user
CloudWatch関連
CloudWatchに実行ログやメモリ使用量などの情報を送り、可視化できるようにするための作業です。公式のサイト(サイト1・サイト2)を参考にしながら、下記のコマンドを実行していきます。
sudo yum install -y perl-Switch perl-DateTime perl-Sys-Syslog perl-LWP-Protocol-https perl-Digest-SHA.x86_64 curl https://aws-cloudwatch.s3.amazonaws.com/downloads/CloudWatchMonitoringScripts-1.2.2.zip -O unzip CloudWatchMonitoringScripts-1.2.2.zip && rm CloudWatchMonitoringScripts-1.2.2.zip && cd aws-scripts-mon
次に
crontab -e
でcrontabを開き、crontabに下記を記述します。(定期的にメモリ使用量をCloudWatchに送る処理です。)
*/5 * * * * ~/aws-scripts-mon/mon-put-instance-data.pl --mem-used-incl-cache-buff --mem-util --mem-used --mem-avail --disk-space-util --disk-path=/ --from-cron
Kaggle API関連
Kaggleの自分のアカウントページからAPI tokenをダウンロードしてきます。そして、下記のようにtokenの情報をコピペしてコマンドライン上で実行します。(このあたりも、docker-composeとAWS パラメータストアを組み合わせて環境変数を外から注入できるようにした方が後からの変更に強そうだということに、ブログを書きながら気づきました。)
echo 'KAGGLE_USERNAME=tepppeikanayama' >> ~/kaggle.txt echo 'KAGGLE_KEY=***********' >> ~/kaggle.txt
イメージの作成
上記「docker関連」「CloudWatch関連」「Kaggle API関連」の実行が終わったら、EC2のコンソール上でイメージの作成をします。
6. IAM roleを作成する
次に、権限管理を行うためのIAM roleを設定します。今回アクセス権限が必要なサービスは、S3, EC2, CloudWatchの3つです。コンソールからIAMのページを開き、この3サービスに対してFullAccessの権限を持ったIAMロールを作成します。
7. EC2の起動テンプレートを作成する
最後に、 EC2の起動テンプレートを作成します。起動テンプレートは、EC2のコンソールから簡単に設定できます。
設定項目は下記です。
- AMI: 「5. AMIを作成する」で作成したAMIを指定します。
- インスタンスタイプ: 実行したいコードの大きさと予算との兼ね合いで、適当なインスタンスを選択します。
- IAMロール: 「6. IAM roleを作成する」で作成したIAMロールを選択します。
- ユーザーデータ(インスタンス起動後に自動で実行してくれるscriptです):
#!/bin/bash sudo service docker start $(aws --region us-east-2 ecr get-login | sed -e 's/-e none //g') docker pull 921126570142.dkr.ecr.us-east-2.amazonaws.com/titanic:latest docker container run --env-file=/home/ec2-user/kaggle.txt --log-driver=awslogs --log-opt awslogs-region=us-west-2 --log-opt awslogs-group=/home/ec2-user/logfile.log 921126570142.dkr.ecr.us-east-2.amazonaws.com/titanic echo "sudo halt" | at now + 5 minutes
ECRのpathなどは適宜読み替えてください。やっていることとしては、ECRにpushされた最新のdocker imageをpullする→実行する→実行が終わったらシャットダウンする、という流れです。
これで、最初に挙げた図のようなシステムが完成です!
今後やりたいこと
- docker-compose, ECS, パラメータストアなどを組み合わせて、特に環境変数周りを整理する。
- TerraformでInfrastructure as Codeを実現する。
*1:メモリ使用率・CPU使用率は5分くらいのタイムラグあり