FTPサーバーもサーバーレスに
ファイルをFTPサーバーで受信するような旧来型のあるサービスをAWSに移行したときの話です。
移行後もFTPサーバーとしての EC2 を立てて…というのはなんだかなぁということで、S3 に配信することにしました。
このとき、まず仕組みとして考えたのが以下の構成。
- (HTTP) CloudFront → API Gateway → Lambda → S3
見様見真似で構築自体は問題なくいきましたが、配信元システムの仕様でHTTPでのファイル配信では運用できないことが分かりました。
そこでFTPで配信する仕組みを構築すべく、AWS Transfer Family を検討しました。
AWS Transfer Family
従来はSFTPのみサポートしていたマネージドサービスですが、今年からFTP/FTPSをサポートするようになりました。まさに僥倖。
※FTPやめようよ…
構築について詳しくはこちらを参考に。
AWS Transfer for SFTPがFTP/FTPSをサポートしました!
Cloud Formation でテンプレートから構築されます。
- Transfer for SFTP
- API Gateway
- Lambda
FTPの場合、認証はカスタムするしかないので、Secrets Manager を利用することにして、作成された Lambda から読み込むようにしました。
(Secrets Manager の読み込みはサンプルコードが生成されるのでそれを元に)
あとは、つくられた VPC のエンドポイントにアクセス確認。
問題なければ、配信元のシステムの設定も更新します。
これで無事、S3にFTP配信される仕組みが整いました。
配信されたファイルをフォルダに振り分ける
あとは元々、ファイル名から適切なフォルダに振り分ける(なければつくる)という処理が入っていました。
このままだとバケットのルートにファイルが溜まり続けるので、S3トリガーで動く Lambda を作成します。
トリガーは S3 ファイルPUT時。
ファイルを適切なフォルダに移動(コピー&削除)する処理を Lambda で記述します。
- Pythonでの例
import logging
import urllib.parse
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
try:
client = boto3.client('s3')
bucket_name = event['Records'][0]['s3']['bucket']['name']
src = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
## ここで対象ファイルのチェックなど
## 向き先のフォルダ名を決めるなど
dst = folder_name + '/' + src
client.copy_object(Bucket=bucket_name, Key=dst, CopySource={'Bucket': bucket_name, 'Key': src})
client.delete_object(Bucket=bucket_name, Key=src)
logger.info("moved file: from[" + src + "]" + ", to[" + dst + "]")
return True
except Exception as e:
logger.error(e)
return False
トリガーを「すべてのイベント時」ではなく PUT に限定することで、このファイル移動処理での COPY のときにも起動されないようにしています。
これで、サーバーレスなFTPサーバー(?)ができました。