Amazon Web Services
2019/10/21更新
AWS SAMのCLIであるaws-sam-cliを使うとローカルでLambda環境を作ってテストし、それをAWS上にデプロイできる。ここではaws-sam-cliのインストールからローカルでのテスト、AWS上にデプロイするまでの手順を示す。
aws-sam-cliをインストールするOSはUbuntu 18.04とする。
AWS Serverless Application Model (SAM) コマンドラインインターフェイス。サーバレスアプリケーションをローカルで構築、テスト、デバッグする (Amazon Web Services ブログ)
aws-sam-cliのインストールと動作確認
動作環境
aws-sam-cliを使うにあたりあらかじめ以下をインストールしておく。
Python 3.x
pip
Docker
AWS CLI
$ python --version Python 3.6.8 $ pip --version pip 19.3.1 from /usr/local/lib/python3.6/dist-packages/pip (python 3.6) $ docker --version Docker version 18.09.7, build 2d0083d $ aws --version aws-cli/1.16.263 Python/3.6.8 Linux/4.15.0-65-generic botocore/1.12.253
aws-sam-cliインストール
aws-sam-cliはpipで簡単にインストールできる。
$ sudo pip install aws-sam-cli $ sam --version SAM CLI, version 0.22.0
サンプルアプリケーション作成(Python3.6)
aws-sam-cliがインストールできたらサンプルアプリケーションを作成してみる。
Lambdaは様々な言語で記述できるが、ここではPython3.6のサンプルアプリケーションを作成する。
$ sam init --runtime python3.6 --name sam-python [+] Initializing project structure... Project generated: ./sam-python Steps you can take next within the project folder =================================================== [*] Invoke Function: sam local invoke HelloWorldFunction --event event.json [*] Start API Gateway locally: sam local start-api Read sam-python/README.md for further instructions [*] Project initialization is now complete $ tree sam-python sam-python ├── README.md ├── events │ └── event.json ................. Lambdaに渡すイベントの定義 ├── hello_world │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ └── app.cpython-36.pyc │ ├── app.py ..................... Lambda関数本体 │ └── requirements.txt ├── template.yaml .................. SAMテンプレート └── tests └── unit ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ └── test_handler.cpython-36.pyc └── test_handler.py 6 directories, 12 files
単独実行
アプリケーションができたら実行してみる。
$ cd sam-python $ sam local invoke HelloWorldFunction --event events/event.json --log-file lambda.log Invoking app.lambda_handler (python3.6) 2019-10-20 23:13:06 Found credentials in shared credentials file: ~/.aws/credentials Fetching lambci/lambda:python3.6 Docker container image...(略)... Mounting ~/sam-python/hello_world as /var/task:ro,delegated inside runtime container
実行結果はログファイルに出力される。
$ cat lambda.log START RequestId: 6eab94c3-2835-4241-9b97-b94b2bd60f2f Version: $LATEST END RequestId: 6eab94c3-2835-4241-9b97-b94b2bd60f2f REPORT RequestId: 6eab94c3-2835-4241-9b97-b94b2bd60f2f Duration: 0 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 19 MB {"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
--eventで指定したイベントの定義は以下のようにして作成できる。
$ sam local generate-event [OPTIONS] COMMAND [ARGS] > event.json
COMMANDには様々なAWSのサービスが指定でき、それぞれARGSが異なるのでhelpを参照する。
$ sam local generate-event --help : Commands: alexa-skills-kit alexa-smart-home apigateway batch cloudformation cloudfront cloudwatch codecommit codepipeline cognito config connect dynamodb kinesis lex rekognition s3 ses sns sqs stepfunctions
(S3用イベントの場合)
$ sam local generate-event s3 --help : Commands: delete Generates an Amazon S3 Delete Event put Generates an Amazon S3 Put Event $ sam local generate-event s3 put --help : $ sam local generate-event s3 put --help Usage: sam local generate-event s3 put [OPTIONS] Options: --region TEXT Specify the region name you'd like, otherwise the default = us-east-1 --partition TEXT Specify the partition name you'd like, otherwise the default = aws --bucket TEXT Specify the bucket name you'd like, otherwise the default = example-bucket --key TEXT Specify the key name you'd like, otherwise the default = test/key --debug Turn on debug logging to print debug message generated by SAM CLI. --help Show this message and exit.
API Gateway経由の実行
次にAPI Gateway経由で実行する。まずはAPI Gatewayを作成してリクエストを待ち受ける。
$ sam local start-api --log-file lambda.log Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET] You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template 2019-10-20 23:15:33 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
この状態で別ターミナルからcurlでアクセスするとAPI Gateway経由でLambdaが起動する。
$ curl http://127.0.0.1:3000/hello {"message": "hello world"}
元ターミナルには以下のように出力される。
Invoking app.lambda_handler (python3.6) 2019-10-20 23:15:54 Found credentials in shared credentials file: ~/.aws/credentials Fetching lambci/lambda:python3.6 Docker container image...... Mounting /home/xxxxx/sam-python/hello_world as /var/task:ro,delegated inside runtime container No Content-Type given. Defaulting to 'application/json'. 2019-10-20 23:15:59 127.0.0.1 - - [20/Oct/2019 23:15:59] "GET /hello HTTP/1.1" 200 - -> Ctrl + cで中断
ログファイルには以下のように記録される。
$ cat lambda.log START RequestId: 7b951478-46f9-427a-a9fc-5c2ac8b9966f Version: $LATEST END RequestId: 7b951478-46f9-427a-a9fc-5c2ac8b9966f REPORT RequestId: 7b951478-46f9-427a-a9fc-5c2ac8b9966f Duration: 0 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 19 MB
またLambdaはDockerコンテナ上で実行されるのでDockerイメージがダウンロードされている。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE lambci/lambda python3.6 2659a569a6df 2 weeks ago 866MB
ローカルエンドポイント経由の実行
ローカルエンドポイントを作成してAWS CLIから実行することもできる。
$ sam local start-lambda --log-file lambda.log Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint. 2019-10-21 00:12:59 * Running on http://127.0.0.1:3001/ (Press CTRL+C to quit)
この状態で別ターミナルからAWS CLIでLambdaを実行させる。
$ aws lambda invoke --function-name HelloWorldFunction --endpoint-url http://127.0.0.1:3001 out.txt { "StatusCode": 200 } $ cat out.txt {"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
元ターミナルには以下のように出力される。
Invoking app.lambda_handler (python3.6) 2019-10-21 00:14:35 Found credentials in shared credentials file: ~/.aws/credentials Fetching lambci/lambda:python3.6 Docker container image...... Mounting /home/xxxxx/sam-python/hello_world as /var/task:ro,delegated inside runtime container 2019-10-21 00:14:40 127.0.0.1 - - [21/Oct/2019 00:14:40] "POST /2015-03-31/functions/HelloWorldFunction/invocations HTTP/1.1" 200 - -> Ctrl + cで中断
ログファイルには以下のように記録される。
$ cat lambda.log START RequestId: c3da9704-d7a4-45bf-b484-a0fd274c8036 Version: $LATEST END RequestId: c3da9704-d7a4-45bf-b484-a0fd274c8036 REPORT RequestId: c3da9704-d7a4-45bf-b484-a0fd274c8036 Duration: 0 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 19 MB
Node.jsのサンプルアプリケーション
他にも例えばNode.jsのサンプルアプリケーションを作成する場合も--runtimeの指定を変えるだけでよい。
$ sam init --runtime nodejs --name sam-nodejs [+] Initializing project structure... Project generated: ./sam-nodejs Steps you can take next within the project folder =================================================== [*] Invoke Function: sam local invoke HelloWorldFunction --event event.json [*] Start API Gateway locally: sam local start-api Read sam-nodejs/README.md for further instructions [*] Project initialization is now complete $ tree sam-nodejs sam-nodejs ├── README.md ├── events │ └── event.json ................. Lambdaに渡すイベントの定義 ├── hello-world │ ├── app.js ..................... Lambda関数本体 │ ├── package.json │ └── tests │ └── unit │ └── test-handler.js └── template.yaml .................. SAMテンプレート 4 directories, 6 files
Lambdaの実行方法は先のPythonの時と同様なので割愛する。
AWS上にデプロイ
AWS上にデプロイ
ローカルで動作確認できたのでCloudFormationでAWS上にデプロイする。
まずテンプレートの正当性を確認する。
$ sam validate 2019-10-21 01:14:36 Found credentials in shared credentials file: ~/.aws/credentials /home/xxxxx/sam-python/template.yaml is a valid SAM Template
問題がなければ上記のように「valid」になるのでS3バケットにアップロードする。ここではバケット名を「l-w-i.lambda」とする。
$ aws s3 mb s3://l-w-i.lambda $ sam package --template-file template.yaml --s3-bucket l-w-i.lambda --output-template-file packaged.yaml Uploading to c0e3fc815ba0ece850ad243e5994a832 1897 / 1897.0 (100.00%) Successfully packaged artifacts and wrote output template to file packaged.yaml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /home/<user>/sam-python/packaged.yaml --stack-name <YOUR STACK NAME>
アップロードが終わったらデプロイを行う。
$ sam deploy --template-file packaged.yaml --stack-name sam-python --capabilities CAPABILITY_IAM Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - sam-python
これによりCloudFormationのスタックが作成され、Lambda関数がデプロイされる。
$ aws cloudformation describe-stacks --stack-name sam-python --query 'Stacks[].Outputs[1]' [ { "OutputKey": "HelloWorldApi", "OutputValue": "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/", "Description": "API Gateway endpoint URL for Prod stage for Hello World function" } ]
API Gatewayのエンドポイントも作成されるのでcurlでアクセスしてみる。
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello {"message": "hello world"}
Lambdaが実行され、CloudWatch Logsにも実行ログが記録される。
デプロイしたLambda関数を削除する場合はCloudFormationのスタックを削除する。S3バケットとCloudWatch Logsのロググループは削除されないので別途削除する。
$ aws cloudformation delete-stack --stack-name sam-python
参考サイト
AWS SAM情報 (Qiita)
2018/12/30更新
対応バージョン: 1.10.33
S3にアクセスする際の署名バージョンには2と4があるが、AWS CLIの古いバージョンで署名バージョン4のみサポートするリージョンのS3バケットにアクセスすると以下のようなエラーになる。(この例ではフランクフルトリージョンのS3バケットにアクセス)
% aws --version aws-cli/1.10.33 Python/2.7.14 Linux/4.14.72-68.55.amzn1.x86_64 botocore/1.4.23 % aws s3 cp test.txt s3://sample-eu-central-1/ upload failed: ./test.txt to s3://sample-eu-central-1/test.txt A client error (PermanentRedirect) occurred when calling the PutObject operation: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint: sample-eu-central-1.s3.eu-central-1.amazonaws.com You can fix this issue by explicitly providing the correct region location using the --region argument, the AWS_DEFAULT_REGION environment variable, or the region variable in the AWS CLI configuration file. You can get the bucket's location by running "aws s3api get-bucket-location --bucket BUCKET". % aws s3api get-bucket-location --bucket sample-eu-central-1 { "LocationConstraint": "eu-central-1" }
これは古いバージョンのAWS CLIがデフォルトで署名バージョン2でS3にアクセスするために起こる事象なので、CLI実行時にリージョンを明示的に指定するか、AWS CLIをアップデートすれば起こらなくなる。
CLI実行時にリージョンを指定する場合
% aws --region eu-central-1 s3 cp test.txt s3://sample-eu-central-1/ upload: ./test.txt to s3://sample-eu-central-1/test.txt
AWS CLIをアップデートする場合
% pip install --upgrade awscli % aws --version aws-cli/1.16.81 Python/2.7.14 Linux/4.14.72-68.55.amzn1.x86_64 botocore/1.12.71 % aws s3 cp test.txt s3://sample-eu-central-1/ upload: ./test.txt to s3://sample-eu-central-1/test.txt
参考サイト
AWS CLIドキュメント (Amazon Web Services)
AWSのリージョンとエンドポイント (Amazon Web Services)
Using the AWS SDKs, CLI, and Explorers (Amazon Web Services)
2019/06/08更新
対応バージョン: 1.11.117
AWS CLIでLambda関数のソースコードをダウンロードする手順を示す。まずget-functionコマンドにてLambdaのソースコードが収められたURL("Location": ...)とともにLambda関数の各種設定情報を取得する。
$ aws lambda get-function --function-name <Lambda関数名> { "Code": { "RepositoryType": "S3", "Location": "https://awslambda-..." }, :
get-function — AWS CLI Command Reference
次にこのURLに対してcurl等でコンテンツを取り出す。以下のようにすればzip圧縮されたソースコード一式をダウンロードできる。
$ func="<Lambda関数名>" $ url=$(aws lambda get-function --function-name ${func} | jq -r '.Code.Location') $ curl -o lambda.zip $url
尚、Lambda関数がバージョン管理されている場合、最新のバージョンをダウンロードするのであれば上記の手順でよいが、特定のバージョンのソースコードをダウンロードするにはまずlist-versions-by-functionコマンドにて保存されているバージョンを確認した後、取得したいバージョンを--qualifierオプションで指定してget-functionコマンドを実行すればよい。
$ aws lambda list-versions-by-function --function-name <Lambda関数名> { "Versions": [ { : "Version": "$LATEST", : "LastModified": "2019-04-24T05:24:34.064+0000", : }, { : "Version": "1", : "LastModified": "2019-03-14T02:08:41.267+0000", : }, { : "Version": "2", : "LastModified": "2019-03-12T02:18:20.732+0000", } : ] } $ aws lambda get-function --function-name <Lambda関数名> --qualifier <バージョン番号>
2019/10/11更新
対応バージョン: ruby-mqtt 0.5.0
AWS IoTで証明書を作成し、これを使ってRubyのMQTTクライアントからAWS IoTに接続しようとするとエラーになる。
コード
$ vi mqtt.rb require 'rubygems' require 'mqtt' client = MQTT::Client.connect( host: 'xxxxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com', port: 8883, ssl: true, cert_file: '<AWS IoTで作成した「このモノの証明書」>', key_file: '<AWS IoTで作成した「プライベートキー」>', ca_file: '<AWS IoTの「ルートCA ダウンロード」のページから取得したルートCA証明書>') :
実行結果
$ ruby mqtt.rb : SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate) (OpenSSL::SSL::SSLError)
これは既にレガシー扱いになったVeriSign発行のルートCA証明書を使っているからであり、Amazon Trust Services発行のルートCA証明書を使えば問題なく動作する。
関連資料・記事
[Amazon Web Services] AWS IoTでMQTTブローカーを作りArduinoからのセンシングデータをSNS通知する手順
[Amazon Web Services] AWS IoTでMQTTブローカーを作りArduinoからのセンシングデータをSNS通知する手順(AWS CLI編)
参考サイト
AWS IoT (Amazon Web Services)
AWS IoT開発者ガイド (Amazon Web Services)
IoT – 特集カテゴリー (DevelopersIO)
AWS IoT情報 (Qiita)
2017/06/11更新
以下の記事でAWS IoTを使ったセンシングデータのSNS通知手順を示したが、AWS IoTの設定をGUI(マネジメントコンソール)でなくCLIで行う手順を示す。AWS IoT以外の設定は変わらないので割愛する。
関連資料・記事
Amazon SNS
Topic作成
% aws sns create-topic --name "mqtt_test" { "TopicArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:mqtt_test" }
通知先(Email)設定
% aws sns subscribe --topic-arn "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:mqtt_test" --protocol email --notification-endpoint "xxx@yyy.com" { "SubscriptionArn": "pending confirmation" }
確認用のメールが届くので「Confirm subscription」をクリックしてこのメールアドレスを有効にするか、「Confirm subscription」のURLからToken="******"部分を抜き出してCLIで有効化する。
% aws sns confirm-subscription --topic-arn "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:mqtt_test" --token "*******************************************" { "SubscriptionArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:mqtt_test:************************************" }
AWS IoT
ArduinoをThingとして登録
aws iot create-thing --thing-name "Arduino" { "thingArn": "arn:aws:iot:ap-northeast-1:xxxxxxxxxxxx:thing/Arduino", "thingName": "Arduino" }
センシングデータを処理するルールを設定
最初にルールにひも付けるIAMロールを作成する。
% vi role.json { "Version":"2012-10-17", "Statement":[{ "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" }] } % aws iam create-role --role-name "mqtt_test_role" --assume-role-policy-document file://role.json { "Role": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" } } ] }, "RoleId": "*********************", "CreateDate": "2017-06-10T04:29:40.209Z", "RoleName": "mqtt_test_role", "Path": "/", "Arn": "arn:aws:iam::xxxxxxxxxxxx:role/mqtt_test_role" } }
ロールが作成できたらルールを作成してひも付ける。
% vi rule.json { "sql": "SELECT * FROM 'topic/test'", "ruleDisabled": false, "actions": [{ "sns": { "targetArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:mqtt_test", "roleArn": "arn:aws:iam::xxxxxxxxxxxx:role/mqtt_test_role" } }] } % aws iot create-topic-rule --rule-name "sendEmail" --topic-rule-payload file://rule.json
ポリシーを作成
% vi policy.json { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["iot:*"], "Resource": ["*"] }] } % aws iot create-policy --policy-name "mqtt_policy" --policy-document file://policy.json { "policyName": "mqtt_policy", "policyArn": "arn:aws:iot:ap-northeast-1:xxxxxxxxxxxx:policy/mqtt_policy", "policyDocument": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [{\n \"Effect\": \"Allow\",\n \"Action\": [\"iot:*\"],\n \"Resource\": [\"*\"]\n }]\n}\n", "policyVersionId": "1" }
デバイス証明書の作成と有効化
% aws iot create-keys-and-certificate --set-as-active --certificate-pem-outfile cert.pem --private-key-outfile thing-private-key.pem { "certificateArn": "arn:aws:iot:ap-northeast-1:xxxxxxxxxxxx:cert/********************************", "certificatePem": "-----BEGIN CERTIFICATE-----\n******\n-----END CERTIFICATE-----\n", "keyPair": { "PublicKey": "-----BEGIN PUBLIC KEY-----\n******\n-----END PUBLIC KEY-----\n", "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\n******\n-----END RSA PRIVATE KEY-----\n" }, "certificateId": "********************************************************" }
ポリシーおよびThingをデバイス証明書にアタッチ
% aws iot attach-principal-policy --policy-name "mqtt_policy" --principal "arn:aws:iot:ap-northeast-1:xxxxxxxxxxxx:cert/****************************************************************" % aws iot attach-thing-principal --thing-name "Arduino" --principal "arn:aws:iot:ap-northeast-1:xxxxxxxxxxxx:cert/****************************************************************"
参考サイト
AWS IoT (Amazon Web Services)
AWS IoT開発者ガイド (Amazon Web Services)
AWS IoTとRaspberry PiではじめるIoT超入門 (CodeZine)
AWS IoT情報 (Qiita)
2017/06/09更新
AWS IoTでMQTTブローカーを作り、ArduinoでセンシングしたデータをPub/Sub通信してAmazon SNSでメール通知する手順を示す。データの流れは以下の通り。
作業の流れは以下のようになる。
Amazon SNS
Topic作成
通知先(Email)設定
AWS IoT
ArduinoをThingとして登録
センシングデータを処理するルールを設定
ポリシーを作成
デバイス証明書の作成と有効化
ポリシーおよびThingをデバイス証明書にアタッチ
ここではAWS IoTの設定はGUI(マネジメントコンソール)を使う。CLIを使った場合の手順は以下に示す。
関連資料・記事
Arduino
センシング用の回路を作成
Arduinoでセンシングを行いAWS IoT上のMQTTブローカーにPublishするプログラムを用意
テスト
尚ここで使用するArduinoはUNOとするが、直接IP通信ができないため母艦としてLinux(Ubuntu 16.04)を使用し、Pub/Sub通信やセンシングを行うプログラムはRubyで作成する。
Amazon SNS
まずセンサーデータを通知する通知先を作成する。手順としては最初にTopicを作成してそれに実際のメールアドレスをひも付ける。
Topic作成
Amazon SNSのダッシュボードから[Topics] > [Create new topic]をクリックし、Topic nameに任意の名前を付けてTopicを作成する。(ここでは「mqtt_test」とする)
メールアドレスのひも付け
作成したTopicにチェックを付け[Actions] > [Subscribe to topic]をクリックする。
Protocolを「Email」としてEndpointに通知先のメールアドレスを指定する。
確認用のメールが届くので「Confirm subscription」をクリックしてこのメールアドレスを有効にする。
ダッシュボードの[Subscriptions]をクリックすると通知先が作成されているのが確認できる。
AWS IoT
通知先が作成できたらAWS IoTの設定に移る。
流れとしてはまずAWS IoT上にArduinoをThingとして登録し、次にセンシングデータを処理するルールを設定したうえでデバイス証明書とポリシーを作成してArduinoにひも付ける。
Thing作成
AWS IoTのダッシュボードから[Registry] > [Things]をクリックして[Create]をクリックする。
Thingに任意の名前を付けて[Create thing]をクリックする。(ここでは「Arduino」とする)
センシングデータを処理するルールを設定
[Rules]をクリックして[Create]をクリックする。
ルールに任意の名前を付け(ここでは「sendEmail」とする)、Message source欄のAttributeに取得対象として全てのデータを表す「*」を、Topic filterに購読するトピック(ここでは「topic/test」とする)をそれぞれ指定して[Add action]をクリックする。
アクションを選択する画面が表示されるので「Send a message as an SNS push notification」を選択し[Configure action]をクリックする。
「SNS target」で先ほど作成したTopic(ここでは「mqtt_test」)を選択し、「IAM role name」で[Create a new role]をクリックする。
IAMロール名を入力できるようになるので任意の名前を付け(ここでは「mqtt_test_role」とする)、[Create a new role]をクリックする。
「Configure action」画面に戻るので[Add action]をクリックする。
「Create a rule」画面で[Create rule]をクリックするとアクションが作成され、ルールの作成が完了する。
ポリシー作成
[Security] > [policies]をクリックし、[Create]をクリックする。
Nameに任意のポリシー名を入力し(ここでは「mqtt_policy」とする)、Thingからの操作を全て許可することとして[Action]に「iot:*」、[Resource ARN]に「*」を入力して[Effect]の[Allow]をチェックして[Create]をクリックする。これでポリシーが作成される。
デバイス証明書作成
[Security] > [Certificates]をクリックし、[Create]をクリックする。
一番上の[Create certificate]をクリックする。
証明書と鍵ペアが作成されるのでこのうち証明書(A certificate for this thing: xxxxx.cert.pem)と秘密鍵(A private key: xxxxx.private.key)をダウンロードして[Activate]をクリックする。これで証明書が有効になる。
ルート証明書も必要になるので入手しておく。この作業はAWS IoTのコンソールでできないのでコマンドで実行する。
% wget https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem -O rootCA.pem
ポリシーおよびThingをデバイス証明書にアタッチ
[Security] > [Certificates]をクリックし、先ほど作成した証明書の「・・・」をクリックして[Attach policy]を選択する。
作成済ポリシーをチェックして[Attach]をクリックする。
同様に先ほど作成した証明書の「・・・」をクリックして[Attach thing]を選択する。
作成済Thingをチェックして[Attach]をクリックする。
Arduino
AWS IoTの設定が終わったら残りはArduino側の設定となる。
Arduino IDEを動かす母艦はUbuntu 16.04とする。
最初にArduinoにセンサーを接続してセンシングを行う回路を作成する。ここでは温度センサーを使って室温を計測することとする。
またArduinoの母艦となるOS(ここではUbuntu 16.04)上にAWS IoTのMQTTブローカーに対してSubscribeを行うプログラムとセンサーからのデータを取得してPublishするプログラムをそれぞれ用意する。言語はここではRubyを使用する。
センシング用の回路を作成
温度センサーとして精度はあまり高くないものの安価に調達できるLM61BIZを使用する。
センサーのピンは3つで刻印面に向かって左から
+Vs(電源電圧)
Vout(出力電圧)
GND(グランド)
という配列になっているのでこれをArduinoの5V、A0、GNDにそれぞれ接続する。
結線イメージは以下のようになる。
関連資料・記事
Arduinoでセンシングを行いAWS IoT上のMQTTブローカーにPublishするプログラムを用意
まずRubyからArduinoを操作するためのarduino_firmataパッケージと、MQTTのPub/Sub通信を行うためのmqttパッケージをインストールする。
% sudo gem install arduino_firmata % sudo gem install mqtt
次に温度センサーからの値を1秒おきに取得して値を温度に変換したうえで前回取得値と異なる場合はAWS IoT上のMQTTブローカーにPublishするプログラムを用意する。
% vi pub.rb require 'rubygems' require 'arduino_firmata' require 'mqtt' arduino = ArduinoFirmata.connect "/dev/ttyACM0" client = MQTT::Client.connect( host: '<Thing ARN>', port: 8883, ssl: true, cert_file: '<証明書ファイル>', key_file: '<秘密鍵ファイル>', ca_file: 'rootCA.pem') before_temp = 0 while true temp = arduino.analog_read 0 temp = ((temp.to_f * 5 / 1024) - 0.6) * 100 temp = temp.round if before_temp != temp puts temp before_temp = temp client.publish('topic/test', "temp = #{temp}") end sleep 1 end arduino.close
関連資料・記事
テスト
上記で用意したプログラムを実行すると、温度が変わる度にAWS IoT上のMQTTブローカーに温度が送信(Publish)される。
% ruby pub.rb 22 22 22 23 <- 温度センサーに手を触れて温度を変化させる 23 23
AWS IoTはこのデータを受け取るとルール設定に従ってAmazon SNS経由でメール通知を行う。通知先には以下のようなメールが届く。
今回はデバイスから取得したデータをアウトプットするまでの一連の動きを見るためにシンプルな実装を行ったが、AWS IoTでは他にも目的に応じて様々な制御が可能である。
参考サイト
AWS IoT (Amazon Web Services)
AWS IoT開発者ガイド (Amazon Web Services)
AWS IoTとRaspberry PiではじめるIoT超入門 (CodeZine)
AWS IoT情報 (Qiita)
2019/10/22更新
Amazon FreeRTOS(以下FreeRTOS)は様々なデバイスにインストールすることができエッジ側にAWSの環境を拡張する手段を提供しているが、ここでは手に入りやすいEspressif ESP32-DevKitC(以下ESP32)にFreeRTOSをインストールする手順を示す。Espressif ESP32-DevKitC と ESP-WROVER-KIT の開始方法 (Amazon Web Services)
動作環境
FreeRTOSを使うにあたりあらかじめ以下をインストールしておく。
Pytyoh 2.7.10以降 (3.xでよいが、2.xを使う場面あり)
pip
AWS SDK for Python (boto3)
AWS CLI
$ python --version Python 3.6.8 $ python2 --version Python 2.7.15+ $ pip --version pip 19.3.1 from /usr/local/lib/python3.6/dist-packages/pip (python 3.6) $ pip list | grep boto3 boto3 1.9.253 $ aws --version aws-cli/1.16.263 Python/3.6.8 Linux/4.15.0-65-generic botocore/1.12.253
ツールチェーンインストール
まずEspressif公式サイトからUbuntu用のツールチェーンをダウンロードしてインストールする。
https://docs.espressif.com/projects/esp-idf/en/v3.1.5/get-started-cmake/linux-setup.html
以下をダウンロード & インストール
xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
$ mkdir ~/esp $ cd ~/esp $ tar zxvf ~/Downloads/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
環境変数設定
export PATH="$PATH:$HOME/esp/xtensa-esp32-elf/bin"
バージョン確認
$ xtensa-esp32-elf-gcc --version xtensa-esp32-elf-gcc (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) 5.2.0 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
CMakeインストール
次にFreeRTOSビルド用のCMakeをインストールする。
Amazon FreeRTOSではCMake 3.13以降がサポートされるが、Ubuntuのパッケージリポジトリには3.10しか用意されていないためCMake公式サイトからダウンロードしてインストールする。
以下をダウンロード & インストール
cmake-3.15.4-Linux-x86_64.tar.gz
$ mkdir ~/tmp $ cd ~/tmp $ tar zxvf ~/Downloads/cmake-3.15.4-Linux-x86_64.tar.gz $ sudo cp -r cmake-3.15.4-Linux-x86_64/{bin,doc,share} /usr/local
manファイルについては格納場所/usr/local/manが/usr/local/share/manへのシンボリックリンクのためmanファイルのみ以下のようにしてコピーする。
$ sudo cp -r cmake-3.15.4-Linux-x86_64/man /usr/local/share
バージョン確認
$ cmake --version cmake version 3.15.4
Amazon FreeRTOSダウンロード & 設定
ビルドの準備が整ったらAmazon FreeRTOS公式GitHubからソース一式をダウンロードする。
https://github.com/aws/amazon-freertos
$ cd ~/tmp $ git clone https://github.com/aws/amazon-freertos.git --recurse-submodules
次にセットアップスクリプト用の設定ファイルを編集する。
$ cd amazon-freertos/tools/aws_config_quick_start $ vi configure.json { "afr_source_dir":"../..", <----------- 上記ダウンロードディレクトリ(amazon-freertosディレクトリのフルパス) "thing_name":"$thing_name", <--------- ESP32に付けるThing Name "wifi_ssid":"$wifi_ssid", <----------- Wi-FiネットワークのSSID "wifi_password":"$wifi_password", <--- Wi-Fiネットワークの接続パスワード "wifi_security":"$wifi_security" <---- Wi-Fiネットワークのセキュリティタイプ }
設定ファイルが編集できたらセットアップスクリプトを実行する。
$ python SetupAWS.py setup
これによりAWS IoT上のリソースが自動的に作られ、リソース間の関連付けも行われる。
モノ(Thing)作成
証明書作成
ポリシー作成
証明書にポリシーをアタッチ
モノに証明書をアタッチ
証明書関連のファイル作成
$ ls -l ESP32_01_* -r--r--r-- 1 xxx xxx 64 10月 22 08:46 ESP32_01_cert_id_file <----------- 証明書ID -r--r--r-- 1 xxx xxx 1220 10月 22 08:46 ESP32_01_cert_pem_file <---------- 公開鍵 -r--r--r-- 1 xxx xxx 1675 10月 22 08:46 ESP32_01_private_key_pem_file <--- 秘密鍵
MQTTエンドポイント、Wi-Fiネットワーク情報をデモ用aws_clientcredential.hファイルに追加
$ cat -n ../../demos/include/aws_clientcredential.h : 35 /* 36 * MQTT Broker endpoint. 37 */ 38 #define clientcredentialMQTT_BROKER_ENDPOINT "xxxxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com" 39 40 41 /* Use of a "define" and not a "static const" here to be able to 42 * use pre-compile concatenation on the string. */ 43 #define clientcredentialIOT_THING_NAME "ESP32_01" : 56 /* 57 * Wi-Fi network to join. 58 */ 59 #define clientcredentialWIFI_SSID "xxxxxxxxxxxxxxxx" 60 61 /* 62 * Password needed to join Wi-Fi network. 63 */ 64 #define clientcredentialWIFI_PASSWORD "xxxxxxxxxxxxx" 65 66 /** 67 * @brief Security type 68 * WPA2 Security, @see WIFISecurity_t 69 * Possible values are - eWiFiSecurityOpen, eWiFiSecurityWEP, eWiFiSecurityWPA, 70 * eWiFiSecurityWPA2 71 */ 72 #define clientcredentialWIFI_SECURITY xxxxxxxxxxxxxxxxx 73 74 #endif
証明書とプライベートキーをBase64エンコードしてデモ用aws_clientcredential_keys.hファイルに追加
$ cat -n ../../demos/include/aws_clientcredential_keys.h : 29 /* 30 * PEM-encoded client certificate 31 * 32 * Must include the PEM header and footer: 33 * "-----BEGIN CERTIFICATE-----\n"\ 34 * "...base64 data...\n"\ 35 * "-----END CERTIFICATE-----\n" 36 */ 37 #define keyCLIENT_CERTIFICATE_PEM \ 38 "-----BEGIN CERTIFICATE-----\n"\ : 57 "-----END CERTIFICATE-----\n" 77 /* 78 * PEM-encoded client private key. 79 * 80 * Must include the PEM header and footer: 81 * "-----BEGIN RSA PRIVATE KEY-----\n"\ 82 * "...base64 data...\n"\ 83 * "-----END RSA PRIVATE KEY-----\n" 84 */ 85 #define keyCLIENT_PRIVATE_KEY_PEM \ 86 "-----BEGIN RSA PRIVATE KEY-----\n"\ : 112 "-----END RSA PRIVATE KEY-----\n" :
FreeRTOSデモプロジェクトビルド & ESP32書き込み & 実行
設定が終わったのでCMakeでFreeRTOSをビルドし、ESP32に書き込んで実行する。
ビルド
$ cd ../.. $ cmake -DVENDOR=espressif -DBOARD=esp32_devkitc -DCOMPILER=xtensa-esp32 -S . -B build $ cd build $ make all -j4 : [100%] Built target app
ESP32書き込み
ここでESP32をホストPCに接続し、まず以下を実行してESP32の中身を消去する。
$ cd .. $ ./vendors/espressif/esp-idf/tools/idf.py erase_flash -B build ESP-IDF currently only supports Python 2.7, and this is Python 3.6.8. Search for 'Setting the Python Interpreter' in the ESP-IDF docs for some tips to handle this.
ただ当該環境ではPython3がデフォルトなので上記のエラーになる。
idf.pyを書き換えてPython2を明示的に指定して再度実行する。
$ vi ./vendors/espressif/esp-idf/tools/idf.py (変更前) #!/usr/bin/env python (変更後) #!/usr/bin/env python2 $ ./vendors/espressif/esp-idf/tools/idf.py erase_flash -B build Setting IDF_PATH environment variable: ... : Chip erase completed successfully in 3.8s Hard resetting via RTS pin... Done
続いてFreeRTOSをESP32に書き込む。
$ cd build $ make flash [ 0%] Built target blank_ota_data [ 1%] Built target partition_table [ 1%] Built target idf_component_ulp : Wrote 953520 bytes (585067 compressed) at 0x00020000 in 14.5 seconds (effective 526.8 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... [100%] Built target flash
AWS上のMQTTメッセージのモニタリング
AWS IoTコンソールでMQTTクライアントを使ってESP32がAWSに送信するメッセージをサブスクライブしておく。
[テスト]をクリックするとMQTTクライアントが開くので[トピックのサブスクリプション]フィールドに「iotdemo/#」と入力して[トピックへのサブスクラ...]をクリックする。
いまサブスクライブしたトピックの[iotdemo/#]をクリックしてメッセージを待ち受ける。
デモプログラム実行
この状態でデモプログラムを実行するとMQTTでパブリッシュが行われ、AWS IoTコンソール上でもメッセージが受信できていることが確認できる。
$ cd .. $ ./vendors/espressif/esp-idf/tools/idf.py monitor -p /dev/ttyUSB0 -B build : 157 914 [iot_thread] [INFO ][DEMO][9140] Demo completed successfully. 158 918 [iot_thread] [INFO ][INIT][9180] SDK cleanup done. 159 918 [iot_thread] [INFO ][DEMO][9180] -------DEMO FINISHED------- -> Ctrl + ]で終了
参考サイト
AWS IoT情報 (Qiita)
2024/05/31更新
ALBのAnomalousHostCountメトリクスはターゲットホストからの異常応答(HTTP 5XXなど)が他のターゲットよりも多い場合の対象ターゲット数を表すが、これを簡単に出す環境を作る。(AnomalousHostCountをトリガーにアラームを設定する場合などにそのテスト環境として役に立つのではないかと)
環境作成とテスト
ALBにぶら下げるターゲットはAWSがAnomalousHostを判断するために3台以上必要なので、まず以下のEC2インスタンスを3台作成する。
Amazon Linux 2023 + nginx(インストールのみ)
次にこのうち1台だけnginxに流量制御を設定する。
$ sudo vi /etc/nginx/nginx.conf : http { limit_req_zone $binary_remote_addr zone=req:1m rate=1r/s; limit_req zone=req; : } $ sudo systemctl restart nginx
limit_req_zoneで流量制御のゾーン定義をし、limit_reqでそのゾーンを有効化する。
$binary_remote_addr
リモートアドレス毎にバッファを用意
zone=req:1m
バッファに「req」というゾーン名を付け、バッファ容量は1MBとする
rate=1r/s
1秒間に1リクエストまでの受け入れを許可
その他パラメータの詳細は公式ドキュメントを参照のこと。
Module ngx_http_limit_req_module (nginx)
この状態でALBを作成して上記3台のEC2をぶら下げ、ヘルスチェックの失敗回数(非正常のしきい値)を2から10に増やしてターゲットホストがUnHealthyになる条件を緩めた上で外部から例えば0.2秒毎にリクエストを送るとAnomalousHostCountメトリクスが出る。
$ while [ 1 ] do curl http://<ALB>のURL/ sleep 0.2 done
なお、流量制御を設定したnginxのログには以下のような内容が記録される。
/var/log/nginx/access.log
xx.xx.xx.xx - - [DD/MMM/YYYY:hh:mm:ss +0900] "GET / HTTP/1.1" 503 3693 "-" "curl/x.x.x" "xx.xx.xx.xx"
/var/log/nginx/error.log
YYYY/MM/DD hh:mm:ss [error] 2187#2187: *1774 limiting requests, excess: 0.195 by zone "req", client: xx.xx.xx.xx, server: _, request: "GET / HTTP/1.1", host: "xxxxxxxx.ap-northeast-1.elb.amazonaws.com"
参考ドキュメント
Application Load Balancer のターゲットグループ > 自動ターゲット重み (ATW) (Amazon Web Services)
2019/12/24更新
対応バージョン: Amazon Linux 2
Amazon Linux 2上でcurlで任意のWebサイトにアクセスしようとすると以下のようなエラーが出ることがある。$ curl https://foo.bar.com/ : curl: (60) SSL certificate problem: unable to get local issuer certificate More details here: https://curl.haxx.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above.
これはローカルに保存されているルート証明書の中にアクセス先サイトのSSL証明書を証明する者(issuer)がいないことが原因なので、証明者をシステムに組み込んであげればよい。
ここではアクセス先サイトのSSL証明書がLet's Encrypt発行のものだった場合の対応手順を示す。
まずSSL証明書の内容を調べる
$ openssl s_client -connect foo.bar.com:443 < /dev/null 2> /dev/null | openssl x509 -text Certificate: Data: Version: 3 (0x2) Serial Number: 04:e3:91:24:eb:0e:28:a7:9e:e7:84:ac:b4:65:8d:4d:b9:c7 Signature Algorithm: sha256WithRSAEncryption Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 Validity Not Before: Oct 26 13:34:11 2019 GMT Not After : Jan 24 13:34:11 2020 GMT :
IssuerがLet's Encryptであることが確認できる。
次に以下からクロス署名されたLet’s Encryptの中間証明書を取得する
Let’s Encrypt - Chain of Trust
手順は以下の通り。
$ curl https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt \ -o /tmp/lets-encrypt-x3-cross-signed.pem $ openssl verify /tmp/lets-encrypt-x3-cross-signed.pem /tmp/lets-encrypt-x3-cross-signed.pem: OK
この中間証明書をシステムに組み込む
/etc/pki/ca-trust/extracted配下のファイルが更新されるので念のためバックアップを取っておく。
$ cd /etc/pki/ca-trust/extracted $ sudo tar cvf crt.tar *
証明書をシステムに組み込む。
$ sudo cp /tmp/lets-encrypt-x3-cross-signed.pem /etc/pki/ca-trust/source/anchors $ sudo update-ca-trust extract
これでcurlでエラーにならずにコンテンツが取得できるようになる。
$ curl https://foo.bar.com/ <!DOCTYPE html> <html> :
Ubuntuの場合
ちなみにUbuntu 18.04の場合は以下のようにすれば同様の対応が可能である。
$ sudo cp -p /etc/ssl/certs/ca-certificates.crt{,.org} $ curl https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt \ -o /tmp/lets-encrypt-x3-cross-signed.pem $ sudo sh -c "cat /tmp/lets-encrypt-x3-cross-signed.pem >> /etc/ssl/certs/ca-certificates.crt" $ openssl verify /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt: OK
参考サイト
Amazon EC2 (Amazon Web Services)
AWS技術情報 (Qiita)
2024/11/07更新
対応バージョン: Amazon Linux 2
EC2のメトリクス値に異常が見受けられる場合、EC2自体(OSの挙動)に問題がなくても(AWSが自動で)取得しているメトリクスのサンプル数が普段と違うことにより異常値を示している場合がある。以下はいくつかのメトリクスのサンプル数が普段と違う場合にCPUUtilizationとNetworkInが異常値を示す例である。(04:00〜23:00の時間帯)
通常サンプル数は一定であるため、サンプル数が普段と違う(上記の場合は1分あたりのサンプル数が5から10に倍増している)時点でAWS側になんらかの原因があると考えられ、自然に元に戻る場合もあるがEC2インスタンスを再起動(停止・起動)してEC2インスタンスが動作するホストを入れ替えると解消する可能性がある。(上記は自然に元に戻ったパターン)
次にCPUUtilizationとNetworkInの値を見てみると、状況的にはこの2つの値は本来一定であるはずだが、まず平均値を見ると以下のことがわかる。
CPUUtilization
50%前後を行き来している
NetworkIn
半分に下がっている
さらに最大値・最小値を見ると以下のことが分かり、通常ではあり得ないパターンを示している。
CPUUtilization
最大は100%に張り付き、最小は0%と3%を行き来している
NetworkIn
最大は普段と変わらないにもかかわらず、最小が0のまま
OS内部のプロセス動作状況やsyslog・sar情報などを確認しても上記のメトリクス値になる要素がないため、AWS側の問題であることがわかる。
2023/12/29更新
対応バージョン: Amazon Linux 2
今までIPv4でSSH接続していたEC2にIPv6で接続するための最小限を設定をまとめる。(IPv4を無効にする設定は行わない)
EC2はVPC内のサブネット内に配置するのでその両方の変更が必要になる。
なお、ここではパブリックサブネットに配置しているEC2を対象とする。(踏み台ホストへの接続を想定)
VPC
対象のVPCを選択して[アクション] > [CIDRの編集]をクリックする
「IPv6 CIDR」の[新しいIPv6 CIDRを追加]をクリックする
[Amazon提供のIPv6 CIDRブロック]を選択して[CIDRを選択]をクリックする
(結果) プレフィックス長 /56 のIPv6アドレスが割り当てられる
ルートテーブル
対象のVPCに紐付けているメインルートテーブルを選択して「ルート」の[ルートを編集]をクリックする
前述で割り当てられたIPv6アドレスのターゲットをインターネットゲートウェイに割り当てる
サブネット
対象のサブネットを選択して[アクション] > [IPv6 CIDRの編集]をクリックする
「サブネットのCIDRブロック」の[IPv6 CIDRを追加]をクリックする
「サブネットのCIDRブロック」の「Subnet CIDR block」を指定して[保存]をクリックする。通常一つのVPCの中に複数のサブネットを配置するので、プレフィックス長 /64 などで小分けにする
対象のサブネットを選択して[アクション] > [サブネットの設定を編集]をクリックする
「IPv6アドレスの自動割り当て設定」の[IPv6アドレスの自動割り当てを有効にする]をチェックして[保存]をクリックする
EC2
EC2は既存のインスタンスにIPv6アドレスを付与することができないため、一度AMIを取得してそのAMIから新規にEC2を作成する
(結果) IPv6アドレスが割り当てられる
セキュリティグループ
対象のEC2に割り当てているセキュリティグループの「インバウンドルール」にIPv6でSSH接続する接続元のIPv6アドレスを設定する
IPv6でSSH接続
これでAWS側の準備ができたのでIPv6でSSH接続する。
$ ssh -6 -i <pemファイル> ec2-user@<接続先EC2のIPv6アドレス> Last login: xxx xxx xx xx:xx:xx xxxx from xxxx:xx:xxxx:xxxx:xxxx:xxxx:xxx:xxxx __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/ [ec2-user@ip-xx-xx-xx-xx ~]$
2022/07/27更新
対応バージョン: Amazon Linux 1
Amazon Linux 1のOSを再起動すると特にsshd関連の設定変更をしたわけでもないのに/etc/pam.d/sshdが更新される。$ uname -r 4.14.285-147.501.amzn1.x86_64
これはsshdの起動スクリプト(/etc/rc3.d/S55sshd)に以下のような処理が書かれているためである。
80行目でhandle_pam_broken_kernel()を呼び出し、同関数の中で「すでに/etc/pam.d/sshdが存在していたらsed -i にて/etc/pam.d/sshd自身を更新する」
: 49 handle_pam_broken_kernel() 50 { 51 if [ -f /etc/pam.d/sshd ] ; then 52 if [[ "$(uname -r)" =~ 3\.10\.34-3[78]\.137\.amzn1 ]] ; then 53 # problematic kernel, disable pam_loginuid.so 54 sed -r -i -e 's/^(session.*pam_loginuid.*$)/##sshd_autodisabled##\1/' /etc/pam.d/sshd 55 else 56 # should be a valid kernel, undo auto-disablement 57 sed -r -i -e 's/^##sshd_autodisabled##//' /etc/pam.d/sshd 58 fi 59 fi 60 } : 72 start() 73 { 74 [ -x $SSHD ] || exit 5 75 [ -f /etc/ssh/sshd_config ] || exit 6 76 # Create keys if necessary 77 /usr/sbin/sshd-keygen 78 79 # workaround a broken kernel build 80 handle_pam_broken_kernel 81 82 echo -n $"Starting $prog: " 83 $SSHD $OPTIONS && success || failure 84 RETVAL=$? 85 [ $RETVAL -eq 0 ] && touch $lockfile 86 echo 87 return $RETVAL 88 } :
なおAmazon Linux 2においてはこの挙動は起こらない。
2021/05/28更新
対応バージョン: Amazon Linux 2
Amazon Linux 2上で動作するanacronによるログローテーションのデフォルトの挙動が複雑なので整理する。また後半ではローテーション設定ファイルで指定可能なオプションを解説する。
なお、ここでは以下のカーネルバージョンを使用する。
$ uname -r 4.14.231-173.361.amzn2.x86_64
ログローテーションの実行タイミング
ログローテーションの実行タイミングは以下の通りである。
1日1回、3時〜22時の間の最初に訪れたxx時6分〜51分(起動がxx時1分 + ランダム遅延時間最大45分 + 固定遅延時間5分)
ログローテーション実行までの詳細な流れ
上記のタイミングを制御するために複数の設定ファイルにより以下のような流れでログローテーションが実行される。
/etc/cron.d/0hourlyの記述に従い毎時1分に/etc/cron.hourlyディレクトリ配下のスクリプトが起動する
$ ls -l /etc/cron.d 合計 16 -rw-r--r-- 1 root root 128 1月 16 2020 0hourly -rw-r--r-- 1 root root 108 8月 2 2018 raid-check -rw------- 1 root root 235 12月 17 2019 sysstat -rw-r--r-- 1 root root 191 10月 16 2018 update-motd $ cat /etc/cron.d/0hourly # Run the hourly jobs SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root 01 * * * * root run-parts /etc/cron.hourly
/etc/cron.hourlyディレクトリには0anacronスクリプトが格納されており、anacronが起動する(/var/spool/anacron/cron.dailyの中身は空である)
$ ls -l /etc/cron.hourly -rwxr-xr-x 1 root root 392 1月 16 2020 0anacron $ cat /etc/cron.hourly/0anacron #!/bin/sh # Check whether 0anacron was run today already if test -r /var/spool/anacron/cron.daily; then day=`cat /var/spool/anacron/cron.daily` fi if [ `date +%Y%m%d` = "$day" ]; then exit 0; fi # Do not run jobs when on battery power if test -x /usr/bin/on_ac_power; then /usr/bin/on_ac_power >/dev/null 2>&1 if test $? -eq 1; then exit 0 fi fi /usr/sbin/anacron -s
anacronの設定ファイルである/etc/anacrontabには以下の内容が記述されており、ログローテーションは/etc/cron.daily/logrotateスクリプトにて実行される
・起動すると最大遅延時間45分が設定される(RANDOM_DELAY=45)
・実行時間は3時〜22時の間(START_HOURS_RANGE=3-22)
・cron.dailyの実行は1日1回(period in daysが1)
・cron.dailyの開始時間はanacron起動から5分後(delay in minutesが5) + 上記RANDOM_DELAY
$ sudo cat /etc/anacrontab # /etc/anacrontab: configuration file for anacron # See anacron(8) and anacrontab(5) for details. SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # the maximal random delay added to the base delay of the jobs RANDOM_DELAY=45 # the jobs will be started during the following hours only START_HOURS_RANGE=3-22 #period in days delay in minutes job-identifier command 1 5 cron.daily nice run-parts /etc/cron.daily 7 25 cron.weekly nice run-parts /etc/cron.weekly @monthly 45 cron.monthly nice run-parts /etc/cron.monthly $ ls -l /etc/cron.daily 合計 12 -rwx------ 1 root root 219 7月 27 2018 logrotate -rwxr-xr-x 1 root root 618 4月 29 2019 man-db.cron -rwx------ 1 root root 208 7月 27 2018 mlocate
/etc/cron.daily/logrotateスクリプトは以下のようになっており、ログローテーションは/etc/logrotate.confの設定に従って実行される
$ sudo cat /etc/cron.daily/logrotate #!/bin/sh /usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf EXITVALUE=$? if [ $EXITVALUE != 0 ]; then /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]" fi exit 0
/etc/logrotate.confは以下の内容になっており、ログローテーション全体の設定および以下のファイルのログローテーション設定が記述されている。それ以外のファイルのローテーション設定は/etc/logrotate.d配下に格納されている
・/var/log/wtmp
・/var/log/btmp
$ cat /etc/logrotate.conf # see "man logrotate" for details # rotate log files weekly weekly # keep 4 weeks worth of backlogs rotate 4 # create new (empty) log files after rotating old ones create # use date as a suffix of the rotated file dateext # uncomment this if you want your log files compressed #compress # RPM packages drop log rotation information into this directory include /etc/logrotate.d # no packages own wtmp and btmp -- we'll rotate them here /var/log/wtmp { monthly create 0664 root utmp minsize 1M rotate 1 } /var/log/btmp { missingok monthly create 0600 root utmp rotate 1 } # system-specific logs may be also be configured here. $ ls -l /etc/logrotate.d 合計 20 -rw-r--r-- 1 root root 76 8月 2 2018 bootlog -rw-r--r-- 1 root root 160 8月 19 2020 chrony -rw-r--r-- 1 root root 408 8月 2 2018 psacct -rw-r--r-- 1 root root 224 6月 16 2020 syslog -rw-r--r-- 1 root root 100 2月 25 18:33 yum
例えばsyslogのログローテーション設定は以下のような内容になっている。
$ cat /etc/logrotate.d/syslog /var/log/cron /var/log/maillog /var/log/messages /var/log/secure /var/log/spooler { missingok sharedscripts postrotate /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true endscript }
これら既存の設定を必要に応じて修正したり、アプリケーションログなど任意のファイルを追加でログローテーションしたい場合はこの/etc/logrotate.d配下に新規にファイルを作成して目的に合った設定を行う。
なお、ログローテーションの状況は対象ファイル毎に/var/lib/logrotate/logrotate.statusに記録され、次回ローテーション実行時に前回実行分との差を確認するために使用される。
$ cat /var/lib/logrotate/logrotate.status logrotate state -- version 2 "/var/log/yum.log" 2021-5-26-8:0:0 "/var/log/boot.log" 2021-5-26-8:0:0 "/var/log/wtmp" 2021-5-26-8:0:0 "/var/log/chrony/*.log" 2021-5-26-8:0:0 "/var/log/spooler" 2021-5-26-8:0:0 "/var/log/btmp" 2021-5-26-8:0:0 "/var/log/maillog" 2021-5-26-8:0:0 "/var/log/secure" 2021-5-26-8:0:0 "/var/log/messages" 2021-5-26-8:0:0 "/var/log/cron" 2021-5-26-8:0:0 "/var/account/pacct" 2021-5-26-8:0:0
ローテーション設定ファイルで指定可能なオプション
主なオプションをざっと掲載する。
(*)印があるコマンドは先頭に「no」を付けると同オプションと反対の動きをする。(ただしifemptyだけは先頭に「not」が付いてnotifemptyになる)
より詳しくは「man logrotate」を参照のこと。
ローテーション時の挙動
compress (*)
古いログファイルをgzip圧縮する
delaycompress (*)
前のログファイルの圧縮を次のローテーション時に行う
compresscmd
compressオプションを指定した場合に圧縮に使うコマンド(デフォルトはgzip)
uncompresscmd
圧縮ファイルの解凍時に使うコマンド(デフォルトはgunzip)
compressext
圧縮ファイルで使用する拡張子(デフォルトはcompresscmdで指定したコマンドのデフォルト拡張子)
compressoptions
圧縮コマンドに指定するコマンドラインオプション(gzipコマンドの場合、デフォルトは-6(速度を犠牲にして圧縮率を高める))
copy (*)
ログファイルのコピーを作成するが、オリジナルは変更しない
このオプションを使用すると古いログファイルがそのまま残るため、create(ファイル作成時の指定)オプションは効果がなくなる
copytruncate (*)
元のログファイルをコピーして新しいファイルを作成する代わりに、コピーを作成したあと元のログファイルをトランケートする(中身を空にする)
copyオプションと同様、このオプションを使用すると古いログファイルがそのまま残るため、create(ファイル作成時の指定)オプションは効果がなくなる
create <モード> <オーナー> <グループ> あるいは create <オーナー> <グループ> (*)
ログローテーションの直後(ローテーション後のスクリプトが実行される前)にログファイルが作成される
ファイルの属性を省略すると元のログファイルのものが使われる
su <ユーザ> <グループ>
ローテーションされたファイルに設定するユーザとグループを指定する(デフォルトはroot)
olddir <ディレクトリ> (*)
ローテーションしたファイルを格納するディレクトリ
createolddir <モード> <オーナー> <グループ> (*)
olddirオプションで指定したディレクトリが存在しない場合は作成する
dateext (*)
ローテーションしたファイルの末尾に数字を追加するのではなく「YYYYMMDD」のような日付拡張子を追加する
拡張子のフォーマットはdateformatおよびdateyesterdayオプションで指定できる
dateformat <フォーマット文字列>
strftime(3)と同様の表記にてdateextオプションの拡張子を指定する
使用可能な表記は「%Y」「%m」「%d」「%H」「%s」のみである
デフォルトは毎時の場合は「-%Y%m%d%H」、それ以外は「-%Y%m%d」
dateyesterday
今日の日付の代わりに昨日の日付を使用してdateext拡張子を作成し、ローテーションされたログファイルの名前にタイムスタンプと同じ日付が含まれるようにする
extension <拡張子>
<拡張子>で指定した拡張子のついたファイルがログローテーション後も保持されるようにする
圧縮を有効化すると拡張子の後に圧縮の拡張子(通常は.gz)が続くが、例えば「mylog.foo」というファイルをローテーションした場合に「mylog.foo.1.gz」ではなく「mylog.1.foo.gz」いうファイル名になるようにする
ifempty (*)
ログファイルが空でもローテーションを行う
missingok (*)
ログファイルが見つからない場合でもエラーにせずに次のログファイルを処理する
rotate <回数>
指定した回数分ローテーションを行う
<回数>が0の場合、古いファイルはローテーションされるのではなく削除される
start <番号>
ローテーション時のファイル末尾に付与する番号の起点番号
例えば0を指定すると、ログは元のログファイルからローテーションされるため拡張子は「.0」となる
9を指定すると、ログファイルの拡張子は「.9」で作成され、0〜8はスキップされる
ファイルはrotateオプションで指定された回数だけローテーションする
size <サイズ>
ログファイルが指定したサイズより大きくなるとローテーションする
サイズはバイト指定だが、k(KB)、M(MB)、G(GB)などの単位を付けて指定することもできる
サイズの指定方法は他のオプションも同一
maxsize <サイズ>
追加指定された時間間隔(日次、週次、月次、または年次)より前であっても、ログファイルが指定したサイズより大きくなった場合にローテーションする
minsize <サイズ>
ログファイルが指定したサイズより大きくなった場合にローテーションするが、追加指定された時間間隔(日次、週次、月次、または年次)より前にはローテーションしない
maxage <カウント>
<カウント>日よりも古いローテーションされたログを削除する
shred (*)
ログファイル削除時にunlink()ではなくshred -uを使用する(デフォルトは無効)
shredcycles <カウント>
ログファイルを削除する前にshredにログファイルを<カウント>回数上書きするように要求する
このオプションを指定しない場合、shredのデフォルト値が使用される
tabooext [+] <拡張子リスト>
ローテーションから除外される拡張子を指定する
「+」が拡張子リストの前にある場合、現在の拡張子リストが拡張され、「+」がない場合は現在の拡張子リストがここで指定したリストに上書きされる
デフォルトの拡張子リストは以下の通り
「.rpmsave」「.rpmorig」「~」「.disabled」「.dpkg-old」「.dpkg-dist」「.dpkg-new」「.cfsaved」「.ucf-old」「.ucf-dist」「.ucf-new」「.rpmnew」「.swp」「.cfsaved」「.rhn-cfg-tmp-*」
ローテーションタイミング
hourly
ローテーションを毎時行う
ただlogrotateはcronの設定で毎日行うように設定されているため、毎時ローテーションを行いたい場合はcronの設定を毎時実行するように変更する必要がある
daily
ローテーションを毎日行う
monthly
logrotateがその月に初めて実行された時にログローテーションを行う(通常それは月の初日になる)
yearly
ローテーションを毎年行う
weekly <曜日>
指定した曜日に1回ローテーションするか、最後のローテーションから少なくとも7日経ってローテーションする
曜日を表す数字は以下の通りで、特別な指定である7は7日毎を意味する(デフォルトは0)
0(日曜日)、1(月曜日)、2(火曜日)、3(水曜日)、4(木曜日)、5(金曜日)、6(土曜日)
スクリプト実行
prerotate 〜 endscript
ログファイルがローテーションされる前にこのオプションに囲まれたスクリプトが実行される
後述のsharedscriptsオプションが指定されている場合はパターン全体がスクリプトに渡される
postrotate 〜 endscript
ログファイルがローテーションされた後にこのオプションに囲まれたスクリプトが実行される
後述のsharedscriptsオプションが指定されている場合はパターン全体がスクリプトに渡される
firstaction 〜 endscript
ログファイルがローテーションされる前、かつprerotateスクリプトが実行される前に1回だけ実行される
ただし、少なくとも1つのログファイルがローテーションされる場合のみ実行される
スクリプトがエラーになった場合、以降の処理は行われない
lastaction 〜 endscript
ログファイルがローテーションされた後、かつpostrotateスクリプトが実行された後に1回だけ実行される
スクリプトがエラーになった場合、エラーメッセージのみが表示される(これが最後のアクションのため)
preremove 〜 endscript
ログファイルを削除する直前に1回だけ実行される
sharedscripts (*)
通常、prerotateとpostrotateで指定したスクリプトはローテーションされるログ毎に実行され、ログファイルへの絶対パスがスクリプトの最初の引数として渡される
このオプションが指定された場合、パターンマッチしたファイルの数に関係なくスクリプトは一回だけ実行される
ただしパターンマッチしたファイルのうちローテーションの必要なものが一つもない場合はスクリプトは実行されない
スクリプトがエラーになった場合、残りのアクションはどのログに対しても実行されない
メール通知
mail <メールアドレス> (*)
対象ログファイルが存在しない状態でローテーションされると指定したメールアドレスにメール通知を行う
mailfirst
メール送信時に期限切れ間近のファイルではなくローテーションしたばかりのファイルを送信する
maillast
メール送信時にローテーションしたばかりのファイルではなく期限切れ間近のファイルを送信する(これがデフォルト)
その他
include <ファイル> あるいは <ディレクトリ>
指定したファイルあるいはディレクトリ配下のファイルを設定ファイルとして追加使用する
2020/07/26更新
対応バージョン: Amazon Linux 2
一般ユーザのnproc値を変更しようと以下のソフトリミット設定を行ったが、スペックの低いEC2インスタンスだとある一定の値以上に増やせない。$ cat /etc/security/limits.d/20-nproc.conf : * soft nproc 4096 root soft nproc unlimited $ ulimit -u 3791
またrootユーザもunlimitedに設定されているにもかかわらず、同様にulimit -uの値が一般ユーザと同じになってしまう。
$ sudo su # ulimit -u 3791
これはnprocのハードリミットが設定されておらずカーネルによって設定された値がハードリミットとなり、この値がulimit -uの上限(上記の例では3791)になるためである。
$ man limits.conf : <type> hard for enforcing hard resource limits. These limits are set by the superuser and enforced by the Kernel. The user cannot raise his requirement of system resources above such values. :
この制限を外すには、以下のように設定してハードリミットを増やしてあげればよい。
$ cat /etc/security/limits.d/20-nproc.conf : * hard nproc 4096
同様に、rootユーザのulimit -uをunlimitedにするには以下のように設定すればよい。
$ cat /etc/security/limits.d/20-nproc.conf : root hard nproc unlimited
2019/08/20更新
対応バージョン: Amazon Linux 2
ローカルのファイルをリモートのEC2にセキュアに同期(コピー)する場合、脆弱性のあるscpを使わずにsftpやrsyncを使ったほうがよいとされており、今後主流になっていくであろうOpenSSH-8.0でも同様の案内がされている。 OpenSSH-8.0リリースノートより: Security ======== : The scp protocol is outdated, inflexible and not readily fixed. We recommend the use of more modern protocols like sftp and rsync for file transfer instead. :
そこで、rsyncを使った同期の方法を示す。
尚、同期元と同期先の環境はそれぞれ以下とする。
同期元 (Ubuntu 18.04)
$ uname -r 4.15.0-58-generic $ ssh -V OpenSSH_7.6p1 Ubuntu-4ubuntu0.3, OpenSSL 1.0.2n 7 Dec 2017 $ rsync --version rsync version 3.1.2 protocol version 31
同期先 (Amazon Linux 2)
$ uname -r 4.14.123-111.109.amzn2.x86_64 $ ssh -V OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017 $ rsync --version rsync version 3.1.2 protocol version 31
rsync使用方法
以下のようにする。
rsync <オプション> -e 'ssh -i <EC2キーペアの秘密鍵>' <ローカルディレクトリ> <リモートディレクトリ>
使用例)
$ rsync -av --delete -e 'ssh -i ~/.ssh/ec2.pem' ~/files/ ec2-user@1.2.3.4:files/
オプションはここでは以下を指定しているが、他にもいろいろあるので用途に応じて指定する。
-a アーカイブモード(-rlptgoDと同義)
-r(ディレクトリを再帰的に処理)
-l(シンボリックリンクをそのままコピー)
-p(ファイルのパーミッションを保持)
-t(ファイルのタイムスタンプを保持)
-g(ファイルの所有グループを保持) ※
-o(ファイルの所有ユーザを保持) ※
-D(デバイスファイル/スペシャルファイルをそのままコピー) ※
※ root権限が必要
-v 処理内容の表示
--delete 同期元に存在しないファイルを同期先から削除
2019/10/19更新
Amazon DynamoDBはAWSクラウド上だけでなくローカルでも実行できるバージョン(DynamoDBローカル)が提供されている。ローカルで実行することにより課金を気にせず動作確認や開発が可能になる。ここではUbuntu 18.04にDynamoDBローカルを導入してテーブルの作成やデータの操作を行ってみる。
DynamoDBローカル動作環境
DynamoDBローカルはjarファイルとDockerイメージの2種類が提供されているが、ここでは前者の方法で確認する。
jarファイルを利用する場合はあらかじめ以下をインストールしておく。
Java Runtime Environment (JRE) 6.x 以降
DynamoDBローカルインストール
まず以下からDynamoDBローカルのtar.gzをダウンロードする。
コンピュータ上の DynamoDB (ダウンロード可能バージョン)
ダウンロードしたら任意の場所に展開する。インストールはこれだけである。
$ tar zxvf dynamodb_local_latest.tar.gz
DynamoDBローカルサーバ起動
サーバの起動も簡単で、以下のようにすればよい。Ctrl + cで停止する。
$ java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb Initializing DynamoDB Local with the following configuration: Port: 8000 InMemory: false DbPath: null SharedDb: true shouldDelayTransientStatuses: false CorsParams: *
尚、デフォルトの待受ポートは8000なので必要に応じて変更する。(-port <n>で指定)
テーブル作成
サーバの起動ができたら実際にテーブルを作成してみる。
AWS CLIを使って以下のようにする。ここでは「test」というテーブルを作り、キーを「num」とする。
$ aws dynamodb create-table --table-name test \ --attribute-definitions AttributeName=num,AttributeType=N \ --key-schema AttributeName=num,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \ --endpoint-url http://localhost:8000 { "TableDescription": { "TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/test", "AttributeDefinitions": [ { "AttributeName": "num", "AttributeType": "N" } ], "ProvisionedThroughput": { "NumberOfDecreasesToday": 0, "WriteCapacityUnits": 1, "LastIncreaseDateTime": 0.0, "ReadCapacityUnits": 1, "LastDecreaseDateTime": 0.0 }, "TableSizeBytes": 0, "TableName": "test", "BillingModeSummary": { "LastUpdateToPayPerRequestDateTime": 0.0, "BillingMode": "PROVISIONED" }, "TableStatus": "ACTIVE", "KeySchema": [ { "KeyType": "HASH", "AttributeName": "num" } ], "ItemCount": 0, "CreationDateTime": 1571411233.367 } }
テーブルができていることを確認する。
$ aws dynamodb list-tables --endpoint-url http://localhost:8000 { "TableNames": [ "test" ] }
データ操作
テーブルが作成できたらデータを挿入/更新/削除してみる。
データ挿入
$ aws dynamodb put-item --table-name test \ --item '{"num":{"N":"1"},"data":{"S":"first"}}' \ --endpoint-url http://localhost:8000
データ取得
$ aws dynamodb get-item --table-name test \ --key '{"num":{"N":"1"}}' \ --endpoint-url http://localhost:8000 { "Item": { "num": { "N": "1" }, "data": { "S": "first" } } }
AWS CLIだけでなくブラウザからアクセスしてJavaScriptで操作することもできるので、以降はブラウザ上で実行する。
http://localhost:8000/shell/
データ挿入
左ペインに以下のコードを入力し、実行ボタン()をクリックすると右ペインに実行結果が表示される。
AWS.config.endpoint = new AWS.Endpoint('http://localhost:8000'); var dynamodb = new AWS.DynamoDB(); var params = { TableName: 'test', Item: { 'num':{N: '2'}, 'data':{S: 'second'} } }; dynamodb.putItem(params, function(err, data) { if(err) { console.log(err, err.stack); } else { console.log(data); } });
データ取得
AWS.config.endpoint = new AWS.Endpoint('http://localhost:8000'); var dynamodb = new AWS.DynamoDB(); var params = { TableName: 'test', Key: { 'num':{N: '2'} } }; dynamodb.getItem(params, function(err, data) { if(err) { console.log(err, err.stack); } else { console.log(data); } });
データ更新
AWS.config.endpoint = new AWS.Endpoint('http://localhost:8000'); var dynamodb = new AWS.DynamoDB(); var params = { TableName: 'test', Key: { 'num':{N: '2'} }, ExpressionAttributeNames: { '#d': 'data' }, ExpressionAttributeValues: { ':newData':{S: '2nd'} }, UpdateExpression: 'SET #d = :newData' }; dynamodb.updateItem(params, function(err, data) { if(err) { console.log(err, err.stack); } else { console.log(data); } });
データ削除
AWS.config.endpoint = new AWS.Endpoint('http://localhost:8000'); var dynamodb = new AWS.DynamoDB(); var params = { TableName: 'test', Key: { 'num':{N: '2'} } }; dynamodb.deleteItem(params, function(err, data) { if(err) { console.log(err, err.stack); } else { console.log(data); } });
テーブル削除
ひと通り確認が終わったのでテーブルを削除する。
$ aws dynamodb delete-table --table-name test \ --endpoint-url http://localhost:8000 { "TableDescription": { "TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/test", "AttributeDefinitions": [ { "AttributeName": "num", "AttributeType": "N" } ], "ProvisionedThroughput": { "NumberOfDecreasesToday": 0, "WriteCapacityUnits": 1, "LastIncreaseDateTime": 0.0, "ReadCapacityUnits": 1, "LastDecreaseDateTime": 0.0 }, "TableSizeBytes": 14, "TableName": "test", "BillingModeSummary": { "LastUpdateToPayPerRequestDateTime": 0.0, "BillingMode": "PROVISIONED" }, "TableStatus": "ACTIVE", "KeySchema": [ { "KeyType": "HASH", "AttributeName": "num" } ], "ItemCount": 1, "CreationDateTime": 1571411233.367 } }
AWS CLIやJavaScriptを使って他にも様々な操作ができるので以下のドキュメントを参照のこと。
DynamoDB - AWS CLI Command Reference
DynamoDB - AWS SDK for JavaScript
DynamoDBローカルのバックエンドはSQLite
DynamoDBローカルのバックエンドはSQLiteを使っているのでSQLiteの各種ツールで中身を参照することができる。
ファイルはDynamoDBローカルのサーバを起動したカレントディレクトリに「shared-local-instance.db」として作成される。
ファイルは一つだけなのでバックアップや別ホストへのコピーも容易に行える。
$ sqlite3 shared-local-instance.db SQLite version 3.22.0 2018-01-22 18:45:57 Enter ".help" for usage hints. sqlite> .tables cf dm sm ss test tr us
テーブルはそれぞれ以下の用途に使われている。
cf
DynamoDBローカルのバージョン
sqlite> .schema cf CREATE TABLE cf (version TEXT); sqlite> select * from cf; v2.4.0
dm
DynamoDBローカル内に定義したテーブルの情報
sqlite> .schema dm CREATE TABLE dm (TableName TEXT, CreationDateTime INTEGER, LastDecreaseDate INTEGER, LastIncreaseDate INTEGER, NumberOfDecreasesToday INTEGER, ReadCapacityUnits INTEGER, WriteCapacityUnits INTEGER, TableInfo BLOB, BillingMode INTEGER DEFAULT 0, PayPerRequestDateTime INTEGER DEFAULT 0, PRIMARY KEY(TableName)); sqlite> select * from dm; test|1571441645458|0|0|0|1|1|{"Attributes":[{"AttributeName":"num","AttributeType":"N"}],"GSIList":[],"GSIDescList":[],"SQLiteIndex":{"":[{"DynamoDBAttribute":{"AttributeName":"num","AttributeType":"N"},"KeyType":"HASH","SQLiteColumnName":"hashKey","SQLiteDataType":"BLOB"}]},"UniqueIndexes":[{"DynamoDBAttribute":{"AttributeName":"num","AttributeType":"N"},"KeyType":"HASH","SQLiteColumnName":"hashKey","SQLiteDataType":"BLOB"}],"UniqueGSIIndexes":[]}|0|0
sm / ss / us
DynamoDB Stream管理情報
sqlite> .schema sm CREATE TABLE sm (StreamID TEXT, StreamStatus TEXT, TableName TEXT, StreamInfo BLOB, CreationDateTime INTEGER, DeletionDateTime INTEGER, PRIMARY KEY(StreamID)); sqlite> .schema ss CREATE TABLE ss (StreamID TEXT, ShardID TEXT, CreationDateTime INTEGER, DeletionDateTime INTEGER, InitialSequenceNumberStart INTEGER, SequenceNumberEnd INTEGER, ParentShardID TEXT, PRIMARY KEY(ShardID)); sqlite> .schema us CREATE TABLE us (StreamID TEXT, ShardID TEXT, SequenceNumber INTEGER, CreationDateTime INTEGER, StreamRecord BLOB, OperationType TEXT, PRIMARY KEY(SequenceNumber)); sqlite> select * from sm; sqlite> select * from ss; sqlite> select * from us;
test
今回の導入手順でテスト用に作成したテーブル
sqlite> .schema test CREATE TABLE IF NOT EXISTS "test" (hashKey BLOB DEFAULT NULL, hashValue BLOB NOT NULL, itemSize INTEGER DEFAULT 0, ObjectJSON BLOB NOT NULL, PRIMARY KEY(hashKey)); CREATE INDEX "test*HVI" ON "test" (hashValue); sqlite> select * from test; >=E10000000000000000000000000000000000000|-��,q���� �T �����|14|{"data":{"S":"first"},"num":{"N":"1"}}
tr
トランザクション管理情報
sqlite> .schema tr CREATE TABLE tr (TransactionId TEXT, TransactionSignature BLOB, CreationDateTime INTEGER, PRIMARY KEY(TransactionId)); sqlite> select * from tr;
参考サイト
Amazon DynamoDB情報 (Qiita)
2019/03/06更新
セキュリティ要件などでCloudWatch Logsの特定のロググループを特定のIPからのみ参照可能にしたい場合は、IAMの以下の2つのポリシーを対象となるユーザやグループにアタッチすればよい。
※ ただしAWSの他のサービスからアクセスが必要な場合はこの方法は使えないので注意が必要
1. CloudWatchLogsFullAccessやCloudWatchLogsReadOnlyAccessのようなCloudWatch Logsにアクセスできるポリシー、あるいはそれに類するポリシー
2. 指定したロググループに対して特定のIPからのみアクセス可能とするポリシー(新規作成)
例) 「ng」というロググループに対してSourceIpで指定したIPからのみアクセス可能とする
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": "logs:DescribeLogStreams", "Resource": "arn:aws:logs:<リージョン>:<AWSアカウント>:log-group:ng:*", "Condition": { "NotIpAddress": { "aws:SourceIp": "xxx.xxx.xxx.xxx" } } }, { "Effect": "Deny", "Action": "logs:FilterLogEvents", "Resource": "arn:aws:logs:<リージョン>:<AWSアカウント>:log-group:ng:*", "Condition": { "NotIpAddress": { "aws:SourceIp": "xxx.xxx.xxx.xxx" } } } ] }
実行例
例えば以下の2つのロググループ/ログストリームが存在する状態で上記のポリシーを適用する。
ok/ok_test
ng/ng_test
この状態でロググループの一覧表示とログストリームの参照・検索を行うと以下のような挙動となる。
ロググループ一覧表示(両方表示される)
$ aws logs describe-log-groups { "logGroups": [ { "arn": "arn:aws:logs:xxxxxxxxxxxx:xxxxxxxxxxxx:log-group:ng:*", "creationTime": 1551426635634, "metricFilterCount": 0, "logGroupName": "ng", "storedBytes": 0 }, { "arn": "arn:aws:logs:xxxxxxxxxxxx:xxxxxxxxxxxx:log-group:ok:*", "creationTime": 1551426626316, "metricFilterCount": 0, "logGroupName": "ok", "storedBytes": 0 } ] }
ログストリーム参照(どのIPからでも参照可能)
$ aws logs describe-log-streams --log-group-name ok { "logStreams": [ { "creationTime": 1551426648336, "arn": "arn:aws:logs:xxxxxxxxxxxx:xxxxxxxxxxxx:log-group:ok:log-stream:ok_test", "logStreamName": "ok_test", "storedBytes": 0 } ] }
ログストリーム参照(特定のIPからのみ参照可能)
$ aws logs describe-log-streams --log-group-name ng An error occurred (AccessDeniedException) when calling the DescribeLogStreams operation: User: arn:aws:iam::xxxxxxxxxxxx:user/xxxx is not authorized to perform: logs:DescribeLogStreams on resource: arn:aws:logs:xxxxxxxxxxxx:xxxxxxxxxxxx:log-group:ng:log-stream: with an explicit deny
ログストリーム検索(どのIPからでも参照可能)
$ aws logs filter-log-events --log-group-name ok { "searchedLogStreams": [], "events": [] }
ログストリーム検索(特定のIPからのみ参照可能)
$ aws logs filter-log-events --log-group-name ng An error occurred (AccessDeniedException) when calling the FilterLogEvents operation: User: arn:aws:iam::xxxxxxxxxxxx:user/xxxx is not authorized to perform: logs:FilterLogEvents on resource: arn:aws:logs:xxxxxxxxxxxx:xxxxxxxxxxxx:log-group:ng:log-stream: with an explicit deny
参考サイト
送信元 IP に基づいて AWS へのアクセスを拒否する (Amazon Web Services)
2019/12/19更新
Amazon CloudFront(以下CloudFront)とEC2を用いてWebサービスなどを提供したい場合に、EC2へのHTTP(S)アクセスをCloudFrontからのみに絞りたい場合がある。ただCloudFrontからのアクセスを簡単に指定する方法はなく、以下で公開されているCloudFrontのIPアドレスを個別にセキュリティグループに設定する必要がある。(記事執筆時点で67個)
CloudFront エッジサーバーの場所と IP アドレス範囲 (Amazon CloudFront 開発者ガイド)
IPアドレス一つひとつをAWS CLIやマネジメントコンソール上で設定するのは大変なので、以下のワンライナーを使えば簡単にセキュリティグループに一括登録することができる。
なお、必要なツールとしてcurl、jq、awkはあらかじめインストールしておくこと。またここではEC2のTCP/80ポートへの接続許可とする。
$ sg="sg-xxxxxxxxx" $ curl http://d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips \ | jq -r '.[][]' \ | awk '{printf("echo %s;aws ec2 authorize-security-group-ingress --group-id %s --protocol tcp --port 80 --cidr %s\n",$1,"'${sg}'",$1)}' \ | sh
以下、一つのコマンド毎に説明する。
CloudFrontのIPアドレス取得(JSONフォーマット)
$ curl http://d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips {"CLOUDFRONT_GLOBAL_IP_LIST": ["144.220.0.0/16", ... , "34.232.163.208/29"]}
そこからjqでIPアドレスのみ取り出す
$ ... | jq -r '.[][]' 144.220.0.0/16 : 34.232.163.208/29
awkを使ってそのIPアドレスをAWS CLIでセキュリティグループに登録する構文を作る(時間がかかるので最初にIPアドレスをechoしておく)
$ ... | awk '{printf("echo %s;aws ec2 authorize-security-group-ingress --group-id %s --protocol tcp --port 80 --cidr %s\n",$1,"'${sg}'",$1)}' echo 144.220.0.0/16;aws ec2 authorize-security-group-ingress --group-id sg-xxxxxxxxx --protocol tcp --port 80 --cidr 144.220.0.0/16 : echo 34.232.163.208/29;aws ec2 authorize-security-group-ingress --group-id sg-xxxxxxxxx --protocol tcp --port 80 --cidr 34.232.163.208/29
これをshで実行する
$ ... | sh 144.220.0.0/16 : 34.232.163.208/29
なお、セキュリティグループには登録数の上限があるので気を付けること。
Amazon VPC の制限 (Amazon Virtual Private Cloud ユーザーガイド)
Amazon VPC でセキュリティグループルールの制限を増やす (AWS ナレッジセンター)
参考サイト
Amazon CloudFront (Amazon Web Services)
AWS技術情報 (Qiita)
2017/7/10更新
対応バージョン: 10.12.5
macOS SierraにAWS CLI環境を構築する手順を示す。
準備
最初にMac App StoreからXcodeをインストールしておく。
https://itunes.apple.com/jp/app/xcode/id497799835?mt=12
AWS CLIインストール
AWS CLIのインストールにはPythonのパッケージマネージャであるpipコマンドを使用するのでpip -> AWS CLIの順でインストールする。
pipインストール
% sudo easy_install pip % pip --version pip 9.0.1 from /Library/Python/2.7/site-packages (python 2.7)
awscliインストール
% pip install awscli --upgrade --user PATH環境変数に以下のパスを追加 $HOME/Library/Python/2.7/bin % aws --version aws-cli/1.11.117 Python/2.7.10 Darwin/16.6.0 botocore/1.5.80
JSON Lintインストール
AWS CLIにおけるJSONデータの表示・加工に便利なJSON Lintをインストールする。
JSON LintのインストールにはNode.js付属のnpmコマンドを使用するのでNode.js -> JSON Lintの順でインストールする。
nodebrewインストール
% curl https://raw.githubusercontent.com/hokaccha/nodebrew/master/nodebrew | perl - setup PATH環境変数に以下のパスを追加 $HOME/.nodebrew/current/bin % nodebrew selfupdate
Node.jsインストール
% nodebrew install latest PATH環境変数に以下のパスを追加 $HOME/.nodebrew/node/v8.1.3/bin % node -v v8.1.3 % npm -v 5.0.3
JSON Lintインストール
% npm install jsonlint PATH環境変数に以下のパスを追加 $HOME/node_modules/.bin % jsonlint -v 1.6.2 % echo '{"key":"value"}' | jsonlint { "key": "value" }
初期設定
Access KeyとSecret Access Keyを設定してAWS CLIが使用できるようにする。
% aws configure list Name Value Type Location ---- ----- ---- -------- profile <not set> None None access_key <not set> None None secret_key <not set> None None region <not set> None None % aws configure AWS Access Key ID [None]: xxxxxxxxxxxxxxxxxx AWS Secret Access Key [None]: xxxxxxxxxxxxxxxxxx Default region name [None]: ap-northeast-1 <- 東京リージョンの場合 Default output format [None]: json <- 他にtable、textが指定可能。好みに応じて指定 % aws configure list Name Value Type Location ---- ----- ---- -------- profile <not set> None None access_key ******************** shared-credentials-file secret_key ******************** shared-credentials-file region ap-northeast-1 config-file ~/.aws/config
設定は以下の2ファイルに格納されるが、それぞれのファイルの[default]の部分に一意の名前を付ければ複数の設定(プロファイル)を共存させられる。その場合、awsコマンドを実行する時に--profileに続けてプロファイル名を指定する。
$HOME/.aws/config
コマンド出力結果フォーマットとリージョン
[default] output = json region = ap-northeast-1
$HOME/.aws/credentials
認証情報
[default] aws_access_key_id = xxxxxxxxxxxxxxxxxx aws_secret_access_key = xxxxxxxxxxxxxxxxxx
動作確認
インスタンス一覧取得
% aws ec2 describe-instances | jq '.Reservations [] .Instances [] .InstanceId'
インスタンス起動
% aws ec2 start-instances --instance-ids i-xxxxxxxxxxxxxxxxx
インスタンス停止
% aws ec2 stop-instances --instance-ids i-xxxxxxxxxxxxxxxxx
インスタンス削除
% aws ec2 terminate-instances --instance-ids i-xxxxxxxxxxxxxxxxx
IAMユーザ取得
% aws iam list-users { "Users": [ { "UserName": "dev", "Path": "/", "CreateDate": "2017-07/10T04:34:38Z", "UserId": "xxxxxxxxxxxxxxxxxxxxx", "Arn": "arn:aws:iam::xxxxxxxxxxxx:user/dev" } ] } % aws iam get-user --query 'User.Arn' "arn:aws:iam::xxxxxxxxxxxx:user/dev"
参考サイト
AWS公式ドキュメント (Amazon Web Services)
AWS CLIドキュメント (Amazon Web Services)
Amazon Web Servicesブログ (Amazon Web Services)
Amazon Web Servicesスライド (Amazon Web Services)
cloudpack.media エンジニアブログ (アイレット)
Developers.IO (クラスメソッド)
サーバーワークス エンジニアブログ (サーバーワークス)
HIGHWAY for AWS (NEC)
ナレコムAWSレシピ (ナレコム)
AWS技術情報 (Qiita)
2019/12/01更新
AWSで見られる分かりにくい挙動やエラーなどについてメモとして記す。このメモではAWSのストレージサービスを扱う。
マネジメントコンソールの表示は日本語/英語を併記する
記載内容は記事執筆時点の情報なのでタイミングやAWS CLI、AWS SDKなどのバージョンによっても変わっている可能性がある
AWS が提供するクラウドストレージ (Amazon Web Services)
Amazon S3
Amazon S3 (Amazon Web Services)
Amazon Simple Storage Service ドキュメント (Amazon Web Services)
アクセス権の設定
バケットポリシーを設定しようとした際、ポリシー内で指定しているリソース(例: IAMロール)が事前に定義されていないと以下のエラーになるので事前に当該リソースを作成しておく必要がある。
$ aws s3api put-bucket-policy \ --bucket ${_BUCKET} \ --policy file://${_POLICY} An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: Invalid principal in policy
バケットポリシーとユーザーポリシーの使用 (Amazon Web Services)
2019/12/01更新
AWSで見られる分かりにくい挙動やエラーなどについてメモとして記す。このメモではAWSのデータベースサービスを扱う。
マネジメントコンソールの表示は日本語/英語を併記する
記載内容は記事執筆時点の情報なのでタイミングやAWS CLI、AWS SDKなどのバージョンによっても変わっている可能性がある
AWS が提供するクラウドデータベース (Amazon Web Services)
Amazon Aurora
Amazon Aurora (Amazon Web Services)
Amazon Relational Database Service ドキュメント (Amazon Web Services)
パラメータグループの変更
Aurora (MySQL)-5.6.10aにて、クラスタ起動後にdynamicタイプのパラメータグループの内容を変更すると本来はインスタンス再起動不要であるにも関わらず、パラメータグループのステータスが「同期中/in-sync」ではなく「再起動の保留中/pending-reboot」と表示されインスタンスの再起動を要求されることがある。
ただしこれは表示上だけの問題なので、実際の設定値が変わっていれば設定変更は完了しておりそのまま使い続けてよい。(AWSサポート回答より)
例) dynamicタイプのmax_connectionsを90から450に変更した場合
(変更前) mysql> select @@max_connections; +-------------------+ | @@max_connections | +-------------------+ | 90 | +-------------------+ (変更後) mysql> select @@max_connections; +-------------------+ | @@max_connections | +-------------------+ | 450 | +-------------------+ -> 実際に設定値が変わっていればよい
DB パラメータグループおよび DB クラスターパラメータグループを使用する (Amazon Web Services)
スロークエリログの記録タイミング
Aurora (MySQL)-5.6.10aにて、スロークエリログのタイムスタンプはSQL開始時刻ではなくSQL終了時刻となる。
例)
16:44:00にSQL開始
mysql> select now(); select sleep(10); +---------------------+ | now() | +---------------------+ | 2017-08-15 07:44:00 | +---------------------+ +-----------+ | sleep(10) | +-----------+ | 0 | +-----------+
スロークエリログの内容(タイムスタンプはSQL開始時刻ではなくSQL終了時刻)
# Time: 170815 7:44:10 # User@Host: awsuser[awsuser] @ [172.31.29.5] Id: 941 # Query_time: 10.000223 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0 SET timestamp=1502783050; select sleep(10);
MySQL データベースログファイル (Amazon Web Services)
2017/08/12更新
対応バージョン: Aurora(MySQL-Compatible) 5.6.10a
Amazon Auroraで出力されるログはデフォルトで以下の3種類がある。error/mysql-error-running.log{,.YYYY-MM-DD.nn}
error/mysql-error.log
external/mysql-external.log
Auroraではこれに加え、クエリログ(general_log)とスロークエリログ(slow_query_log)も出力することができる。
手順は以下の通りで、パラメータグループで設定することができる。尚、これら設定はAuroraインスタンスを再起動することなく動的に反映可能である。(反映にかかる時間は10秒程度)
出力先
まずログの出力先(log_output)がデフォルトで"TABLE"になっているので"FILE"に変更する。
続いて各ログの出力設定を行う。片方だけの設定も両方の設定もどちらも可能である。
クエリログ(general_log)
general_logの値を1(ON)に設定する。デフォルトは0(OFF)。
これにより以下のログが新たに出力されるようになる。
general/mysql-general.log
スロークエリログ(slow_query_log)
slow_query_logを1(ON)に設定する。デフォルトは0(OFF)。
さらにクエリが何秒以上かかった時にログを出力させるかをlong_query_timeで設定する。デフォルトは10(秒)で、0(秒)にすると遅延ありなしに関係なく全てのクエリがログに出力されるようになる。
これにより以下のログが新たに出力されるようになる。
slowquery/mysql-slowquery.log
参考サイト
Amazon Aurora (Amazon Web Services)
Amazon Relational Database Service ドキュメント (Amazon Web Services)
Amazon Aurora情報 (Qiita)