AnsibleでAWS操作シリーズ
- aws-cliインストール編
- EC2インスタンス編
- S3バケット編
- CloudFrontディストリビューション編
- Simple Email Service編
- Certificate Manager編
- Lambda編
関連記事
やりたかったこと
-
Lambda
関数の登録 -
S3
バケットにオブジェクトがPutされたタイミングでCloudFrontにInvalidationをかけ、全てのキャッシュをリセット GUIを使わずに黒い画面でコマンドを「ッターーン!」してかっこつけたい
やったこと
前提
- CIサーバー(
ansible
実行サーバー)構築済み - CLIサーバー(
aws-cli
実行サーバー)構築済み -
Ansible
インストール済み -
aws-cli
インストール済み - 各サーバーへのSSH接続設定済み
-
CF
とS3
の設定済み -
Lambda
実行用のiam
作成済み
※ ${~}
は各環境に合わせて値を設定してください。
作業フロー
1. 関数をLambdaにアップロード
ansible-playbook -i inventory/production upload-aws-lambda-function.yml
戻り値のarnをAWS.LAMBDA.ARNに指定する
2. lambda関数への実行権限の不要
ansible-playbook -i inventory/production update-aws-lambda-function.yml
3. S3バケットへのイベント設定
ansible-playbook -i inventory/production update-aws-s3-event.yml
ディレクトリ構成
├── ansible.cfg
├── templates
│ └── production
│ ├── lambda
│ │ └── lambda_function.j2
│ └── s3api
│ └── event.j2
├── inventory
│ └── production
│ └── inventory
├── roles
│ ├── update-aws-lambda-function
│ │ └── tasks
│ │ └── main.yml
│ ├── upload-aws-lambda-function
│ │ └── tasks
│ │ └── main.yml
│ └── update-aws-s3api-event
│ └── tasks
│ └── main.yml
├── update-aws-lambda-function.yml
├── upload-aws-lambda-function.yml
├── update-aws-s3api-event.yml
└── vars
└── all.yml
Ansible構成ファイル
inventory
[ciservers]
${CIサーバーホスト}
[cliservers]
${CLIサーバーホスト}
[all:vars]
ENV=production
vars
AWS:
CLOUD_FRONT:
DISTRIBUTION:
ID: ${ディストリビューションID}
IAM:
LAMDA:
ROLE:
ARN: ${ラムだ関数実行ロールARN}
LAMBDA:
ARN: ${lambda関数のarn}
CLOUD_FRONT:
INVALIDATION:
NAME: ${Lambda関数名}
STATEMENT: ${ステートメント名}
SOURCE_ARN: ${ソースARN}
S3:
BUCKET:
NAME: ${対象バケット名}
S3API:
LAMBDA_FUNCTION:
ID: ${一意となるID}
TEMP:
DIRECTORY: /temp
templates
from __future__ import print_function
import boto3
import time
def lambda_handler(event, context):
client = boto3.client('cloudfront')
invalidation = client.create_invalidation(DistributionId='{{ AWS.CLOUD_FRONT.DISTRIBUTIO.IDN }}',
InvalidationBatch={
'Paths': {
'Quantity': 1,
'Items': ['/*']
},
'CallerReference': str(time.time())
})
for items in event["Records"]:
path = "/" + items["s3"]["object"]["key"]
print(path)
内容としては、 対象のバケットに一つでもオブジェクトがPutされたら全てのキャッシュを削除 します。
※最初は、Putされたオブジェクトだけ にInvalidation
をかけていましたが、 静的ホスティング として利用していて、かつ デフォルトインデックスドキュメント を指定 していた場合に、 「**/」にアクセスした際の リダイレクトキャッシュ がInvaidationされずに古いキャッシュが残ってしまっていました。
原因としては、CloudFrontのキャッシュの仕様上、 ブラウザのURL単位 でキャッシュをしているようです。
なので、PutされたオブジェクトのみInvalidationをしてもリダイレクトキャッシュは残ってしまっていたようです。
やり方はあると思いますが、そこは調べる時間が取れなかったので一旦 リダイレクトキャッシュ含む全てのオブジェクト に対してInvalidation
をかけています。
{
"LambdaFunctionConfigurations": [
{
"LambdaFunctionArn": "{{ AWS.LAMBDA.ARN }}",
"Id": "{{ AWS.S3API.LAMBDA_FUNCTION.ID }}",
"Events": [
"s3:ObjectCreated:*"
]
}
]
}
playbook
- hosts: cliservers
roles:
- upload-aws-lambda-function
vars_files:
- vars/all.yml
- hosts: cliservers
roles:
- update-aws-lambda-function
vars_files:
- vars/all.yml
- hosts: cliservers
roles:
- update-aws-s3api-event
vars_files:
- vars/all.yml
tasks
- name: Create File
template:
src={{ ENV }}/lambda/lambda_function.j2
dest={{ TEMP.DIRECTORY }}/lambda/lambda_function.py
tags:
- always
-
- name: "Create Zip"
shell: zip -r {{ TEMP.DIRECTORY }}/lambda_function.zip *
args:
chdir: "{{ TEMP.DIRECTORY }}/lambda"
tags:
- always
- name: "Upload Zip"
shell: |
aws lambda create-function \
--function-name {{ AWS.LAMBDA.CLOUD_FRONT.INVALIDATION.NAME }} \
--zip-file fileb://{{ TEMP.DIRECTORY }}/lambda_function.zip \
--role {{ AWS.IAM.LAMDA.ROLE.ARN }} \
--handler lambda_function.lambda_handler \
--runtime python2.7 \
--timeout 10 \
--memory-size 1024
register: result
changed_when: False
- debug: var=result.stdout_lines
when: result | success
tags:
- always
- name: "Add Permission"
shell: |
aws lambda add-permission \
--function-name "{{ AWS.LAMBDA.CLOUD_FRONT.INVALIDATION.NAME }}" \
--statement-id "{{ AWS.LAMBDA.CLOUD_FRONT.INVALIDATION.STATEMENT }}" \
--action "lambda:InvokeFunction" \
--principal "s3.amazonaws.com" \
--source-arn "{{ AWS.LAMBDA.CLOUD_FRONT.INVALIDATION.SOURCE_ARN }}"
register: result
changed_when: False
- debug: var=result.stdout_lines
when: result | success
tags:
- always
- name: Create File
template:
src={{ ENV }}/s3api/event.j2
dest={{ TEMP.DIRECTORY }}/event.json
tags:
- always
-
- name: "Update Zip"
shell: |
aws s3api put-bucket-notification-configuration \
--bucket {{AWS.S3.BUCKET.NAME }} \
--notification-configuration file://{{ TEMP.DIRECTORY }}/event.json
register: result
changed_when: False
- debug: var=result.stdout_lines
when: result | success
tags:
- always
終わりに
これで、対象のS3
バケットにオブジェクトをPut
するとCloudFront
にInvalidation
がかかり、キャッシュがリセットされると思います。
また、Lambda実行iamにCloudWatch
の権限をつけることで、Lambda
関数の実行ログが 自動 で出力されるので、監視も非常にしやすいです♪
S3
+CloudFront
の組み合わせは静的サイトを作成する際にはパフォーマンス面で非常に強力です。
しかし、しっかりとキャッシュを意識しないと更新内容が即時に反映されずに逆効果になってしまう場合もあります。
都度手動でインバリデーションをかけるのは運用的にも大変ですし、ヒューマンエラーも起きやすくなるので、是非Lambda
を利用して自動化しておきましょう♪
じゃあの。