AWS Lambda
Mix.install([
{:aws, "~> 0.13"},
{:ex_aws, "~> 2.5"},
{:ex_aws_sts, "~> 2.3"},
{:ex_aws_lambda, "~> 2.1"},
{:req, "~> 0.5"},
{:nx, "~> 0.9"},
{:stb_image, "~> 0.6"},
{:hackney, "~> 1.20"},
{:sweet_xml, "~> 0.7"},
{:kino, "~> 0.14"}
])
認証
access_key_id_input = Kino.Input.password("ACCESS_KEY_ID")
secret_access_key_input = Kino.Input.password("SECRET_ACCESS_KEY")
region_input = Kino.Input.text("REGION")
[
access_key_id_input,
secret_access_key_input,
region_input
]
|> Kino.Layout.grid(columns: 3)
client =
AWS.Client.create(
Kino.Input.read(access_key_id_input),
Kino.Input.read(secret_access_key_input),
Kino.Input.read(region_input)
)
auth_config = [
access_key_id: Kino.Input.read(access_key_id_input),
secret_access_key: Kino.Input.read(secret_access_key_input),
region: Kino.Input.read(region_input)
]
Kino.nothing()
アカウントIDの取得
account_id =
ExAws.STS.get_caller_identity()
|> ExAws.request!(auth_config)
|> then(& &1.body.account)
IAM ロールの定義
client
|> AWS.IAM.create_role(%{
"RoleName" => "lambda-resnet-role",
"AssumeRolePolicyDocument" =>
Jason.encode!(%{
"Statement" => [
%{
"Sid" => "STS202201051440",
"Effect" => "Allow",
"Principal" => %{
"Service" => ["lambda.amazonaws.com"]
},
"Action" => "sts:AssumeRole"
}
]
})
})
client
|> AWS.IAM.create_policy(%{
"PolicyName" => "lambda-resnet-role-policy",
"PolicyDocument" =>
Jason.encode!(%{
"Version" => "2012-10-17",
"Statement" => [
%{
"Effect" => "Allow",
"Action" => [
"cloudwatch:PutMetricData",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:CreateLogGroup",
"logs:DescribeLogStreams"
],
"Resource" => ["*"]
}
]
})
})
client
|> AWS.IAM.attach_role_policy(%{
"RoleName" => "lambda-resnet-role",
"PolicyArn" => "arn:aws:iam::#{account_id}:policy/lambda-resnet-role-policy"
})
ECRリポジトリー作成
region = Kino.Input.read(region_input)
image = "lambda-resnet"
fullname = "#{account_id}.dkr.ecr.#{region}.amazonaws.com/#{image}:latest"
client
|> AWS.ECR.create_repository(%{
"repositoryName" => image
})
client
|> AWS.ECR.describe_repositories(%{})
client
|> AWS.ECR.set_repository_policy(%{
"repositoryName" => image,
"policyText" =>
Jason.encode!(%{
"Statement" => [
%{
"Sid" => "ECR202201051440",
"Effect" => "Allow",
"Principal" => %{
"AWS" => ["*"]
},
"Action" => "ecr:*"
}
]
})
})
ECR リポジトリーへのイメージプッシュ
token =
client
|> AWS.ECR.get_authorization_token(%{})
|> elem(1)
|> Map.get("authorizationData")
|> Enum.at(0)
|> Map.get("authorizationToken")
[username, password] =
token
|> Base.decode64!()
|> String.split(":")
System.cmd(
"docker",
[
"login",
"--username",
username,
"--password",
password,
fullname
]
)
System.cmd(
"docker",
[
"build",
"-t",
image,
"/lambda/resnet"
]
)
System.cmd(
"docker",
[
"tag",
image,
fullname
]
)
System.cmd(
"docker",
[
"push",
fullname
]
)
client
|> AWS.ECR.list_images(%{
"repositoryName" => image
})
|> elem(1)
|> Map.get("imageIds")
|> Kino.DataTable.new()
Lambda関数の作成
client
|> AWS.Lambda.create_function(%{
"FunctionName" => "resnet",
"PackageType" => "Image",
"Code" => %{
"ImageUri" => fullname
},
"Role" => "arn:aws:iam::#{account_id}:role/lambda-resnet-role",
"MemorySize" => 1024,
"Timeout" => 900,
"Environment" => %{
"Variables" => %{
"LOG_LEVEL" => "debug"
}
}
})
AWS.Lambda.get_function(client, "resnet")
Lambda 関数呼出
binary =
"https://raw.githubusercontent.com/pjreddie/darknet/master/data/dog.jpg"
|> Req.get!()
|> Map.get(:body)
binary
|> StbImage.read_binary!()
|> StbImage.to_nx()
|> Kino.Image.new()
"resnet"
|> ExAws.Lambda.invoke(%{"Payload" => Base.encode64(binary)}, %{})
|> ExAws.request!(auth_config)
|> then(& &1["body"]["predictions"])
|> Kino.DataTable.new()
image_input = Kino.Input.image("INPUT_IMAGE", format: :jpeg)
binary =
image_input
|> Kino.Input.read()
|> Map.get(:file_ref)
|> Kino.Input.file_path()
|> File.read!()
"resnet"
|> ExAws.Lambda.invoke(%{"Payload" => Base.encode64(binary)}, %{})
|> ExAws.request!(auth_config)
|> then(& &1["body"]["predictions"])
|> Kino.DataTable.new()
ログ確認
log_stream_name =
client
|> AWS.CloudWatchLogs.describe_log_streams(%{
"logGroupName" => "/aws/lambda/resnet"
})
|> elem(1)
|> Map.get("logStreams")
|> Enum.at(-1)
|> Map.get("logStreamName")
client
|> AWS.CloudWatchLogs.get_log_events(%{
"logGroupName" => "/aws/lambda/resnet",
"logStreamName" => log_stream_name
})
|> elem(1)
|> Map.get("events")
|> Enum.map(& &1["message"])
Lambda関数削除
"resnet"
|> ExAws.Lambda.delete_function()
|> ExAws.request!(auth_config)
IAM ロール削除
client
|> AWS.IAM.detach_role_policy(%{
"RoleName" => "lambda-resnet-role",
"PolicyArn" => "arn:aws:iam::#{account_id}:policy/lambda-resnet-role-policy"
})
client
|> AWS.IAM.delete_policy(%{
"PolicyArn" => "arn:aws:iam::#{account_id}:policy/lambda-resnet-role-policy"
})
client
|> AWS.IAM.delete_role(%{
"RoleName" => "lambda-resnet-role"
})
ECR リポジトリー削除
image_ids =
client
|> AWS.ECR.list_images(%{
"repositoryName" => image
})
|> elem(1)
|> Map.get("imageIds")
client
|> AWS.ECR.batch_delete_image(%{
"repositoryName" => image,
"imageIds" => image_ids
})
client
|> AWS.ECR.delete_repository(%{
"repositoryName" => image
})