file-typeでファイルのMIME Typeを取得する

概要

npmパッケージの file-type を利用して、ファイルのMIME Typeを取得します。

内容

  • パッケージのインストール
$ npm install file-type
  • ファイルから取得
const fileType = require('file-type')

(async () => {
  const ft = await fileType.fromFile('path/to/file')
  console.log(ft)
  // { ext: 'xxx', mime: 'xxx/xxx' }
})()
  • バッファから取得
const fs = require('fs')
const fileType = require('file-type')

(async () => {
  const ft = await fileType.fromBuffer(fs.readFileSync('path/to/file'))
  console.log(ft)
  // { ext: 'xxx', mime: 'xxx/xxx' }
})()
  • ストリームから取得
const fs = require('fs')
const fileType = require('file-type')

(async () => {
  const ft = await fileType.fromStream(fs.createReadStream('path/to/file'))
  console.log(ft)
  // { ext: 'xxx', mime: 'xxx/xxx' }
})()
  • URLから取得
const fileType = require('file-type')
// npm install node-fetch
const fetch = require('node-fetch')

(async () => {
  const res = fetch('https://placehold.jp/150x150.png')
  const ft = await fileType.fromStream(res.body)
  console.log(ft)
  // { ext: 'png', mime: 'image/png' }
})()

備考

CloudWatch Logsに出力したログをログストリーム名でフィルタする

概要

AWS ECSでコンテナのログをCloudWatch Logsに出力している場合、CloudWatch Logs Insightsを利用すると様々な条件でフィルタすることができる。 今回は、ログストリーム名でフィルタするクエリ構文を記載します。

クエリ構文

  • 対象のログストリームが1つの場合
fields @timestamp, @message
| filter @logStream = '${ログストリーム名}'
| sort @timestamp desc
| limit 100
  • 対象のログストリームが複数の場合
fields @timestamp, @message
| filter @logStream in ['${ログストリーム名1}', '${ログストリーム名2}', '${ログストリーム名3}', ...]
| sort @timestamp desc
| limit 100

参考

Lambda Layersで最新バーションのAWS SDKを利用できるようにする

はじめに

Lambdaで最新バージョンのAWS SDKを利用したい場合に、Lambda Layersを用いた方法を記載します。

AWS SDKのバージョンを確認する

利用されているAWS SDKのバージョンを確認しておきます。

  • Lambda Execution Roleの作成
$ aws iam create-role \
  --role-name lambda-ex \
  --assume-role-policy-document \
  file://trust-policy.json

trust-policy.jsonの内容は以下の通り。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

作成したIAM Roleに「AWSLambdaBasicExecutionRole」ポリシーをアタッチします。

$ aws iam attach-role-policy \
  --role-name lambda-ex \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
  • Lambda関数の作成

関数を作成して、ファイル名をindex.jsとして保存します。

const AWS = require('aws-sdk')

exports.handler = async (event) => {
  return AWS.VERSION
}

デプロイパッケージを作成します。

$ zip function.zip index.js

Lambda関数を作成します。

$ aws lambda create-function \
  --function-name get-aws-sdk-nodejs-version \
  --zip-file fileb://function.zip \
  --handler index.handler \
  --runtime nodejs14.x \
  --role <LAMBDA_EXEC_ROLE_ARN>
  • Lambda関数を実行します。
$ aws lambda invoke \
  --function-name get-aws-sdk-nodejs-version \
  --log-type Tail \
  version-before

$ cat version-before
"2.804.0"

Lambda Layerの作成

次に、最新バージョンのAWS SDKのLayerを作成します。

デプロイパッケージは、Lambda環境と互換性がある必要があり、Lambdaランタイムと一致するOSを利用することが推奨されているようです。

ここでは、amazonlinux2のdockerイメージを利用してデプロイパッケージを作成します。

  • dockerコンテナの起動
$ docker run -it --rm -v $(pwd):/tmp/aws-sdk-layer amazonlinux
  • デプロイパッケージの作成
# amazon-linux-extras install -y epel
# yum -y install nodejs zip
# mkdir nodejs
# cd nodejs
# npm install aws-sdk
# zip -r /tmp/aws-sdk-layer/package.zip ../nodejs
# exit
  • Lambda Layerの作成
aws lambda publish-layer-version \
  --layer-name aws-sdk-nodejs \
  --description "aws sdk nodejs 2.868.0" \
  --license-info "MIT" \
  --compatible-runtimes nodejs14.x \
  --zip-file fileb://./package.zip
  • Lambda関数にLayerを適用する
