Azure Container Instancesを使ってバッチジョブを作ってみる

# はじめに
こちらは[エーピーコミュニケーションズ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ap-com) 24日目の記事となります。

今回、Azure Container Instances（以下、ACI） について以下の観点で確認をおこないました。

* そもそもどんな使い勝手か？
* バッチ処理の基盤として使えそうか？

# 目的
私が所属する部署では、[Azureを軸にしたクラウドネイティブ内製化支援](https://www.ap-com.co.jp/service/cloudnative_insourcing/)をおこなっております。
コンテナ技術を軸に支援をおこなっていくにあたり、今後も残り続けるであろう[伝統的なバッチ処理](https://qiita.com/koduki/items/e90ee1fea5aa75071d95)をどうやって段階的にモダナイズしていくのがいいか、というのを考えています。
[こちらの記事（有料記事）](https://xtech.nikkei.com/it/atclncf/service/00023/071400002/?P=2)にバッチ処理に有用な（Azureの）サービスがまとまっているのですが、ACIもバッチ処理に使えそうな雰囲気があったので、試してみることにしました。
あとそもそもACIの使い勝手を試してみたいという観点もありました。

# ACIについて
https://docs.microsoft.com/ja-jp/azure/container-instances/container-instances-overview

AWSでいうところのFargate、GCPでいうところのCloud Runと比較される立ち位置のものです。
スケールアウト不要な、単体の実行環境をさくっと立てられるサービスです。

# やったこと
バッチ処理を模したpythonスクリプトをACIのコンテナで実行してみました。
（SQLデータベースからデータを取得→結果ファイルをblobストレージに置く、という、データ連携の処理をイメージしました）

ユースケースとしては、JP1みたいなジョブコントローラーのAgentプロセスがコンテナ上で動いていて、そのAgentがスクリプトを実行する、というシーンを想定しました。

# やったことの流れ
1. サンプルスクリプト作成
1. dockerイメージ作成
1. ↑をACRへPush
1. ↑のイメージからACIを作成
1. ↑で作成したACIインスタンスにssh接続してサンプルスクリプト実行


# 使ったAzureの環境
* Azure Container Instances 
* Azure Container Registry
* SQLデータベース（サンプルデータが含まれる状態で作成しました）
* ストレージアカウント（blobストレージ）

# サンプルコード（Dockerイメージに含めました）
* 参考にした記事
    * https://docs.microsoft.com/ja-jp/azure/azure-sql/database/connect-query-python
    * https://docs.microsoft.com/ja-jp/azure/storage/blobs/storage-quickstart-blobs-python#list-the-blobs-in-a-container

```python:sample.py
import datetime
import os
import pyodbc
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient, __version__

# database access settings
server = '**************'
database = '**************'
username = '**************'
password = '**************'
driver= '{ODBC Driver 17 for SQL Server}'

# storage access settings
connect_str = '**************'
container_name = "data"

print ("exec start")

date_s = (datetime.datetime.now().strftime('%Y%m%d%H%M%S%f'))
filename = "sample_"+ date_s + ".csv"
full_path = os.getcwd() + "/data/" + filename
f = open(full_path, "x")

cnxn = pyodbc.connect('DRIVER='+driver+';PORT=1433;SERVER='+server+';PORT=1443;DATABASE='+database+';UID='+username+';PWD='+ password)
cursor = cnxn.cursor()

# selectしているテーブルは、SQLデータベースのサンプルデータベースのテーブルです。
cursor.execute("SELECT TOP 20 pc.Name as CategoryName, p.name as ProductName FROM [SalesLT].[ProductCategory] pc JOIN [SalesLT].[Product] p ON pc.productcategoryid = p.productcategoryid")
row = cursor.fetchone()
while row:
    f.write("\"" + str(row[0]) + "\"" + "," + "\""+ str(row[1]) + "\"\n")
    row = cursor.fetchone()

cursor.close()
cnxn.close()
f.close

# Create the BlobServiceClient object which will be used to create a container client
blob_service_client = BlobServiceClient.from_connection_string(connect_str)

# Create a blob client using the local file name as the name for the blob
blob_client = blob_service_client.get_blob_client(container=container_name, blob=filename)

print("\nUploading to Azure Storage as blob:\n\t" + filename)

# Upload the created file
# with open(full_path, "rb") as data:
blob_client.upload_blob(full_path)

print ("exec end")

```


# dockerイメージ作成→Azure Container RegistryへPush

* 参考にした記事
    * https://qiita.com/jhorikawa_err/items/fb9c03c0982c29c5b6d5
    * https://docs.microsoft.com/ja-jp/sql/connect/python/pyodbc/step-1-configure-development-environment-for-pyodbc-python-development?view=sql-server-ver15#linux
    * https://docs.microsoft.com/ja-jp/python/api/overview/azure/sql?view=azure-python
    * https://docs.microsoft.com/ja-jp/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15#microsoft-odbc-17

↓のDockerfileでイメージ作成した。

```Dockerfile:Dockerfile
FROM python:3
USER root

RUN apt-get update
RUN apt-get -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8

# pyodbcを入れるための前提セットアップ
ENV DEBIAN_FRONTEND noninteractive
ENV DEBCONF_NOWARNINGS yes

# 先にunixodbc入れておかないと「RUN ACCEPT_EULA=Y apt-get install -y msodbcsql17」が失敗する
# 先にunixodbc-dev入れておかないと「RUN pip install pyodbc」が失敗する
RUN apt-get -y install gcc g++ build-essential unixodbc unixodbc-dev

# for Microsoft ODBC Driver for SQL Server
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -

#Download appropriate package for the OS version
#Choose only ONE of the following, corresponding to your OS version
#Debian 10
RUN curl https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list

RUN exit
RUN apt-get update
RUN ACCEPT_EULA=Y apt-get install -y msodbcsql17

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN pip install pyodbc

# blobストレージ操作のためのパッケージインストール
RUN pip install azure-storage-blob

# バッチファイル配置
WORKDIR /opt/batch
COPY ./sample.py /opt/batch
RUN mkdir /opt/batch/data

# sshアクセスできるようにする
RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:*******' | chpasswd
RUN sed -i 's/#*PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed -i 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' /etc/pam.d/sshd

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
```

# Container Instancesの作成

* 参考にした記事
    * https://docs.microsoft.com/ja-jp/azure/container-registry/container-registry-get-started-portal

**Container Instancesの作成画面を開く→サブスクリプションとリソースグループを選びます。**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411926/9b3418af-34c1-254d-2708-12431042bf33.png)|
|:--|


**コンテナ名とイメージのソースを選びます。今回はイメージのソースとしてAzure Container Registryを選択します。**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411926/04466aa4-a1f9-4f86-185e-7a39535f36cd.png)|
|:--|


**レジストリとイメージを選択し、ネットワークの設定画面に進みます。**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411926/63bf06de-d19e-76fa-bba5-523d194daa35.png)|
|:--|


