MLflow on ECSとSageMakerをまるごと一発で作成する (aws-mlflow-sagemaker-cdk) 

# 内容

新しいCDKレポ (https://github.com/aws-samples/aws-mlflow-sagemaker-cdk) が出来ていて、SageMakerとMLflowを一発でDeployできそうなので試す。 

MLflow初であれば→[MLflow 基礎](https://qiita.com/nakamasato/items/6337e7f543535ecb9062)

# 準備

AWS CDKの設定が終わっている必要があるので、まだの方はこちらから

https://qiita.com/nakamasato/items/178984a622809261df20

# デプロイされるもの

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7059/62eaa08b-d105-c787-bd54-12075a4e838b.png)

- MLflow
    - MLflow用のVPC (SubnetsやPrivateLink、NatGatewayなどもろもろ)
    - ECS + Fargate
    - MLflowというECS Serviceで起動 (Nginx + mlflowコンテナ)
    - AuroraDBをMLflowのデータストアとして利用
    - S3をArtifactとして使用
- SageMaker
    - SageMaker用のVPC (Subnets、PrivateLinkへの)
    - SageMaker

# ステップ


## 1. CDKの準備

```bash
git clone https://github.com/aws-samples/aws-mlflow-sagemaker-cdk
cd aws-mlflow-sagemaker-cdk/cdk/singleAccount
npm install
npm run build
cdk bootstrap # すでにBootstrapしてあればいらない
```

## 2. Deploy　(MLflow + SageMaker) (20分くらい)

(profileで `default`を使う人は、 README通り `./deploy.sh`でよい。)

```bash
AWS_REGION=ap-northeast-1 cdk deploy --all --profile <プロファイル名>
```

ただし、MLflow UIは、Internalロードバランサーで作成されているので、アクセスは内部からしか出来ない。 (ここは、なんかしらの工夫が必要。 (VPNなど)

## 3. SageMaker確認
(省略‥)
## 4. Client VPNを設定 (MLflow UIにアクセスしなければ不要)

今回は、サーバ証明書・クライアント証明書による相互認証を使用します。

### 4-1. 証明書を作成・準備

```bash
git clone https://github.com/OpenVPN/easy-rsa.git
cd easy-rsa/easyrsa3
```

PKIのInit

```bash
./easyrsa init-pki
```

CA構築

```bash
± ./easyrsa build-ca nopass
```

<details><div>

```bash
Using SSL: openssl LibreSSL 2.8.3
Generating RSA private key, 2048 bit long modulus
...........+++
.........................+++
e is 65537 (0x10001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/Users/masato-naka/repos/OpenVPN/easy-rsa/easyrsa3/pki/ca.crt
```

</div></details>

サーバー証明書とキーを生成

```bash
± ./easyrsa build-server-full server nopass
```

<details><div>

```bash
Using SSL: openssl LibreSSL 2.8.3
Generating a 2048 bit RSA private key
........................................+++
............+++
writing new private key to '/Users/masato-naka/repos/OpenVPN/easy-rsa/easyrsa3/pki/easy-rsa-55399.BZw1sP/tmp.NsBTDg'
-----
Using configuration from /Users/masato-naka/repos/OpenVPN/easy-rsa/easyrsa3/pki/easy-rsa-55399.BZw1sP/tmp.zWNqlt
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'server'
Certificate is to be certified until Jan 24 00:46:30 2024 GMT (825 days)

Write out database with 1 new entries
Data Base Updated
```

</div></details>


クライアント証明書とキーを生成

```bash
± ./easyrsa build-client-full client1.domain.tld nopass
```

<details><div>

```bash
Using SSL: openssl LibreSSL 2.8.3
Generating a 2048 bit RSA private key
...............+++
.............+++
writing new private key to '/Users/masato-naka/repos/OpenVPN/easy-rsa/easyrsa3/pki/easy-rsa-55607.nyYDyK/tmp.P5DBSw'
-----
Using configuration from /Users/masato-naka/repos/OpenVPN/easy-rsa/easyrsa3/pki/easy-rsa-55607.nyYDyK/tmp.sft09h
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'client1.domain.tld'
Certificate is to be certified until Jan 24 00:46:45 2024 GMT (825 days)

Write out database with 1 new entries
Data Base Updated
```

</div></details>

ファイルを移動

```bash
± mkdir ~/cert_folder/
± cp pki/ca.crt ~/cert_folder/
cp pki/issued/server.crt ~/cert_folder/
cp pki/private/server.key ~/cert_folder/
cp pki/issued/client1.domain.tld.crt ~/cert_folder
cp pki/private/client1.domain.tld.key ~/cert_folder/
cd ~/cert_folder/
```

AWS ACMへ登録 (server)

```bash
aws acm import-certificate --certificate fileb://server.crt --private-key fileb://server.key --certificate-chain fileb://ca.crt --region ap-northeast-1
{
    "CertificateArn": "arn:aws:acm:ap-northeast-1:135493629466:certificate/xxxxxx"
}
```

AWS ACMへ登録 (client)

```bash
aws acm import-certificate --certificate fileb://client1.domain.tld.crt --private-key fileb://client1.domain.tld.key --certificate-chain fileb://ca.crt --region ap-northeast-1

{
    "CertificateArn": "arn:aws:acm:ap-northeast-1:135493629466:certificate/xxxxxxxx"
}
```

### 4-2. Client VPNを作成・設定

Client VPN endpointの作成 (Split Tunnelを指定することを奨励)

```bash
aws ec2 create-client-vpn-endpoint \
--client-cidr-block 10.10.0.0/22 \ # こちらは、Client VPNが配置されるCidrで、目的VPCのCidrとかぶってはいけない
--server-certificate-arn arn:aws:acm:ap-northeast-1:135493629466:certificate/xxxx \
--authentication-options Type=certificate-authentication,MutualAuthentication={ClientRootCertificateChainArn=arn:aws:acm:ap-northeast-1:135493629466:certificate/xxxx} \
--connection-log-options Enabled=false \
--split-tunnel
```

<details><div>

```
{
    "ClientVpnEndpointId": "cvpn-endpoint-xxxx",
    "Status": {
        "Code": "pending-associate"
    },
    "DnsName": "cvpn-endpoint-xxxx.prod.clientvpn.ap-northeast-1.amazonaws.com"
}
```

</div></details>

VPN endpointとSubnetを連携 (MLflowのSubnetは今回2つなので、2つ連携)

```bash
aws ec2 associate-client-vpn-target-network \
--client-vpn-endpoint-id cvpn-endpoint-xxxx\ # うえで取得したendpoint-id
--subnet-id subnet-xxxx1 # MLflowがいるSubnetを指定
```

```bash
aws ec2 associate-client-vpn-target-network \
--client-vpn-endpoint-id cvpn-endpoint-xxxx\ # うえで取得したendpoint-id
--subnet-id subnet-xxxx2 # MLflowがいるSubnetを指定
```

クライアントがアクセスできるようにAuthorizeする

```bash
aws ec2 authorize-client-vpn-ingress \
--client-vpn-endpoint-id cvpn-endpoint-xxx \
--target-network-cidr 10.0.0.0/16 \ # 今回はMLflow VPCのCidrをそのまま指定
--authorize-all-groups
{
    "Status": {
        "Code": "authorizing"
    }
}
```

設定完了

### 4-3. Client設定ファイルを作成

Client設定をAWSのコンソールからダウンロードする

ダウンロードしたファイルの以下の2つを変更:

1. ファイルの末尾にCerｔを加える

    ```
    <cert>
    ※最初に作成したclient1.domain.tld.crt(-----BEGIN CERTIFICATE-----～-----END CERTIFICATE-----)の内容※
    </cert>

    <key>
    ※最初に作成したclient1.domain.tld.key（-----BEGIN PRIVATE KEY-----～-----END PRIVATE KEY-----）の内容※
    </key>
    ```
1. DNSの前にランダムな文字列を追加

   `cvpn-endpoint-xxxx.prod.clientvpn.ap-northeast-1.amazonaws.com` -> `test.cvpn-endpoint-xxxx.prod.clientvpn.ap-northeast-1.amazonaws.com`

### 4-4. Client VPNのクライアントを設定

[https://aws.amazon.com/vpn/client-vpn-download/](https://aws.amazon.com/vpn/client-vpn-download/)からダウンロードして、インストール。

`ファイル>プロファイルを追加`から上で作成したClient設定ファイルをインポート

接続。

![スクリーンショット 2021-10-21 10.32.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7059/c445dc36-b32d-abed-f8a3-66788b3eaaed.png)


## 4. MLflowを確認

前のステップでClient VPNを設定していないとアクセスできない!
ローカルからVPN越しにinternal loadbalancerへ接続。 

![スクリーンショット 2021-10-21 10.40.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7059/193c471c-6859-3992-f192-f7df069c37bf.png)


## 5. Destroy MLflow + SageMaker

`aws-mlflow-sagemaker-cdk`のディレクトリに戻る

```bash
cd aws-mlflow-sagemaker-cdk/cdk/singleAccount
```

削除

```bash
AWS_REGION=ap-northeast-1 cdk destroy --all --profile <プロファイル名>
```

## 5. Delete Client VPN

２つのSubnetsをdisassociateする

```bash
aws ec2 disassociate-client-vpn-target-network \
--client-vpn-endpoint-id cvpn-endpoint-xxx \
--association-id cvpn-assoc-xxxxx
{
    "AssociationId": "cvpn-assoc-xxxx",
    "Status": {
        "Code": "disassociating"
    }
}
aws ec2 disassociate-client-vpn-target-network \
--client-vpn-endpoint-id cvpn-endpoint-xxx \
--association-id cvpn-assoc-xxxx 
{
    "AssociationId": "cvpn-assoc-xxxx",
    "Status": {
        "Code": "disassociating"
    }
}
```

client VPNを削除する

```bash
aws ec2 delete-client-vpn-endpoint \
--client-vpn-endpoint-id cvpn-endpoint-xxx
{
    "Status": {
        "Code": "deleting"
    }
}
```

acmを削除

```bash
aws acm delete-certificate --certificate-arn arn:aws:acm:ap-northeast-1:xxxx:certificate/xxxx # for server
aws acm delete-certificate --certificate-arn arn:aws:acm:ap-northeast-1:xxxx:certificate/xxxx # for client
```

# まとめ

- https://github.com/aws-samples/aws-mlflow-sagemaker-cdk をつかって一発でSageMakerとMLflowをデプロイ出来た
- Client VPNを使ってinternalに作成されたMLflow UIにもアクセスできるようになった <- このステップはマニュアルなコマンドが多いので、CDK作るかもっと簡単に作成できるようにしたい

最終的に出来たアーキテクチャ図はこちら (Client VPNを追加)

![スクリーンショット 2021-10-22 9.56.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7059/1329e442-472e-3b21-799f-8667e3f569cd.png)


# 参考
- https://github.com/aws-samples/aws-mlflow-sagemaker-cdk
- [Client VPNからのPrivate Subnetアクセス
](https://qiita.com/hamingcode/items/bc2546273386a2bd4539)
- https://fu3ak1.hatenablog.com/entry/2020/08/12/145243
- [AWS Client VPNでスプリットトンネルを有効にしてみる](https://dev.classmethod.jp/articles/aws-clientvpn-splittunnel/)
- https://github.com/OpenVPN/easy-rsa