$ aws lambda update-function-configuration \
  --function-name get-aws-sdk-nodejs-version \
  --layers <LAYER_ARN>

Lambda Layerの適用を確認する

  • Lambda関数の実行
$ aws lambda invoke \
  --function-name get-aws-sdk-nodejs-version \
  --log-type Tail \
  version-after

$ cat version-after
"2.868.0"

参考

Amazon ECS Execを試してみる

はじめに

AWS Fargate上のコンテナに対して対話型のシェル、または、任意のコマンドを実行できるようになったようなので、試してみる。

手順などは、以下に記載のものをそのままなぞってます。

aws.amazon.com

AWS CLI v1のインストール

現時点では、AWS CLI v1のみの対応(v2も数週間以内に対応)のようなので、v1をインストールします。 すでにv2をインストール済みだったため、以下の記事を参考にして、v2とv1を共存させるようにしてます。

dev.classmethod.jp

$ curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
$ unzip awscli-bundle.zip
$ sudo ./awscli-bundle/install -i /usr/local/aws-cli-v1 -b /usr/local/bin/aws-v1
$ aws-cli/1.19.29 Python/3.8.0 Darwin/19.6.0 botocore/1.20.29

ECSタスクを起動する

上記の手順だと、VPCとそのVPCに含まれる2つのパブリックサブネットが存在していることが前提のようなので、作成しておきます。

$ aws-v1 ec2 create-vpc \
  --cidr-block 172.16.0.0/16
  • Internet Gatewayの作成とVPCへのアタッチ
$ aws-v1 ec2 create-internet-gateway
$ aws-v1 ec2 attach-internet-gateway \
  --internet-gateway-id <INTERNET_GATEWAY_ID> \
  --vpc-id <VPC_ID>
  • サブネットとRoute Tableの作成、その関連付け
$ aws-v1 ec2 create-subnet \
  --vpc-id  <VPC_ID>\
  --cidr-block 172.16.0.0/24 \
  --availability-zone ap-northeast-1a
$ aws-v1 ec2 create-subnet \
  --vpc-id <VPC_ID> \
  --cidr-block 172.16.1.0/24 \
  --availability-zone ap-northeast-1c
$ aws-v1 ec2 create-route-table \
  --vpc-id <VPC_ID>
$ aws-v1 ec2 create-route \
  --route-table-id <ROUTE_TABLE_ID> \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id <INTERNET_GATEWAY_ID>
$ aws-v1 ec2 associate-route-table \
  --route-table-id <ROUTE_TABLE_ID> \
  --subnet-id <SUBNET1_ID>
$ aws-v1 ec2 associate-route-table \
  --route-table-id <ROUTE_TABLE_ID> \
  --subnet-id <SUBNET2_ID>

ここからは、ほぼ手順の通りに。。。

  • KMSキー作成
$ aws-v1 kms create-key \
  --region=ap-northeast-1
$ aws-v1 kms create-alias \
  --alias-name alias/ecs-exec-demo-kms-key \
  --target-key-id <KMS_KEY_ID> \
  --region ap-northeast-1
$ aws-v1 ecs create-cluster \
  --cluster-name ecs-exec-demo-cluster \
  --region ap-northeast-1 \
  --configuration executeCommandConfiguration="{
    logging=OVERRIDE, \
    kmsKeyId=<KMS_KEY_ID>, \
    logConfiguration={ \
      cloudWatchLogGroupName="/aws/ecs/ecs-exec-demo", \
      s3BucketName=ecs-exec-demo-output-xxxxxxxxxx, \
      s3KeyPrefix=exec-output \
    } \
  }"
  • CloudWatch ロググループの作成
$ aws-v1 logs create-log-group \
  --log-group-name /aws/ecs/ecs-exec-demo \
  --region ap-northeast-1
$ aws-v1 s3api create-bucket \
  --bucket ecs-exec-demo-output-xxxxxxxxxx \
  --region ap-northeast-1 \
  --create-bucket-configuration LocationConstraint=ap-northeast-1
  • セキュリティグループの作成
$ aws-v1 ec2 create-security-group \
  --group-name ecs-exec-demo-SG \
  --description "ECS exec demo SG" \
  --vpc-id <VPC_ID> \
  --region ap-northeast-1