**sshでアクセスするためにtcp/22を開ける設定をします。**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411926/05efc2bd-0c0e-30e3-3994-731c661faebe.png)|
|:--|


**詳細のところは特にいじらずに、作成を始めます。**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411926/cc2017e4-374c-3ed9-6915-bbb49594870b.png)|
|:--|


**数分で作成完了します。**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411926/7eabc8b5-0af7-7a1f-7865-5ff1b411e232.png)|
|:--|


# コンテナにssh接続してpythonスクリプトを叩いてみる

* 参考にした記事
    * https://qiita.com/miraijin/items/0c7bfbd70234967e87bd

```bash:実行結果
Linux SandboxHost-637756640658662370 5.4.81-microsoft-standard #1 SMP Thu Dec 3 23:47:24 UTC 2020 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@SandboxHost-637756640658662370:~# cd /opt/batch/
root@SandboxHost-637756640658662370:/opt/batch# python sample.py 
exec start

Uploading to Azure Storage as blob:
        sample_20211221151705153174.csv
exec end
```
**コンテナで作成したcsvファイルがストレージにUPされました。**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411926/f162909f-1c97-9083-b9f7-a08f1c556e5f.png)|
|:--|

# まとめ
結果として、ACIはバッチ処理基盤として十分使えそうだという感触が得られました。
（ジョブコントローラー製品がコンテナに対応していれば）
（JP1はDocker対応しているらしいので、使えそう・・・？）

また、ACIの立ち上げは初めて触ったわりにはさくっとできたので、結構使い勝手がよさそうに感じました。


# 今後やりたいこと
「今回はDockerイメージを予めローカルで作成→ACRへPushしてACIインスタンス立てる」という流れでしたが、これをGitHub Actionsで自動化したりする確認をしていきたいなと思います。
また、実際のジョブコントローラー製品と組み合わせて動作を確かめたいなと思っています。