- 投稿日:2019-03-02T23:53:47+09:00
aws-cli で S3ウェブサイトホスティングをする
前提
- S3 のwebサイトホスティングをcliでやってみようの巻。
- 今回は bucket-acl のみで bucket-policy は使ってません。
- cloudfrontは無しの構成です。
流れ
- バケット作成
- webサイトの設定をする
- コンテンツ置く
- 公開
手順
バケット作成
aws s3 mb s3://mybucketwebサイトホスティングの設定をする
以下のファイルを作成する(website.json)
website.json(例){ "IndexDocument": { "Suffix": "index.html" }, "ErrorDocument": { "Key": "error.html" } }上で作ったファイルをオプションで指定してwebサイト用の設定を投入
aws s3api put-bucket-website --bucket mybucket --website-configuration file://website.jsonコンテンツ(html とか)置く
aws s3 cp index.html s3://mybucket/index.html --acl public-read
- その他のファイルをアップロードする場合は、上のコマンドの index.html を置き換えてください。
- 注意
aws s3api put-object
を使う場合はContent-type
(text/html)やContent-Length
オプションを設定して下さい。aws s3 cp
であれば自動でいい感じに設定してくれますが、aws s3api put-object
だと設定してくれないです。
公開
aws s3api put-bucket-acl --bucket mybucket --acl public-readアクセス
http://mybucket.s3-website-us-west-2.amazonaws.com/
- バケットのURLと違うので気をつけて下さい。
- リージョンはバケットを作ったリージョンに置き換えてください
- 投稿日:2019-03-02T23:26:04+09:00
API GatewayのLambda統合で嵌った話
今まで仕事でLambdaを触っていたけど、自宅で一から作ろうとしたら嵌ったので、その時の話を書いていきます。
仕事の記憶を頼りに、次のようなLambdaハンドラと
serverless.yml
を書きました。ごく単純なPOSTメソッドで、リクエストボディからparam
という値を取り出します。handler.pydef handle_request(event, context): body = event.get('body') param = body.get('param') # 以下省略serverless.ymlfunctions: sample: handler: python:handler.handle_request events: - http: post sample早速リクエストを送ってみると、
502 Internal server error
が返ってきて、Lambdaのログには以下のエラーが出ていました。[ERROR] AttributeError: 'str' object has no attribute 'get' Traceback (most recent call last): File "/var/task/handler.py", line 22, in handle_request param = body.get('param')あれ、おかしいな。仕事の時は
body
からget
するだけでパラメータを取得できていたのだが…。で、調べてみると、Lambdaプロキシ統合が関係していることが分かりました。
今までほとんど意識していませんでしたが、これを使用するとAPI GatewayがLambdaへうまい感じにリクエストパラメータ等を渡したり、Lambdaからのレスポンスを処理したりするようです。
ただし、リクエストボディはJSON形式ではなく文字列に変換されてしまうとのこと。Lambdaプロキシ統合を使用しないとJSON形式で扱えたので、この点だけは若干不便な気がします。
serverless.ymlfunctions: sample: handler: python:handler.handle_request events: - http: path: sample method: post integration: lambdaこのように書くと、Lambda統合はされますが、Lambdaプロキシ統合は使用されません。仕事ではこうしていたから
get
だけでうまく行ったんだな。週明けに直さなきゃ!
- 投稿日:2019-03-02T22:54:32+09:00
AWS Cloud9がConnecting...の画面から抜け出せたので解決方法まとめました。(for Mac)
- 投稿日:2019-03-02T22:54:32+09:00
AWS Cloud9がConnecting...の画面から抜け出せたので解決方法まとめました。
- 投稿日:2019-03-02T20:58:37+09:00
EC2(Linux)のメモリをCloudWatchのメトリクスでモニタリングできるように設定(自分用のメモ)
自分用のメモ
概要
CloudWatchではCPUやStatusCheck(EC2が止まっているかどうか)については表示のメトリクスが用意されているが、MemoryやSwapなどはメトリクスが用意されていないため、自分でカスタマイズメトリクスを作成しなければならない。
手順
以下参照(これを上から順番に見るだけでできます)
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/mon-scripts.html設定例
crontabの設定例
*/1 * * * * ~/aws-scripts-mon/mon-put-instance-data.pl --mem-util --mem-used --mem-avail --mem-avail --memory-units=Gigabytes --swap-util --swap-used --from-cron
- 投稿日:2019-03-02T20:47:04+09:00
AWS SDK for Go S3バケット基本操作
AWS SDK for Go
S3と接続するインスタンスの生成
import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/service/s3" ) creds := credentials.NewStaticCredentials("AWS_ACCESS_KEY", "AWS_SECRET_ACCESS_KEY", "") sess, err := session.NewSession(&aws.Config{ Credentials: creds, Region: aws.String("ap-northeast-1")}, ) svc := s3.New(sess)バケットの一覧を取得:ListBuckets
result, _ := svc.ListBuckets(nil) fmt.Println("Buckets:") for _, b := range result.Buckets { fmt.Printf("* %s created on %s\n", aws.StringValue(b.Name), aws.TimeValue(b.CreationDate)) }バケットの作成:CreateBucket
bucket := "new-bucket-name" resp, err := svc.CreateBucket(&s3.CreateBucketInput{ Bucket: aws.String(bucket), }) fmt.Println(resp) // -> { Location: "http://new-bucket-name.s3.amazonaws.com/" }バケット内のオブジェクト一覧を取得:ListObjects
bucket := "bucket-name" resp, err := svc.ListObjects(&s3.ListObjectsInput{Bucket: aws.String(bucket)}) for _, item := range resp.Contents { fmt.Println("Name: ", *item.Key) fmt.Println("Last modified:", *item.LastModified) fmt.Println("Size: ", *item.Size) fmt.Println("Storage class:", *item.StorageClass) fmt.Println("") }ファイルのアップロード:Upload
import "github.com/aws/aws-sdk-go/service/s3/s3manager" bucket := "bucket-name" filename := "filename" file, err := os.Open(filename) defer file.Close() uploader := s3manager.NewUploader(sess) _, err = uploader.Upload(&s3manager.UploadInput{ Bucket: aws.String(bucket), Key: aws.String(filename), Body: file, })ファイルのダウンロード:Download
import "github.com/aws/aws-sdk-go/service/s3/s3manager" bucket := "bucket-name" file, err := os.Create("filename") defer file.Close() downloader := s3manager.NewDownloader(sess) numBytes, err := downloader.Download(file, &s3.GetObjectInput{ Bucket: aws.String(bucket), Key: aws.String(item), })バケット間でファイルをコピー:CopyObject
bucket := "bucket-containing-file" item := "file-to-copy" other := "bucket-to-file-copied" _, err = svc.CopyObject(&s3.CopyObjectInput{Bucket: aws.String(other), CopySource: aws.String(source), Key: aws.String(item)})ファイルの削除:DeleteObject
bucket := "bucket-name" obj := "file-to-delete" _, err = svc.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(bucket), Key: aws.String(obj)}) err = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{ Bucket: aws.String(bucket), Key: aws.String(obj), }) // errorが帰ってこなければ成功全てのファイルを削除:DeleteObjects
import "github.com/aws/aws-sdk-go/service/s3/s3manager" bucket := "bucket-name" iter := s3manager.NewDeleteListIterator(svc, &s3.ListObjectsInput{ Bucket: aws.String(bucket), }) if err := s3manager.NewBatchDeleteWithClient(svc).Delete(aws.BackgroundContext(), iter); err != nil { fmt.Println(err) }アーカイブされたファイルの復元:RestoreObject
obj := "file-to-restore" _, err = svc.RestoreObject(&s3.RestoreObjectInput{ Bucket: aws.String(bucket), Key: aws.String(obj), RestoreRequest: &s3.RestoreRequest{Days: aws.Int64(30)} })バケットの削除:DeleteBucket
bucket := "bucket-name" _, err = svc.DeleteBucket(&s3.DeleteBucketInput{ Bucket: aws.String(bucket), }) err = svc.WaitUntilBucketNotExists(&s3.HeadBucketInput{ Bucket: aws.String(bucket), })
- 投稿日:2019-03-02T20:08:27+09:00
モダンな技術を全く知らないSIer5年目がWebサービスを作ることになったため0から勉強する ~AWS編その2~
はじめに
現在SIer5年目でjavascript(Jqueryのみ)、PHP(フレームワーク無し)を2年ほど、C#(Windowsアプリ)3年ほどやってきました。
色々なご縁があり、個人で最近Webサービスの立ち上げをやることになったのですが何せ本当にWebサービスを立ち上げるための知識がほぼ0に等しいですただ今後のキャリアを考えた時に今のままではいけないと思いチャレンジすることにしました。
まずは最初に技術を習得しないといけないので、学ぶ&アウトプットするために毎回投稿していこうと思います。
今後身についていこうと思ってるのは下記のような技術です。
AWS
Docker
CI/CD環境の構築(何を使うかはまだ未定)
Laravel
Nuxt.js今回はAWSについて学んでいきます。
関連記事
モダンな技術を全く知らないSIer5年目がWebサービスを作ることになったため0から勉強する ~AWS編その1~
EC2を学ぶ
AWSでは一番有名?なサービスで、自分もAWSは全く勉強してなくてもこのサービス名とこれでクラウドにサーバを建てれるのは知っていました。
EC2インスタンスを起動する
起動までの流れ
大きく分けて5つの流れで構成されています。
- OS/Imageの選択
- インスタンスタイプの選択
- ストレージの選択
- セキュリティグループの作成
- SSHキーペアの設定
OS/Imageの選択
どのOSを使用するか選定する
EC2サービスの画面でインスタンス作成ボタンを押下すると
Amazonマシンイメージ(AMI)を選択する画面が出てくる
クイックスタート
AWSで用意されているデフォルトのイメージ
マイAMI
自分で作成したAMI
AWS Marketplace
他のベンダーが作成したAMI
コミュニティAMI
その他コミュニティで作成されているAMI
インスタンスタイプの設定
インスタンスタイプを一覧から選択する。今回は無料枠にしておく
ストレージの設定
ストレージの設定を行う
今回は初期値にしておくが、実際Webサービス構築時にはここら辺も考慮が必要かも。。セキュリティグループの設定
FWの設定を行える。
ソースの項目の箇所でよりアクセスできるところを限定しておかないとセキュリティ的に危ない
SSHキーペアの設定
SSHのキーペアを作成し、ローカルに保存しておく。
SSH接続に必要なため
EC2インスタンスに接続し、ミドルウェアを起動する
立ち上げたEC2にSSH接続し、ミドルウェアを起動する(Apache)
SSH接続する
ターミナルを開いてSSHのキーファイルに移動する
cd {xxx.pemファイルのパス}SSHキーのファイル権限が大きすぎるためこのままだと接続時にWarningで弾かれてしまう
そのためpermissionの設定を変えるchmod 400 xxx.pemSSHで接続する
ssh -i udemy-aws-14days.pem ec2-user@{EC2のIPv4 パブリック IP}httpdのインストールを行う
sudo yum install httpdhttpdを立ち上げる
sudo service httpd startサーバのIPアドレスでアクセスすると下記の画面が表示されるのでWebサーバが立ち上がったのが確認できる
AMIを取得する
- AMIとして取得したいインスタンスを停止させる
AMIタブを押下し、作成されたAMIを選択し作成ボタンを押下して新たにインスタンスの生成を行う
通常のインスタンスのアクセスと同様に行い、Webサーバを立ち上げるとこまで行う
EIPでIPアドレスを固定する
通常インスタンスを起動/停止するたびにIPアドレスが変わってしまう。
IPアドレスを固定にするためEIPの設定を行う
[Elastic IP]>[アクション]>[アドレスの関連付け]に移動し、関連付けるインスタンスを選択し、[関連付け]ボタンを押下する
注意点
取得したEIPはどこにも割り当てていなくても料金が発生してしまうため
取得したまま放置しないこと
- 投稿日:2019-03-02T18:18:47+09:00
CloudFormationを使ってS3とCloudFrontの構成で静的Webサイトを構築する(Origin Access Identityは使う)
はじめに
本記事では、AWS CloudFormation管理コンソールを使って、S3とCloudFrontで、静的Webサイトの環境を構築する手順を説明しています。(初心者向け)
また、CloudFrontのOriginの設定においては、OAI(Origin Access Identity)を使って、S3の静的Webサイトには、直接アクセスできない 構成としています。
直接アクセスできる構成にしたい場合は、下記の記事を参考にしてください。
CloudFormationを使ってS3とCloudFrontの構成で静的Webサイトを構築する(Origin Access Identityは使わない)本記事で掲載しているテンプレートの最新版は、下記に置いてます。
https://github.com/okubo-t/aws-cloudformation構成図
前提条件
CloudFrontに設定するSSLサーバ証明書を、リージョンがバージニア北部のAWS Certificate Manager(ACM)にインポートしていること。
ACM管理コンソールで、CloudFrontに設定する証明書の識別子をメモしておく。
構築手順
1 AWS CloudFormation管理コンソールから、スタックの作成をクリックします。
パラメータ名 用途 備考 スタックの名前 テンプレートから作成するリソース一式の名前 例 s3-cf-web-20190226 BucketName コンテンツを配置するバケット名 必須 WebsiteDomainName 静的Webサイトのドメイン名 CloudFrontのCNAME 必須 CFSSLCertificateId 前提条件でメモした証明書の識別子 必須 4 後続は、デフォルトのまま次へ次へで、作成します。
5 状況が CREATE COMPLETEになれば、S3とCloudFrontの構築が完了です。
6 管理コンソールの下部の出力から、構築したS3バケットとCloudFrontの情報を確認できます。
ここで、キーがDomainNameの値をメモしておきます。
7 確認のために、S3の管理コンソールから、パラメータに設定したバケットに、index.htmlファイルをアップロードします。
8 ブラウザから、先ほどメモしたDomainNameの値にWebアクセスして、index.htmlの内容が表示されれば、OKです。
9 最後に、適宜、WebsiteDomainNameのCNAMEレコード(Route53の場合は、ALIASレコード)として、CloudFrontのドメイン名をDNSサーバ(Route53)に登録してください。
テンプレート
s3-cloudfront-web-hosting-02.ymlAWSTemplateFormatVersion: "2010-09-09" Description: S3 and CloudFront for Static website hosting (OAI Available) Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "S3 and CloudFront Configuration" Parameters: - BucketName - WebsiteDomainName - CFSSLCertificateId ParameterLabels: BucketName: default: "BucketName" WebsiteDomainName: default: "WebsiteDomainName" CFSSLCertificateId: default: "CFSSLCertificateId" # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: BucketName: Type: String WebsiteDomainName: Type: String CFSSLCertificateId: Type: String Resources: # ------------------------------------------------------------# # S3 Bucket # ------------------------------------------------------------# # Bucket Bucket: Type: "AWS::S3::Bucket" Properties: BucketName: !Ref BucketName CloudFrontOriginAccessIdentity: Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity" Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Sub "access-identity-${Bucket}" BucketPolicy: Type: "AWS::S3::BucketPolicy" Properties: Bucket: !Ref Bucket PolicyDocument: Statement: - Action: "s3:GetObject" Effect: Allow Resource: !Sub "arn:aws:s3:::${Bucket}/*" Principal: CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId # ------------------------------------------------------------# # CloudFront # ------------------------------------------------------------# CloudFrontDistribution: Type: "AWS::CloudFront::Distribution" Properties: DistributionConfig: PriceClass: PriceClass_All Aliases: - !Ref WebsiteDomainName Origins: - DomainName: !GetAtt Bucket.DomainName Id: !Sub "S3origin-${BucketName}" S3OriginConfig: OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}" DefaultRootObject: index.html DefaultCacheBehavior: TargetOriginId: !Sub "S3origin-${BucketName}" ViewerProtocolPolicy: redirect-to-https AllowedMethods: - GET - HEAD CachedMethods: - GET - HEAD DefaultTTL: 3600 MaxTTL: 86400 MinTTL: 60 Compress: true ForwardedValues: Cookies: Forward: none QueryString: false ViewerCertificate: SslSupportMethod: sni-only MinimumProtocolVersion: TLSv1.1_2016 AcmCertificateArn: !Sub "arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${CFSSLCertificateId}" HttpVersion: http2 Enabled: true # ------------------------------------------------------------# # Output Parameters # ------------------------------------------------------------# Outputs: #BucketName BucketName: Value: !Ref Bucket #DistributionID DistributionID: Value: !Ref CloudFrontDistribution #DmainName DomainName: Value: !GetAtt CloudFrontDistribution.DomainName
- 投稿日:2019-03-02T16:54:24+09:00
AWS Configにてマネージドルールを定期評価をさせる方法
経緯
AWS ConfigにはAWSリソースが適切な設定になっているかを監視してくれる機能があります。
以下は一例です。
- S3 バケットでパブリック読み取りアクセスが許可されないことを確認します。
- AWS アカウントで AWS CloudTrail が有効になっているかどうかを確認します。
- RDS DB インスタンスの高可用性が有効になっているかどうかを確認します。
一回目の非準拠メールを見逃してしまうと
上記のルールに非準拠した状態が継続することになります。
中々恐ろしいですね。
その為、ルールを定期的に評価させてみることになりました。勘違い
AWS Configのマネージドルールについて勘違いをしまして、
ルールの詳細のトリガータイプを「設定の変更」→「定期的」にすれば
どれでも定期実行が可能だと思ってました。が、ルールをEditで開いても「定期的」がグレーアウトしていてチェックすることができないものがあります。
AWSのサポートから教えて頂いた対処
StartConfigRulesEvaluationをLambdaで実装してCloudWatch EventsからLambdaを定期実行すれば
要望満たせるとのことでした。Lambda用のRole
以下のPolicyを作成してこれから作成するLambda用のRoleへアタッチすればOKです。
JSONの場合
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "config:StartConfigRulesEvaluation", "Resource": "*" } ] }マネージドコンソールの場合
Lambda(Python3.6)
Pythonの参考ページを参照しつつ作成したコードがこちらです。
import boto3 client = boto3.client('config') def lambda_handler(event, context): response = client.start_config_rules_evaluation( ConfigRuleNames=[ 's3-bucket-public-read-prohibited', 'cloudtrail-enabled', 'rds-multi-az-support' ] )ConfigRuleNamesの箇所にマネージドルールの項目名を記載するだけです。
上で作成したRoleを選択するとLambdaの画面でConfigが連携されます。CloudWatch Events
スケジュールにて確認したい間隔を設定し、Lambdaを指定することで、連携が完了です。
確認方法
ルールの項目から評価状況を確認してください。
最後に成功した呼び込みと最後に成功した評価が指定した間隔で更新されたら成功です。落とし穴
AWS Configは仕様でルールの準拠・非準拠のステータス変更時しか通知してくれません。
つまり非準拠が継続の場合にはメール通知がありません。
サポートから上記の回答を頂いたので2019年2月末時点では代替手段もなさそなので
「運用でカバー」をしないといけない様です・・・■ 公式リンク
公式ページ
ブラックベルト
AWS Config マネージドルールのリスト■ AWS Config過去記事
AWS Configの通知内容をLambdaで成形
- 投稿日:2019-03-02T12:07:59+09:00
モダンな技術を全く知らないSIer5年目がWebサービスを作ることになったため0から勉強する ~AWS編その1~
はじめに
初のQiita投稿になります。
色々と間違ってる点や拙い文章になると思いますが生暖かく見守って、ついでにアドバイスをいただけると幸いですw現在SIer5年目でjavascript(Jqueryのみ)、PHP(フレームワーク無し)を2年ほど、C#(Windowsアプリ)3年ほどやってきました。
色々なご縁があり、個人で最近Webサービスの立ち上げをやることになったのですが何せ本当にWebサービスを立ち上げるための知識がほぼ0に等しいですただ今後のキャリアを考えた時に今のままではいけないと思いチャレンジすることにしました。
まずは最初に技術を習得しないといけないので、学ぶ&アウトプットするために毎回投稿していこうと思います。
今後身についていこうと思ってるのは下記のような技術です。
AWS
Docker
CI/CD環境の構築(何を使うかはまだ未定)
Laravel
Nuxt.js今回はAWSについて学んでいきます。
AWSアカウント作成と初期設定諸々
- AWSアカウント作成
- 作業用IAMを作る
- CloudTrailを設定
- 料金アラートを設定
1. AWSアカウント作成
下記のURLからAWSのアカウントを作成する
手順通り作成していく
AWS公式サイト作成後にIAMユーザで請求確認できるように設定する
[UserName]>[マイアカウント]この次の手順で作成するIAMユーザでも請求書を確認できるようにする
2. IAMユーザの作成
AWSでアカウントを作成した直後では、rootユーザのみで全権限を持っており
アカウントの設定方法、サポートプラン変更時はこのrootユーザでなければできない
それ以外はIAMユーザに権限を与えてそちらで作業させるのがAWSのベストプラクティスらしいそれぞれのアカウントの特徴は下記の通り
rootユーザ
- 全サービスを扱える特権ユーザ
- アカウント設定変更/サポートプラン変更はこのユーザのみでしか行えない
- セキュリティ的に通常の作業はこのユーザの仕様は非推奨
IAMユーザ
- 許可されたAWSサービスしか行えない
- 利用者ごとに払い出し、通常の作業はこちらで行い
IAMユーザの作成をしていく
(1) AWSサービスの検索ボックスで「IAM」と検索し、IAMユーザの画面に移動する
(2) [ユーザ]タブ>[ユーザーを追加]ボタン
(3) 任意のユーザ名を入力
(4) アクセス種類[AWSマネジメントコンソールへアクセス]にチェック
(5) パスワード設定
※他の作業者のために作成してるならパスワードリセットのチェックボックスをONにするアクセスポリシーの設定
今回は一旦全てのサービスを利用できるようにするためAWSで定義されている下記のポリシーで設定する
IAMユーザの作成をしたら、ログイン用の独自のURLが発行され
作成したIAMユーザはそのURLからログインすることになる3. CloudTrailの設定
CloudTrailはAWSに関する操作ログを自動取得するサービス
デフォルトでは90日まで残すようになっているが、個人ならいいが実際に稼働するサービスを構築する場合にはもっと長い期間を置いておく必要がある移動したら次にログを永続的に残すために[証跡の作成]ボタンを押下する
今回はこんな感じの設定にしました。ちなみにスクショにはないけどS3のバケットを作成し、S3バケット名も定義しました
4. 料金アラートの設定
AWSの料金が指定の値を超えたら通知するように設定する
「請求アラートの設定」
[マイアカウント]>[Billing の設定]
請求アラートを受け通るのチェックボックスをONにする[CloudWatch機能を利用する]
CloudWatchサービスの画面に移動する
[請求]メニュー>[アラームの作成]ボタン(1)アラームする金額設定
(2)通知先のメールアドレスの設定補足:AWS料金の調べ方
最近Qiitaで良さげなの見つけたんでこれ貼っときます
ざっくりAWS
- 投稿日:2019-03-02T11:57:16+09:00
リージョン丸ごと綺麗にしてみた
環境作りの練習に使っていた環境を削除するので、その様子をメモ書きします。
やったこと
- CloudFormationのスタックを削除する準備
- CloudFormationのスタックを削除する
- 残ったものを削除する
1. CloudFormationのスタックを削除する準備
- ECRを削除します。
あらかじめ消さない場合、以下のようにCloudFormationのスタック削除が失敗します。in registry with id '******' cannot be deleted because it still contains images (Service: AmazonECR; Status Code: 400; Error Code: RepositoryNotEmptyException; Request ID: cbe340fe-39e6-11e9-90f7-******)
API GatewayのVPC Linkを削除します。
VPC Linkが参照しているリソースは削除できないためです。RDSインスタンスを削除します。
2. CloudFormationのスタックを削除する
普通にCloudFormationのコンソール画面からスタックを削除します。
3. 残ったものを削除する
- 投稿日:2019-03-02T09:22:07+09:00
Amazon EFSに数百GB以上のデータを置く場合の注意点
複数EC2インスタンスからアクセス可能なキャッシュとしてEFSを利用し、数百GB以上のデータを定常的に運用している。その中でいくつかハマりポイントが見つかったので書いておく。
EFSとは
えごったーでは、ツイッターAPIのデータのキャッシュとしてEFSを利用している。EFSは簡単に言うと、複数EC2インスタンスに同時にマウントできるEBS。その他の特徴は下記の通り。
- 複数EC2インスタンスに同時にマウントできる共有ストレージサービス
- 内部的にはNFS
- フルマネージド、複数AZへの冗長化あり
- アクセス速度は、ローカルストレージ(=EBS)よりは遅い、(使い方によるが)S3よりは早い
- EBS > EFS > S3
- 料金は、EBSよりも高い、S3よりはもちろん安い
- EFS > EBS > S3
- 保存するデータ量に応じてサイズが自動で増減する
この中でもサイズが自動で増減する点が他にはない特徴かつ非常に役に立つ点。自動で増加するサービスは多いが、減少するサービスは比較的珍しい。柔軟性が高い分、EBSよりも料金が高くなっている。
ただし、良い点だけでなく注意が必要な点もいくつかある。それを順に書いていく。
注意点
この規模のデータになると通常のファイルシステム(ext4など)でもそれなりに遅くなります。EFSだけが遅いというわけではない点にご注意ください。データが大きくなるとファイルの一覧表示(ls)に数十分以上の時間がかかるようになる
今回、EFSには183GBのデータを保存している。
$ df -h ファイルシス サイズ 使用 残り 使用% マウント位置 ap-northeast-1b.fs-00000000.efs.ap-northeast-1.amazonaws.com:/ 8.0E 183G 8.0E 1% /efsすると、ファイルの一覧表示ができなくなってしまう。
$ ls /efs/cache # 結果が返ってこないディレクトリ構造は一例として下記の通り。1, 2, 3, 4がファイルで中身はJSONが入っている。ファイル数は250万ほどある。
$ tree /efs/cache ├── 032 │ └── 320 │ └── 1 ├── 033 │ └── 330 │ └── 2 ├── 034 │ └── 340 │ └── 3 ├── 035 │ └── 350 │ └── 4もちろん、ディレクトリのサイズ表示(du -sh)もできない。
$du -sh /efs/cache # 結果が返ってこないファイルの一覧表示ができないと困るのは有効期限切れのキャッシュを削除するとき
似たような例として、Redis等のキャッシュシステムではキーの一覧表示は非常に負荷がかかる処理であり、本番環境では実行しないように注意する必要があることはよく知られている。
それと同様に、lsをしなければよいと思うかもしれないが、それではうまくいかないケースがある。
Redisは有効期限切れのキャッシュの削除を自動で行う仕組みが用意されているが、ファイルシステムベースのキャッシュは有効期限切れのキャッシュの削除を次回アクセス時に行う実装になっていることがある。
つまり、ある時点で作ったキャッシュは削除されずにずっと残り続ける。それではデータが増えすぎてしまうため、定期的に削除する必要があるが、ファイルシステムベースのキャッシュは有効期限切れキャッシュの探索にlsと類似する仕組みを用いることがあり、このケースに当たると利用しないキャッシュがいつまでも削除されないことになってしまう。
一例として、Rails(ActiveSupport)のFileStoreは上記のような実装になっており、EFSをキャッシュ保存先として利用していると、データが大きくなるにつれ下記のコードで有効期限切れのキャッシュ削除をすることができなくなってしまう。
ActiveSupport::Cache::FileStore.new(dir).cleanup # 一覧表示ができないためそもそも始まりすらしないこの問題に対して、えごったーでは、定期的にキャッシュを保存するディレクトリを変更することで対処している。
# 1月のキャッシュ。2月になったらrmで削除可能 /efs/cache/201901 # 2月のキャッシュ /efs/cache/201902ただし、EFSに数百GBのデータを保存すると、ファイルの削除にも非常に時間がかかるため、rmの実行にも注意が必要となる。
データが大きくなるとファイルの削除(rm)にも数十分以上の時間がかかるようになる
183GBのデータをrmで単純に削除すると、約3時間かかることになる。この点も注意してEFSを運用する必要がある。
$ time rm -rf /efs/cache real 183m54.272s user 0m9.202s sys 1m33.282s注意点
この規模のデータをrmで削除すると、通常のファイルシステム(ext4など)でもそれなりに遅くなります。EFSだけが遅いというわけではない点にご注意ください。参考リンク
- 投稿日:2019-03-02T09:22:07+09:00
Amazon EFSで数百GB以上のデータを運用する場合のハマりポイント
複数EC2インスタンスからアクセス可能なキャッシュとしてEFSを利用し、数百GB以上のデータを定常的に運用している。その中でいくつかハマりポイントが見つかったので書いておく。
EFSとは
えごったーでは、ツイッターAPIのデータのキャッシュとしてEFSを利用している。EFSは簡単に言うと、複数EC2インスタンスに同時にマウントできるEBS。その他の特徴は下記の通り。
- 複数EC2インスタンスに同時にマウントできる共有ストレージサービス
- 内部的にはNFS
- フルマネージド、複数AZへの冗長化あり
- アクセス速度は、ローカルストレージ(=EBS)よりは遅い、(使い方によるが)S3よりは早い
- EBS > EFS > S3
- 料金は、EBSよりも高い、S3よりはもちろん安い
- EFS > EBS > S3
- 保存するデータ量に応じてサイズが自動で増減する
この中でも サイズが自動で増減する点が他にはない特徴かつ非常に役に立つ点 。自動で増加するサービスは多いが、減少するサービスは比較的珍しい。柔軟性が高い分、EBSよりも料金が高くなっている。
ただし、良い点だけでなく注意が必要な点もいくつかある。それを順に書いていく。
注意点
この規模のデータになると通常のファイルシステム(ext4など)でもそれなりに遅くなります。EFSだけが遅いというわけではない点にご注意ください。データが大きくなるとファイルの一覧表示(ls)に数十分以上の時間がかかるようになる
今回、EFSには183GBのデータを保存している。
$ df -h ファイルシス サイズ 使用 残り 使用% マウント位置 ap-northeast-1b.fs-00000000.efs.ap-northeast-1.amazonaws.com:/ 8.0E 183G 8.0E 1% /efsすると、ファイルの一覧表示ができなくなってしまう。
$ ls /efs/cache # 結果が返ってこないディレクトリ構造は一例として下記の通り。1, 2, 3, 4がファイルで中身はJSONが入っている。ファイル数は250万ほどある。
$ tree /efs/cache ├── 032 │ └── 320 │ └── 1 ├── 033 │ └── 330 │ └── 2 ├── 034 │ └── 340 │ └── 3 ├── 035 │ └── 350 │ └── 4もちろん、ディレクトリのサイズ表示(du -sh)もできない。
$du -sh /efs/cache # 結果が返ってこないファイルの一覧表示ができないと困るのは有効期限切れのキャッシュを削除するとき
似たような例として、Redis等のキャッシュシステムではキーの一覧表示は非常に負荷がかかる処理であり、本番環境では実行しないように注意する必要があることはよく知られている。
それと同様に、lsをしなければよいと思うかもしれないが、それではうまくいかないケースがある。
Redisは有効期限切れのキャッシュの削除を自動で行う仕組みが用意されているが、 ファイルシステムベースのキャッシュは有効期限切れのキャッシュの削除を次回アクセス時に行う実装になっていることがある 。
つまり、ある時点で作ったキャッシュは削除されずにずっと残り続ける。それではデータが増えすぎてしまうため、定期的に削除する必要があるが、ファイルシステムベースのキャッシュは有効期限切れキャッシュの探索にlsと類似する仕組みを用いることがあり、このケースに当たると利用しないキャッシュがいつまでも削除されないことになってしまう。
一例として、Rails(ActiveSupport)のFileStoreは上記のような実装になっており、EFSをキャッシュ保存先として利用していると、データが大きくなるにつれ下記のコードで有効期限切れのキャッシュ削除をすることができなくなってしまう。
ActiveSupport::Cache::FileStore.new(dir).cleanup # 一覧表示ができないためそもそも始まりすらしないこの問題に対して、えごったーでは、定期的にキャッシュを保存するディレクトリを変更することで対処している。
# 1月のキャッシュ。2月になったらrmで削除可能 /efs/cache/201901 # 2月のキャッシュ /efs/cache/201902ただし、EFSに数百GBのデータを保存すると、ファイルの削除にも非常に時間がかかるため、rmの実行にも注意が必要となる。
データが大きくなるとファイルの削除(rm)にも数十分以上の時間がかかるようになる
183GBのデータをrmで単純に削除すると、約3時間かかることになる。この点も注意してEFSを運用する必要がある。
$ time rm -rf /efs/cache real 183m54.272s user 0m9.202s sys 1m33.282s注意点
この規模のデータをrmで削除すると、通常のファイルシステム(ext4など)でもそれなりに遅くなります。EFSだけが遅いというわけではない点にご注意ください。参考リンク