この記事のゴール
・Serverless Frameworkの基本的な使い方を学ぶ。
・ESLint × PrettierでTypeScriptのソースコードを自動整形できるようにする。
・S3アップロードイベントで実行されるLambda関数を作成してみる。
→ 実用性がありそう&応用が効きそうなケース
もくじ
o1. Serverless Frameworkプロジェクト基盤作成
o2. S3アップロード時にLambdaが発火するようにする。
o3. CSVファイル読込
o4. mysqlインポート
ソースコード
o1. Serverless Frameworkプロジェクト基盤作成
プロジェクトフォルダ作成&npm init
でpackage.json作成
mkdir serverless-framework
cd serverless-framework
npm init
serverless CLIインストール
npm install -g serverless
必要なライブラリ追加
// TypeScript
npm install --save-dev typescript
// Serverless関連
npm install --save-dev serverless-offline
npm install --save-dev serverless-offline-lambda
npm install --save-dev serverless-s3-local
npm install --save-dev serverless-plugin-typescript
// AWS関連
npm install aws-lambda
npm install --save-dev @types/aws-lambda
npm install aws-sdk
npm install --save-dev @types/aws-sdk
// CSVパース関連
npm install async
npm install --save-dev @types/aws-sdk
npm install csv-parse
npm install iconv-lite
// DB関連
npm install promise-mysql
TypeScriptコンパイル設定(tsconfig.json追加)
TypeScriptコンパイル用の設定ファイル(tsconfig.json)をルートディレクトリに追加する。tsconfig.jsonの詳細はこちら。
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"lib": ["es2020"],
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"moduleResolution": "node",
"baseUrl": "src",
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["dist", "node_modules"],
}
ESLint導入
・ESLintとはJavaScriptのリンター(ソースコードを分析し問題点を指摘してくれる静的解析ツール)である。
・JavaScriptのリンターであるESLintにTypeScriptを適用するためには、下記のプラグインが必要なので追加する。
npm i -D eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser
lint
コマンドでエラー検知できるようにする
ルート直下に.eslintrc.json
を作成。
{
"root": true, // .eslintrc.jsonがプロジェクトのルートに配置させているか(指定がないと上位階層へ設定ファイルを探索される)
"parser": "@typescript-eslint/parser", // ESLintにTypeScriptを理解させる
"parserOptions": {
"project": "./tsconfig.json" // tsconfig.jsonの場所を指定
},
"plugins": [
"@typescript-eslint" // ESLintのTypeScriptプラグインのルールを適用できる様にする(/eslint-pluginは省略可)
],
"extends": [
"eslint:recommended", // ESLintのJavaScriptルールセットを適用
"plugin:@typescript-eslint/eslint-recommended", // eslint:recommendedに含まれるルールを型チェックでカバーできるものは無効化
"plugin:@typescript-eslint/recommended-requiring-type-checking", // 型チェックが必要なルールを適用
]
}
package.jsonにlintコマンドを追加する。
"scripts": {
"lint": "eslint . --ext ts"
}
試しに下記のsrc/hello.ts
を作成してみて、yarn lint
を実行してみる。
var hello = undefined;
ちゃんと怒られる。
% yarn lint
yarn run v1.22.17
$ eslint . --ext ts
/Users/daisuke/Desktop/serverless-practice/src/hello.ts
1:1 error Unexpected var, use let or const instead no-var
1:5 error 'hello' is assigned a value but never used no-unused-vars
✖ 2 problems (2 errors, 0 warnings)
1 error and 0 warnings potentially fixable with the `--fix` option.
error Command failed with exit code 1.
修正して再度yarn lint
実行。
const hello = "";
console.log(hello);
無事にlintコマンドが通るようになった!
% yarn lint
yarn run v1.22.17
$ eslint . --ext ts
✨ Done in 3.29s.
ファイル保存時に自動フォーマットを適用する
Prettierインストール
コードを自動整形してくれるツールとしてPrettierをインストールする。
npm install --save-dev prettier eslint-config-prettier
ESLint「ここのソースコード、汚えから整形した方がええで」
Prettier「おk。整形しとくわ」
というように、ESLintで設定したルールに基づいて自動フォーマットを行うには「ESLint✖️Prettier」の両方必要というわけだ。
eslint-config-prettierは ESLint のコードフォーマットに関連するルールを無効化し、バグを検出するルールのみを有効にするプラグイン。
.eslintrc.json
のextendsにPrettierを追加
{
/* 中略 */
extends: [
/* 中略 */
"prettier", // 追加。他の設定の上書きを行うために、必ず最後に配置する。
],
};
VSCode拡張機能インストール
ESLint(dbaeumer.vscode-eslint)と Prettier(esbenp.prettier-vscode) の拡張機能をインストールする。
.vscode/settings.json追加
ルート直下に.vscode/settings.json
を追加する。
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
ここまでの設定を完了すれば、VSCodeでのファイル保存時に.eslintrc.json
の設定に基づいてPrettierが自動フォーマットしてくれる👍
o2. S3アップロード時にLambdaが発火するようにする。
準備系が一通り終わったので、次にS3アップロードしたらLambdaを発火させる
というのをローカル環境で動作確認できるようにする。
serverless.yml追加
Serverless Frameworkの設定ファイル。
service: serverless-practice
frameworkVersion: '3'
// プロバイダ(AWS/GCP/Azure)関連の設定
provider:
name: aws
runtime: nodejs14.x
region: ${opt:region, 'us-east-1'}
stage: ${opt:stage, 'offline'}
endpointType: private
// process.env.XXXX で参照したい変数をここに定義する。
environment: ${file(serverless-config/env/env.${self:provider.stage}.yml)}
plugins:
// ---.tsをコンパイル対象にする
- serverless-plugin-typescript
// ローカル環境でLambda動作確認用
- serverless-offline-lambda
- serverless-offline
// ローカル環境でS3動作確認用
- serverless-s3-local
custom:
// serverless-s3-local用の情報
// ./tmpで http://localhosst:4568 でS3をホストする
s3:
host: localhost
port: 4569
directory: ./tests/resources
// sls offline start, sls deploy時にS3バケットを作成する。
resources:
Resources:
NewResource:
Type: AWS::S3::Bucket
Properties:
BucketName: local-bucket
// Lambda関数
functions:
csvRead:
handler: src/functions/csvRead/handler.run
events:
- s3:
bucket: local-bucket
event: s3:ObjectCreated:*
src/functions/csvRead/handler.ts追加
import { S3Event } from "aws-lambda";
export const run = (event: S3Event) => {
if (!event.Records || event.Records.length == 0) {
return null;
}
console.log(`BucketName: ${event.Records[0].s3.bucket.name}`);
console.log(`ObjectName: ${event.Records[0].s3.object.key}`);
};
動作確認
ローカルS3接続用のAWS認証情報設定
% aws configure --profile s3local
AWS Access Key ID [None]: S3RVER
AWS Secret Access Key [None]: S3RVER
Default region name [None]: us-east-1
Default output format [None]: json
テスト用CSVファイル用意 / ローカルS3バケット用のフォルダ作成
・ルート直下にtests/resources/sample_user.csv
を作成する。
Lambda/S3バケットをローカル起動
% npx sls offline start
starting handler
Found S3 event listener for local-bucket
Offline Lambda Server listening on http://localhost:4000
S3 local started ( port:4569, family: IPv4, address: 127.0.0.1 )
Compiling with Typescript...
Using local tsconfig.json - tsconfig.json
Warning: "rootDir" from local tsconfig.json is overriden
Typescript compiled.
Watching typescript files...
Starting Offline at stage dev (us-east-1)
Offline [http for lambda] listening on http://localhost:3002
Function names exposed for local invocation by aws-sdk:
* csvRead: serverless-practice-dev-csvRead
ローカルS3にファイルアップロード
別のターミナル/コマンドプロンプトを起動して、S3アップロードコマンドを実行する。
% aws --endpoint http://localhost:4569 --profile s3local s3 cp ./tests/resources/sample_user.csv s3://local-bucket/sample_user.csv
upload: tests/resources/sample_user.csv to s3://local-bucket/sample_user.csv
npx sls offline start
を実行したターミナル側では、以下のログが出力される。
info: Stored object "sample_user.csv" in bucket "local-bucket" successfully
BucketName: local-bucket
ObjectName: sample_user.csv
info: PUT /local-bucket/sample_user.csv 200 46ms 0b
・S3バケット(local-bucket)にsample_user.csvが無事にアップロードされたこと
・アップロードをトリガーにLambda関数(src/functions/csvRead/handler.run)起動されていること
が確認できる。
o3. CSVファイル読込
S3にアップロードされたCSVファイルの中身を読み込んで標準出力してみる。
src/utils/s3Client.ts追加
import AWS from "aws-sdk";
import * as stream from "stream";
let S3: AWS.S3 | null = null;
async function connectS3(): Promise<AWS.S3> {
if (S3 == null) {
if (process.env.NODE_ENV == "offline") {
S3 = new AWS.S3({
// force urls like http://{host}/{bucket}/{key} instead of http://{bucket}.
s3ForcePathStyle: true,
endpoint: new AWS.Endpoint(process.env.S3_ENDPOINT || ""),
accessKeyId: "S3RVER",
secretAccessKey: "S3RVER",
});
} else {
S3 = new AWS.S3({
// force urls like http://{host}/{bucket}/{key} instead of http://{bucket}.
s3ForcePathStyle: true,
endpoint: new AWS.Endpoint(process.env.S3_ENDPOINT || ""),
});
}
}
return S3;
}
export async function getObjectReadStream(key: string): Promise<stream.Readable> {
const request: AWS.S3.GetObjectRequest = {
Bucket: process.env.S3_BUCKET || "",
Key: key,
};
const s3: AWS.S3 = await connectS3();
return await s3
.getObject(request)
.createReadStream()
.on("error", (err: Error) => {
console.log(err);
return null;
});
}
環境変数定義ファイル追加
S3やDB関連のように環境ごとに値が変わるものはserverless-config/env/env.${ENV}.yml
に定義する。
NODE_ENV: ${self:provider.stage}
// S3
S3_ENDPOINT: http://localhost:4569
S3_BUCKET: local-bucket
src/functions/csvRead/handler.ts修正
・CSVパーサでファイル読込&標準出力する。
import { S3Event } from "aws-lambda";
import { Parser, parse } from "csv-parse";
import iconv from "iconv-lite";
import { getObjectReadStream } from "../../utils/s3Client";
export const run = async (event: S3Event) => {
if (!event.Records || event.Records.length == 0) {
return null;
}
// get csv file's s3 object key
const objectKey = event.Records[0].s3.object.key;
// configure how csv parser processes each row
const parser: Parser = parse();
parser.on("readable", () => {
let row;
while ((row = parser.read())) {
console.log(row);
}
});
// read csv file
const csvReadStream = await getObjectReadStream(objectKey);
// write stream into csv parser
csvReadStream.pipe(iconv.decodeStream("Shift_JIS")).pipe(parser);
};
動作確認
・CSVファイル(tests/resources/sample_user.csv
)に下記を追記する。
'00001','Anthony',10
'00002','Bryant',20
'00003','Carmelo',30
・先ほどと同じくnpx sls offline start
でローカル起動してから、別ターミナルからCSVファイルをS3にアップロードする。
% aws --endpoint http://localhost:4569 --profile s3local s3 cp ./tests/resources/sample_user.csv s3://local-bucket/sample_user.csv
% npx sls offline start
〜〜一部割愛〜〜
Function names exposed for local invocation by aws-sdk:
* csvRead: serverless-practice-offline-csvRead
info: Stored object "sample_user.csv" in bucket "local-bucket" successfully
offline: (λ: csvRead) RequestId: cl36vrfq70000rgo03zy44q38 Duration: 533.83 ms Billed Duration: 534 ms
info: PUT /local-bucket/sample_user.csv 200 577ms 0b
info: GET /local-bucket/sample_user.csv 200 8ms 84b
[ '00001', 'Anthony', 10 ]
[ '00002', 'Bryant', 20 ]
[ '00003', 'Carmelo',30 ]
↑の通り、S3にアップロードしたCSVファイルが読み込まれ、中身が標準出力に表示されている👍
o4. mysqlインポート
CSVファイルに格納されているデータがDB投入用データ
と仮定して、次の動きを実現する。
S3アップロード → CSVファイル読込 → MySQLにデータ投入
docker-compose.yml追加
・ローカルでMySQLコンテナを起動するためのdocker-compose.yml
をルート直下に作成。
version: "3.8"
services:
db:
image: mysql:latest
container_name: serverless-practice-db
environment:
MYSQL_DATABASE: serverlessPracticeDb
MYSQL_ROOT_PASSWORD: passw@rd
TZ: "Asia/Tokyo"
volumes:
- ./docker/db/data:/var/lib/mysql
- ./docker/db:/tmp/db
- ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
ports:
- 3309:3306
docker/db/my.cnf
作成
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default_authentication_plugin = mysql_native_password
[client]
default-character-set=utf8mb4
default_authentication_plugin = mysql_native_passwordについて
・MySQL8.0以降、認証プラグインは「caching_sha2_password」がデフォルト。
・生パスワード(mysql_native_password)で認証したい場合は、my.cnfの[mysqld]にて下記の設定を行う。
[mysqld]
default_authentication_plugin = mysql_native_password
DB情報を環境定義ファイルに追加
先ほど作成したserverless-config/env/env.offline.yml
に、DB情報を追加する。
NODE_ENV: ${self:provider.stage}
// S3
S3_ENDPOINT: http://localhost:4569
S3_BUCKET: local-bucket
// DB
DB_HOST: localhost
DB_PORT: 3309
DB_NAME: serverlessPracticeDb
DB_USER: root
DB_PASSWORD: passw@rd
テーブル作成SQLファイル作成
docker/db/create_table.sql
作成。
create table if not exists sample_user (
user_id char(15),
user_name varchar(50),
age int(2)
);
MySQLコンテナ起動&テーブル作成
// MySQLコンテナ起動
docker compose up -d
// コンテナログイン
docker exec -it serverless-practice-db bash -p
// テーブル作成SQL実行
root@f8bda5a9fe02:/# cd /tmp/db
root@f8bda5a9fe02:/tmp/db# mysql --user=root --password=passw@rd < create_table.sql serverlessPracticeDb
// データベースserverlessPracticeDb に sample_userテーブルが作成されていること
root@f8bda5a9fe02:/tmp/db# mysql --user=root --password=passw@rd serverlessPracticeDb
mysql> show tables;
+--------------------------------+
| Tables_in_serverlesspracticedb |
+--------------------------------+
| sample_user |
+--------------------------------+
1 row in set (0.01 sec)
src/utils/dbUtils.ts作成
・MySQLに接続しSQLを実行するための処理を追加。
import mysql from "promise-mysql";
import BlueBird from "bluebird";
export async function createDbConnection(): Promise<BlueBird<mysql.Connection>> {
return mysql.createConnection({
host: process.env.DB_HOST,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
port: process.env.NODE_ENV == "offline" ? 3309 : 3306,
timezone: "jst",
});
}
src/functions/csvRead/handler.ts修正
・CSVファイルの中身をINPUTに、MySQLに対してINSERT文を実行する。
import { S3Event } from "aws-lambda";
import { Parser, parse } from "csv-parse";
import iconv from "iconv-lite";
import mysql from "promise-mysql";
import { getObjectReadStream } from "../../utils/s3Client";
import { createDbConnection } from "../../utils/dbUtils";
export const run = async (event: S3Event) => {
if (!event.Records || event.Records.length == 0) {
return null;
}
const objectKey = event.Records[0].s3.object.key;
// create db connection
const mysqlConnection: mysql.Connection = await createDbConnection();
// configure how csv parser processes each row
const parser: Parser = parse();
parser.on("readable", () => {
let row;
while ((row = parser.read())) {
const statement = `INSERT INTO sample_user VALUES(${row})`;
console.log(`executed: ${statement}`);
mysqlConnection.query(statement);
}
});
// read csv file
const csvReadStream = await getObjectReadStream(objectKey);
// write stream into csv parser
csvReadStream.pipe(iconv.decodeStream("Shift_JIS")).pipe(parser);
};
動作確認
Lambda起動
npx sls offline start
ローカルS3にCSVファイルアップロード
aws --endpoint http://localhost:4569 --profile s3local s3 cp ./tests/resources/sample_user.csv s3://local-bucket/sample_user.csv
Lambda起動中のターミナルログ
info: Stored object "sample_user.csv" in bucket "local-bucket" successfully
info: PUT /local-bucket/sample_user.csv 200 811ms 0b
offline: (λ: csvRead) RequestId: cl38pcfy6000092o05fir7azs Duration: 1043.45 ms Billed Duration: 1044 ms
info: GET /local-bucket/sample_user.csv 200 6ms 62b
executed: INSERT INTO sample_user VALUES('00001','Anthony',10)
executed: INSERT INTO sample_user VALUES('00002','Bryant',20)
executed: INSERT INTO sample_user VALUES('00003','Carmelo',30)
DBに反映されていること
// コンテナログイン
docker exec -it serverless-practice-db bash -p
// DBログイン
mysql --user=root --password=passw@rd serverlessPracticeDb
// 確認
mysql> select * from sample_user;
+---------+-----------+------+
| user_id | user_name | age |
+---------+-----------+------+
| 00001 | Anthony | 10 |
| 00002 | Bryant | 20 |
| 00003 | Carmelo | 30 |
+---------+-----------+------+
3 rows in set (0.00 sec)
o5. RDS作成 ※後日追記
o6. デプロイ(RDSと同じVPC内にLambda作成) ※後日追記
余談
MySQL「複数のINSERT文」「1つのINSERT文with複数VALUE」どっちが速い?
結論:1つのINSERT文with複数VALUE の方が速い。
MySQL INSERT multiple rows limit
max_allowed_packet
に設定されている。
mysql> SHOW VARIABLES LIKE 'max_allowed_packet';
+--------------------+---------+
| Variable_name | Value |
+--------------------+---------+
| max_allowed_packet | 4194304 |
+--------------------+---------+
1 row in set (0.11 sec)
MySQL my.cnf設定関連
mysql> SHOW VARIABLES like 'char%';
+--------------------------+--------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.00 sec)
mysql> SET character_set_results = utf8mb4;
serverless.ymlチートシート
service: serverless-practice
frameworkVersion: '3'
// プロバイダ(AWS/GCP/Azure)関連の設定
provider:
name: aws
runtime: nodejs14.x
region: ${opt:region, 'us-east-1'}
stage: ${opt:stage, 'offline'}
endpointType: private
// process.env.XXXX で参照したい変数をここに定義する。
environment: ${file(serverless-config/env/env.${self:provider.stage}.yml)}
iam.role
versionFunctions
tags
s3
plugins:
// ---.tsをコンパイル対象にする
- serverless-plugin-typescript
// ローカル環境でLambda動作確認用
- serverless-offline-lambda
- serverless-offline
// ローカル環境でS3動作確認用
- serverless-s3-local
custom:
// serverless-s3-local用の情報
// ./tmpで http://localhosst:4568 でS3をホストする
s3:
host: localhost
port: 4569
directory: ./tests/resources
// CloudFormationで作成したいAWSリソースを定義する。
// resources配下に定義するのは、CloudFormation templateそのもの。
resources:
Resources:
NewResource:
Type: AWS::S3::Bucket
Properties:
BucketName: local-bucket
// Lambda関数
functions:
csvRead:
handler: src/functions/csvRead/handler.run
events:
- s3:
bucket: local-bucket
event: s3:ObjectCreated:*
S3バケット関連の設定
provider.s3.HogeBucket
に定義可能ならプロパティは公式ドキュメントに一覧化されている。
# バケットに設定可能なプロパティは
provider:
s3:
fileBucket:
name: ${self:custom.s3.bucketName}
bucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
KMSMasterKeyID: ${self:custom.conf.KMS_KEY_ARN}
SSEAlgorithm: 'aws:kms'
publicAccessBlockConfiguration:
BlockPublicAcls: true
IgnorePublicAcls: true
BlockPublicPolicy: true
RestrictPublicBuckets: true
loggingConfiguration:
DestinationBucketName: ${self:custom.conf.S3_LOGGING_BUCKET}
LogFilePrefix: ${self:custom.s3.bucketName}-
tags: ${file(serverless-config/tags-s3.yml)}
# serverless-s3-local用の情報
# ローカル動作用の設定(ホストURLとホストディレクトリの設定のみ)
# ./tmpで http://localhosst:4568 でS3をホストする
custom:
s3:
host: localhost
port: 4569
directory: ./tests/resources
バケット関連の設定を理解する。
プロパティ名 | 意味 |
---|---|
BucketEncryption |
S3について理解する
① サーバ側の暗号化
② 暗号化キータイプ
③ ブロックパブリックアクセス設定
④ バージョニング
① "サーバ側"の暗号化とは?
・S3オブジェクトを暗号化して保存する。
・暗号/復号に使う鍵は、Amazon S3に作成/管理させる/自分でKSMに鍵を作る/別で暗号化キーを用意する のいずれかの方法で用意する。
・SSE-S3(Amazon S3 が作成、管理、使用する暗号化キー)で暗号化する場合、暗号化にお金はかからない。
・SSE-S3のデフォルト暗号化設定をカスタマイズする場合は、別料金がかかる。
There are no additional fees for using server-side encryption with Amazon S3-managed keys (SSE-S3). However, requests to configure the default encryption feature incur standard Amazon S3 request charges. For information about pricing, see Amazon S3 pricing.
② 3つの暗号化キータイプ
キータイプ | 特徴 |
---|---|
SSE-S3 | Amazon S3 が作成、管理、使用する暗号化キー AES-256方式の共通鍵を使う。 |
SSE-KMS | AWS Key Management Service (AWS KMS) で保護されている暗号化キー。 |
SSE-C | ユーザーが暗号化キーを管理 |
特に要件がなければ「SSE-S3」を。
複数のバケットに対して1つの暗号鍵を利用したい場合などは「SSE-KMS」を。
他に要件があれば「SSE-C」を。
で一旦OKな気がする。
③ ブロックパブリックアクセス設定
↑の設定を理解するためには「ACL(アクセスコントロールリスト)」「バケットポリシー」を理解する必要がある。
ACL(アクセスコントロールリスト)
・バケット/オブジェクトへのアクセス権限を付与する対象をここに定義する。
・付与対象は①AWSユーザー②事前定義済みS3グループ
・付与する単位について、
対象 | アクション | 意味 |
---|---|---|
バケット | READ | バケット内の全オブジェクトを「リスト/一覧化」可能 |
オブジェクト | READ | オブジェクトデータ、メタデータの参照可能 |
バケット | WRITE | バケット内にオブジェクトを作成可能 |
オブジェクト | WRITE | ※該当しない※ |
バケット | READ-ACP | バケットACLを読み込み可能 |
オブジェクト | READ-ACP | オブジェクトACLを読み込み可能 |
バケット | WRITE-ACP | バケットACLを書き込み可能 |
オブジェクト | WRITE-ACP | オブジェクトACLを書き込み可能 |
バケット | FULL_CONTROL | バケットに対するREAD/WRITE/READ_ACL/WRITE_ACLと同義 |
オブジェクト | FULL_CONTROL | オブジェクトに対するREAD/WRITE/READ_ACL/WRITE_ACLと同義 |
✅ ACL = 文字通りバケット/オブジェクトにアクセスさせる対象を管理できるもの。
✅ 「バケットのACL」と「オブジェクトのACL」の2つある。
✅ ACLによるアクセス制御の管理単位は「AWSユーザー」なので、ACLによるアクセス制御ではなくバケットポリシーを使うべき。
バケットポリシー
・デフォルトで「全拒否」なので、設定してあげる必要がある。
・ACLと異なり、アクセス制御の管理単位は下記のように幅広く設定可能。
AWSアカウント/ルートユーザー
IAMユーザー
IAMロール
AWSサービス
AWS「パブリックアクセスは基本的にさせないようにしよ?」
文字通り「AWSアカウントを持たない外部の人間をアクセスできる状態」が「パブリックアクセス」である。S3の基本的な考え方として「S3にあげるものはパブリックアクセスできないもの」であるので、作成されるバケット/オブジェクトがパブリックアクセスされないようにする設定/オプションがあるわけだ。
パブリックアクセスをブロックするための設定
① バケットごとの設定
② AWSアカウントが保有する全バケットに対する設定
パブリックアクセスに対する4つのブロッキング設定 -> 参考
種類 | 説明 |
---|---|
BlockPublicAcls | 未来にパブリックなACLに変更することを阻止する |
IgnorePublicAcls | 現在のパブリックなACLを無効化する |
BlockPublicPolicy | 未来にパブリックなポリシーに変更することを阻止する |
RestrictPublicBuckts | 現在のパブリックなポリシーを無効化する |
【BlockPublicAcls】 新しいアクセスコントロールリスト (ACL) を介して付与されたバケットとオブジェクトへのパブリックアクセスをブロックする
新しくバケット/オブジェクトを作る。
↓
バケット/オブジェクトに紐づくACLが存在する。
↓
このACLが「パブリックアクセスできるようなもの」であっても、パブリックアクセスできないようにする。
↓
未来に作られるパブリックなACLをBlockする。
【IgnorePublicAcls】 任意のアクセスコントロールリスト (ACL) を介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする
これから新しく作るバケット/オブジェクトがパブリックアクセスできるようなACLであってもパブリックアクセスできないようにする【1】に対して、【2】は対象が全て(これから新しく作る+既存のもの)となる。
↓
既存+未来で作られるパブリックなACLを全てIgnoreする。
【BlockPublicPolicy】 新しいパブリックバケットポリシーを介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする
【BlockPublicAcls】のバケット版。バケットポリシーでパブリックアクセスできないようにする。
【RestrictPublicBuckts】 任意のパブリックバケットポリシーを介して、バケットとオブジェクトへのパブリックアクセスとクロスアカウントアクセスをブロックする
【IgnorePublicAcls】のバケット版。バケットポリシーでパブリックアクセスできないようにする。
④ バージョニング
その他MEMO
そもそも暗号鍵/共通鍵暗号方式/AESとは
暗号鍵とは「暗号化したり、元に戻したり(復号)するときに使うデータ/文字列」
暗号化方式とは「暗号化のやり方(方式)」
暗号方式の1つに「共通鍵暗号方式」がある。データの送信者と受信者が持つ鍵は他者に奪われてはいけないものなので「秘密鍵暗号方式」とも呼ぶ。
共通鍵暗号方式の1つとして「AES(Advanced Encryption Standard)」が挙げられる。
serverlessによるデプロイはデフォルトでバージョニングされる→容量圧迫
serverless frameworkではデフォルトで、デプロイした全てのバージョンを保持するようになっています。
デプロイするたびに上書きされると思いきや、バージョニングされることで古いものは残りっぱなしになるわけだ。
不要ならserverless.yml
にversionFunctions: false
を定義すればバージョニングされない👍
provider:
versionFunctions: false
sls deploy
時に発生するError: EPERM: operation not permitted, unlink
の対処方法
原因はこのissueにあるとおり、serverless-typescript-pluginのバグ。
中間ファイルの存在がネックとなって引き起こされるバグらしいので、この中間ファイルを消した上でsls deploy
を実行する。
rm -rf .serverless
rm -rf .build
sls deploy
時に発生するs3 bucket already exists in stack
の原因・対処方法
S3バケットを生成するserverelssプロジェクトとして、下記のserverless.ymlを作成した。
しかしこのserverless.ymlだと2つのS3バケットを作ってしまう。
1つめ:NewResourceによるS3バケット作成
2つめ:events.s3によるS3バケット作成 <-- これを知らなくてハマった 参考
# CloudFormation template syntax.
# What goes in this property is raw CloudFormation template syntax
resources:
Resources:
NewResource:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.s3.bucketName}
functions:
importCsvData:
handler: src/functions/importCsvData/importCsvDataHandler.runner
events:
- s3:
bucket: ${self:custom.s3.bucketName}
event: s3:ObjectCreated:*
rules:
- suffix: .csv
なので、こうすればOK。
functions:
importCsvData:
handler: src/functions/importCsvData/importCsvDataHandler.runner
events:
- s3:
bucket: ${self:custom.s3.bucketName}
event: s3:ObjectCreated:*
rules:
- suffix: .csv
Serverless Plugin IfElse で、プロパティ定義自体を差し込めるようにする
・add, remove or change the values of attributes in the yml file.
・It works with both package and deploy commands.
Install
npm i serverless-plugin-ifelse --save-dev