$ aws-v1 ec2 authorize-security-group-ingress \
  --group-id <SECURITY_GROUP_ID> \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0 \
  --region ap-northeast-1

TCP80番ポートへのアクセスを許可しています。

  • IAMロールの作成
$ aws-v1 iam create-role \
  --role-name ecs-exec-demo-task-execution-role \
  --assume-role-policy-document file://ecs-tasks-trust-policy.json \
  --region ap-northeast-1

$ aws-v1 iam create-role \
  --role-name ecs-exec-demo-task-role \
  --assume-role-policy-document file://ecs-tasks-trust-policy.json \
  --region ap-northeast-1

ECS Task Execution RoleとECS Task Roleをそれぞれ作成しています。

ecs-tasks-trust-policy.jsonの内容は以下の通り。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "ecs-tasks.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
  • ECS Task Execution Roleへポリシーを追加
$ aws-v1 iam attach-role-policy \
  --role-name ecs-exec-demo-task-execution-role \
  --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
  • ECS Task Roleへ権限を追加
$ aws-v1 iam put-role-policy \
  --role-name ecs-exec-demo-task-role \
  --policy-name ecs-exec-demo-task-role-policy \
  --policy-document file://ecs-exec-demo-task-role-policy.json

ecs-exec-demo-task-role-policy.jsonの内容は以下の通り

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssmmessages:CreateControlChannel",
        "ssmmessages:CreateDataChannel",
        "ssmmessages:OpenControlChannel",
        "ssmmessages:OpenDataChannel"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:DescribeLogGroups"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:DescribeLogStreams",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:ap-northeast-1:<AWS_ACCOUNT_ID>:log-group:/aws/ecs/ecs-exec-demo:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::ecs-exec-demo-output-xxxxxxxxxx/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetEncryptionConfiguration"
      ],
      "Resource": "arn:aws:s3:::ecs-exec-demo-output-xxxxxxxxxx"
    },
    {
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt"
      ],
      "Resource": <KMS_KEY_ARN>
    }
  ]
}
  • ECS タスク定義の追加
$ aws-v1 ecs register-task-definition \
  --cli-input-json file://ecs-exec-demo.json \
  --region ap-northeast-1

ecs-exec-demo.jsonの内容は以下の通り。

{
  "family": "ecs-exec-demo",
  "networkMode": "awsvpc",
  "executionRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ecs-exec-demo-task-execution-role",
  "taskRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ecs-exec-demo-task-role",
  "containerDefinitions": [
    {
      "name": "nginx",
      "image": "nginx",
      "linuxParameters": {
        "initProcessEnabled": true
      },
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/aws/ecs/ecs-exec-demo",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "container-stdout"
        }
      }
    }
  ],
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "cpu": "256",
  "memory": "512"
}
  • ECSタスクの起動、その確認
$ aws-v1 ecs run-task \
  --cluster ecs-exec-demo-cluster  \
  --task-definition ecs-exec-demo \
  --network-configuration awsvpcConfiguration=" \
    { \
      subnets=[<SUBNET1_ID>, <SUBNET2_ID>], \
      securityGroups=[<SECURITY_GROUP_ID>], \
      assignPublicIp=ENABLED \
    }" \
  --enable-execute-command \
  --launch-type FARGATE \
  --tags key=environment,value=production \
  --platform-version '1.4.0' \
  --region ap-northeast-1

$ aws-v1 ecs describe-tasks \
  --cluster ecs-exec-demo-cluster \
  --region ap-northeast-1 \
  --tasks <TASK_ID>
  • nginxへのアクセス

ECSタスクに設定されたパブリックをIPを取得して、

$ aws-v1 ec2 describe-network-interfaces \
  --network-interface-ids <NETWORK_INTERFACE_ID>

アクセスすると nginxのデフォルトのページが取得できます。

$ curl http://<PUBLIC_IP>

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

ECS Execで対話型のシェルを実行する

aws-v1 ecs execute-command  \
  --region ap-northeast-1 \
  --cluster ecs-exec-demo-cluster \
  --task <TASK_ID> \
  --container nginx \
  --command "/bin/bash" \
  --interactive

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.


Starting session with SessionId: ecs-execute-command-xxxxxxxxxxxxxxxxx
This session is encrypted using AWS KMS.
root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx:/# hostname
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx

root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx:/# whoami
root

root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx:/# ls
bin   docker-entrypoint.d   home   managed-agents  opt   run   sys  var
boot  docker-entrypoint.sh  lib    media       proc  sbin  tmp
dev   etc           lib64  mnt         root  srv   usr

root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx:/# echo "This page has been created with ECS Exec" > /usr/share/nginx/html/index.html

root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx:/# exit
exit


Exiting session with sessionId: ecs-execute-command-xxxxxxxxxxxxxxxxx

ECS Execで任意のコマンドを実行する

$ aws-v1 ecs execute-command  \
  --region ap-northeast-1 \
  --cluster ecs-exec-demo-cluster \
  --task <TASK_ID> \
  --container nginx \
  --command "ls" \
  --interactive

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.


Starting session with SessionId: ecs-execute-command-xxxxxxxxxxxxxxxxx
This session is encrypted using AWS KMS.
bin   docker-entrypoint.d   home   managed-agents  opt   run   sys  var
boot  docker-entrypoint.sh  lib    media       proc  sbin  tmp
dev   etc           lib64  mnt         root  srv   usr


Exiting session with sessionId: ecs-execute-command-xxxxxxxxxxxxxxxxx

作成したリソースを削除する

$ aws-v1 ecs stop-task \
  --cluster ecs-exec-demo-cluster \
  --region ap-northeast-1 \
  --task <TASK_ID>

$ aws-v1 ecs delete-cluster \
  --cluster ecs-exec-demo-cluster \
  --region ap-northeast-1

$ aws-v1 logs delete-log-group \
  --log-group-name /aws/ecs/ecs-exec-demo \
  --region ap-northeast-1

$ aws-v1 s3 rm s3://ecs-exec-demo-output-xxxxxxxxxx \
  --recursive

$ aws-v1 s3api delete-bucket \
  --bucket ecs-exec-demo-output-xxxxxxxxxx

$ aws-v1 iam detach-role-policy \
  --role-name ecs-exec-demo-task-execution-role \
  --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"

$ aws-v1 iam delete-role \
  --role-name ecs-exec-demo-task-execution-role

$ aws iam delete-role-policy \
  --role-name ecs-exec-demo-task-role \
  --policy-name ecs-exec-demo-task-role-policy

$ aws-v1 iam delete-role \
  --role-name ecs-exec-demo-task-role

$ aws-v1 kms schedule-key-deletion \
  --key-id <KMS_KEY_ID> \
  --region ap-northeast-1 \
  --pending-window-in-days 7

$ aws-v1 kms delete-alias \
  --alias-name alias/ecs-exec-demo-kms-key

$ aws-v1 ec2 delete-security-group \
  --group-id <SECURITY_GROUP_ID> \
  --region ap-northeast-1

$ aws-v1 ec2 disassociate-route-table \
  --association-id <SUBNET1_ASSOCIATION_ID>

$ aws-v1 ec2 disassociate-route-table \
  --association-id <SUBNET2_ASSOCIATION_ID>

$ aws-v1 ec2 delete-route-table \
  --route-table-id <ROUTE_TABLE_ID>

$ aws-v1 ec2 delete-subnet \
  --subnet-id <SUBNET1_ID>

$ aws-v1 ec2 delete-subnet \
  --subnet-id <SUBNET2_ID>

$ aws-v1 ec2 detach-internet-gateway \
  --internet-gateway-id <INTERNET_GATEWAY_ID> \
  --vpc-id <VPC_ID>

$ aws-v1 ec2 delete-internet-gateway \
  --internet-gateway-id <INTERNET_GATEWAY_ID>

$ aws-v1 ec2 delete-vpc \
  --vpc-id <VPC_ID>

さいごに

ECS Execを試すところより、ECSタスクを起動する手順のほうが多かったですが、ECS Fargate上のコンテナにアクセスできることが確認できました。

環境構築時や開発中など、Fargate上のコンテナに簡単にアクセスできればなぁと思うことも、それなりあったので、今後は活用できそうです。

AWS CLIでページ分割を無効にする

概要

AWS CLI バージョン2では、コマンドラインオプション、config、環境変数などを利用して、ページ分割を無効化できる

ページ分割の無効化

$ aws ec2 describe-images --owners self amazon --no-cli-pager
  • configで指定する場合

configファイル(~/.aws/config)にcli_pagerの指定を追加

[default]
region = ap-northeast-1
cli_pager =
$ aws ec2 describe-images --owners self amazon
$ AWS_PAGER= aws ec2 describe-images --owners self amazon

参照