【AWS】CLIからバージョニングされたS3バケットを削除する方法
S3バケットは空にしてからでないと削除できません。それはバージョニングされたバケットでも同様です。
問題は、バージョニングされたオブジェクトを削除しても、実は消えていないことです。DeleteMarkerが追加されるだけで、オブジェクトが完全に消えるわけではありません。完全に削除するためには、DeleteMarkerとバージョンも削除する必要があります。
マネージメントコンソールであれば削除するのは難しくありません。しかし、AWS CLIから少々面倒なので手順をまとめました。
[1] オブジェクトの削除
[1-1] バケットとファイルの作成
試しにバージョニングされたバケットを作成し、test.txtをアップロードします。
$ aws s3 ls s3://{バケット名} 2021-04-17 13:19:12 4 test.txt
バージョンを確認します。
$ aws s3api list-object-versions --bucket {バケット名} { "Versions": [ { "LastModified": "2021-04-17T13:19:12.000Z", "VersionId": "Goie_timWFWXJTwHv9swGAyjbIrDI6cQ", "ETag": "\"033bd94b1168d7e4f0d644c3c95e35bf\"", "StorageClass": "STANDARD", "Key": "test.txt", "Owner": { "DisplayName": "{所有者の表示名}", "ID": "{所有者のID}" }, "IsLatest": true, "Size": 4 } ] }
[1-2] オブジェクトの削除
aws s3 rm
でオブジェクトを削除します。
$ aws s3 rm s3://{バケット名}/ --recursive delete: s3://{バケット名}/test.txt
aws s3 ls
で確認すると削除されたように見えます。
$ aws s3 ls s3://{バケット名}
しかし、バケットを削除しようとするとエラーが発生します。
$ aws s3 rb s3://{バケット名} --force remove_bucket failed: s3://{バケット名} An error occurred (BucketNotEmpty) when calling the DeleteBucket operation: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
[1-3] バージョンの確認
バージョンを確認すると、完全には削除されていないことが分かります。DeleteMarkersが追加されています。
$ aws s3api list-object-versions --bucket {バケット名} { "DeleteMarkers": [ { "Owner": { "DisplayName": "{所有者の表示名}", "ID": "{所有者のID}" }, "IsLatest": true, "VersionId": "mfYZIOM57MlB_8BkvG7YcNuKWJz0geHS", "Key": "test.txt", "LastModified": "2021-04-17T13:35:10.000Z" } ], "Versions": [ { "LastModified": "2021-04-17T13:19:12.000Z", "VersionId": "Goie_timWFWXJTwHv9swGAyjbIrDI6cQ", "ETag": "\"033bd94b1168d7e4f0d644c3c95e35bf\"", "StorageClass": "STANDARD", "Key": "test.txt", "Owner": { "DisplayName": "{所有者の表示名}", "ID": "{所有者のID}" }, "IsLatest": false, "Size": 4 } ] }
"DeleteMarkers", "Versions"を削除する必要があります。
[2] 完全な削除
以下のシェルスクリプトを作成・実行して削除します。コマンドは下記記事を参考にさせていただきました。
バージョニングが有効なS3バケットをAWS CLIで空にする手順(オブジェクト1000個以下) - のぴぴのメモ
#!/bin/bash BUCKET_NAME={バケット名} # バケット内の全オブジェクト削除 aws s3 rm s3://${BUCKET_NAME}/ --recursive # DeleteMarkersの削除 aws s3api list-object-versions --bucket ${BUCKET_NAME} \ | jq -r -c '.["DeleteMarkers"][] | [.Key,.VersionId]' \ | while read line do key=`echo $line | jq -r .[0]` versionid=`echo $line | jq -r .[1]` aws s3api delete-object --bucket ${BUCKET_NAME} \ --key ${key} --version-id ${versionid} done # Versionsの削除 aws s3api list-object-versions --bucket ${BUCKET_NAME} \ | jq -r -c '.["Versions"][] | [.Key,.VersionId]' \ | while read line do key=`echo $line | jq -r .[0]` versionid=`echo $line | jq -r .[1]` aws s3api delete-object --bucket ${BUCKET_NAME} \ --key ${key} --version-id ${versionid} done
実行すると、以下のような結果が返ってきます。
$ sh delete_s3_object.sh { "VersionId": "mfYZIOM57MlB_8BkvG7YcNuKWJz0geHS", "DeleteMarker": true } { "VersionId": "Goie_timWFWXJTwHv9swGAyjbIrDI6cQ" }
バージョンを再度確認すると、今度は削除されています。
$ aws s3api list-object-versions --bucket {バケット名}
バケット削除を実行すると今度は削除に成功します。
$ aws s3 rb s3://{バケット名} --force remove_bucket: {バケット名}
出典
【AWS】t4gでnumpy, pandas, matplotlibをインストールする方法
AWSのArmベースインスタンスのt4g.micro、2021年3月31日まで無料トライアル中です。無料で使えるやったと思っていたら思わぬところで引っかかりました。
- [0] 前提条件
- [1] numpyのインストールに失敗する
- [2] numpyのインストールに時間がかかる
- [3] pandasがout of memoryでインストールできない
- [4] matplotlibのインストールに失敗する
- 終わりに
[0] 前提条件
OSはAmazon Linux2、python3とpip3はインストール済みです。
$ python3 -V Python 3.7.9 $ pip3 -V pip 9.0.3 from /usr/lib/python3.7/site-packages (python 3.7)
[1] numpyのインストールに失敗する
$ pip3 install numpy --user ... ModuleNotFoundError: No module named 'Cython' ...
Cythonが無いと怒られます。対処法はcythonのインストールです。
$ pip3 install cython --user
[2] numpyのインストールに時間がかかる
理由は、ソースをビルドしているからです。
$ pip3 install numpy --user Collecting numpy Using cached https://files.pythonhosted.org/packages/51/60/3f0fe5b7675a461d96b9d6729beecd3532565743278a9c3fe6dd09697fa7/numpy-1.19.5.zip Installing collected packages: numpy Running setup.py install for numpy ... done Successfully installed numpy-1.19.5
また、t4g.microのスペックはvCPU=2、メモリ=1(GiB)と決して高くないため、より時間がかかります。
[3] pandasがout of memoryでインストールできない
pandasもnumpyと同様に、ソースをビルドしてインストールしようとします。
pip3 install pandas --user Collecting pandas Downloading https://files.pythonhosted.org/packages/11/1c/b0bc154996617eae877ff267fcf84e55e6c6808dbade0da206f0419dd483/pandas-1.2.1.tar.gz (5.5MB) 100% |████████████████████████████████| 5.5MB 273kB/s ... Running setup.py install for pandas ... error ... as: out of memory allocating 4064 bytes after a total of 163106816 bytes ...
t4g.microのメモリ=1(GiB)では、pandasをソースからビルドするのには足らなかったようです。
有効な対応策が見つからなかったため、t4g.smallでビルドしました。
[4] matplotlibのインストールに失敗する
matplotlibに必要な「kiwisolver」のインストールに失敗します。
$ pip3 install matplotlib --user Collecting matplotlib Downloading https://files.pythonhosted.org/packages/7b/b3/7c48f648bf83f39d4385e0169d1b68218b838e185047f7f613b1cfc57947/matplotlib-3.3.3.tar.gz (37.9MB) 100% |████████████████████████████████| 37.9MB 39kB/s Collecting cycler>=0.10 (from matplotlib) Downloading https://files.pythonhosted.org/packages/f7/d2/e07d3ebb2bd7af696440ce7e754c59dd546ffe1bbe732c8ab68b9c834e61/cycler-0.10.0-py2.py3-none-any.whl Collecting kiwisolver>=1.0.1 (from matplotlib) Downloading https://files.pythonhosted.org/packages/90/55/399ab9f2e171047d28933ae4b686d9382d17e6c09a01bead4a6f6b5038f4/kiwisolver-1.3.1.tar.gz (53kB) 100% |████████████████████████████████| 61kB 13.0MB/s ... ValueError: bad marshal data (unknown type code) During handling of the above exception, another exception occurred: ... Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-7wgyhm4y/kiwisolver/
対応策は、pipを最新にすることです。
$ sudo pip3 install -U pip WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead. Collecting pip Downloading https://files.pythonhosted.org/packages/de/47/58b9f3e6f611dfd17fb8bd9ed3e6f93b7ee662fb85bdfee3565e8979ddf7/pip-21.0-py3-none-any.whl (1.5MB) 100% |████████████████████████████████| 1.5MB 916kB/s Installing collected packages: pip Found existing installation: pip 9.0.3 Uninstalling pip-9.0.3: Successfully uninstalled pip-9.0.3 Successfully installed pip-21.0 $ source ~/.bash_profile
終わりに
最終的には無事解決できましたが、意外なところでハマってしまいました。
【AWS】ArmベースのEC2インスタンスt4gとt3のスペック・料金比較
昨年9月からArmベースのt4gインスタンスが利用可能になりました。同じく汎用インスタンスであるt3インスタンスはIntelベースです。
昨年はAppleのM1チップや、WindowsのCPU自社設計など大きな話題がありましたが、いずれもArmベースです。Armはすでにスマホ用CPUでシェアを握っていますが、PC向けでも勢力を拡大しつつありますね。
料金とスペック
Intelベースのt3, AMDベースのt3a, Armベースのt4の性能面を比較していきます。
お試しでよく使われる「micro」で比較します。価格は東京リージョンのものです。
インスタンスサイズ | 価格(東京リージョン) | vCPU | メモリ (GiB) | ベースラインパフォーマンス/vCPU | CPU クレジット取得/時間 | ネットワークバースト幅 (Gbps) | EBS バースト幅 (Mbps) |
---|---|---|---|---|---|---|---|
t4g.micro | 0.0108USD/時間 | 2 | 1 | 10% | 12 | 最大 5 | 最大 2,085 |
t3.micro | 0.0136USD/時間 | 2 | 1 | 10% | 12 | 最大 5 | 最大 2,085 |
t3a.micro | 0.0122USD/時間 | 2 | 1 | 10% | 12 | 最大 5 | 最大 2,085 |
スペックは同じで価格が異なります。t4gとt3を比較すると、t4gの方が20%安いです。
micro以外も確認しましたが、microと同様にスペックは同じで20%安いという結果でした。micro以外の性能を見たい方は公式を参照してください。
Amazon EC2 T4g Instances - Amazon Web Services
t4g以外のArmベースインスタンス
t4g以外にもArmベースのインスタンスは存在します。a1, m6g, c6g, r6gです。
a1はArmベースのインスタンスの中では古い部類だったと記憶しています。なので、t4g, m6g, c6g, r6gで使い分けるのがよいと思います。
- お試し用途、バーストが必要ならt4g
- 汎用でバースト不要ならm6g
- 高性能CPUが必要ならc6g
- メモリ容量が必要ならr6g
t4.microは2021年末まで無料トライアル中
t4.microは750時間/月まで無料で利用できます。t3からの移行を試すには非常にありがたいです。
Amazon EC2 T4g Instances - Amazon Web Services
注意点
t3を始めとしたIntel系(x86)のインスタンスと、ライブラリのインストール方法が異なる場合があります。ソースからビルドしてインストールが必要な場合もあります。
numpy, pandas, matplotlibのインストール手順は下記記事にまとめています。
終わりに
Armは昨年、ソフトバンクからNVIDIAに売却されることで話題になりましたね。Armは業績が伸び悩んでいると言われていますが、今後どうなっていくのか楽しみです。
参考資料
下記記事はOpenSSLで実際の性能を測定しており面白い記事でした。
ARMベースのCPUをバースト利用できる「T4g」インスタンスの性能をOpenSSLで測定してみた | DevelopersIO
【AWS】Route53でドメインを登録してHTTPS対応のnginxサーバーをCloudFrontとCloudFormationで構築するまで
AWSでHTTPS対応のnginxサーバーを構築するまでの手順をまとめました。
HTTPS対応のサーバーを構築する手順は多々あります。今回は「CertificateManager」でサーバ証明書を発行し「CloudFront」でHTTPS対応する構成にしました。
Route53で独自ドメインの登録を行いましたが、別のプロバイダで登録することも可能です。
- [0] 前提条件
- [1] Route53でドメイン登録
- [2] Certificate Managerで証明書発行
- [3] EC2インスタンス構築
- [4] nginxインストール
- [5] セキュリティグループの設定
- [6] CloudFrontの設定
- [7] 動作確認
- [8] スタックの削除
- 終わりに
- 参考資料
[0] 前提条件
nginxインストールなどの操作を行うために、EC2へSSHでログインします。SSHでログインするためのキーペアは作成済みとします。
[前提条件]
- キーペアは作成済み
[1] Route53でドメイン登録
マネージメントコンソールから、Route53で独自ドメインを登録します。
[1-1] ドメインの選択
ドメインの選択欄にドメイン名を入力し[チェック]を押すと、利用可能か否かと料金が表示されます。
[カートに入れる]を押すとカートに料金と登録期間が表示されます。登録期間を過ぎても自動延長できます。長期間使用する予定がなければ1年のままで問題ありません。画面下部の[続行]を押して次に進みます。
[1-2] 連絡先の入力と購入
登録者の連絡先を登録します。電話番号以外は日本語表記で問題ありません。
プライバシーの保護は、個人の場合は[有効化]にしておきます。
[続行]を押すと、次のように表示されますが問題ありませんでした。心配な場合は連絡先に登録したEメールを確認しましょう。AWSからメールが来ているはずです。
ドメインを登録すると自動的にホストゾーンが作成されます。ホストゾーンは12時間以上使用すると、0.5$/月かかります。しばらく使用しない場合は後で手動で削除しましょう。
ドメインの自動更新は個々の目的に応じて選びます。
連絡先に登録したEメールを確認し、メール内のリンクをクリックします。
リンクをクリックした後、[ステータスの更新]を押します。すると、ステータスが検証済みになります。
以上でドメインの登録は完了です。ドメイン登録が完了して使えるようになるまでには多少時間がかかります。私の場合は30分かかりました。
ホストゾーンが自動作成されたかを確認すると、作成されていました。
[2] Certificate Managerで証明書発行
続いて、Certificate Managerでサーバ証明書を発行します。証明書の発行は無料です。
なお、リージョンは「米国東部(バージニア北部)」にします。別のリージョンで発行するとCloudFrontと証明書の関連付けが出来ません。
CloudFront で SSL/TLS 証明書を使用するための要件 - Amazon CloudFront
CloudFront ディストリビューションで使用するために SSL 証明書を米国東部 (バージニア北部) リージョンに移行する
[2-1] 証明書発行
[パブリック証明書のリクエスト]を選択します。
ドメイン名を入力します。登録したドメインがexample.com
であれば、example.com
と*.example.com
を登録します。ワイルドカードを使用してサブドメインも登録します。
検証方法は[DNSの検証]にします。
証明書を多数発行するなど特別な理由がなければ、タグは空欄のままで問題ありません。
内容を確認し[確認とリクエスト]を押します。
[2-2] 検証
「検証保留中」になっています。これを「発行済み」にしていきます。ドメイン名横のドロップダウンを選択します。
[Route53でのレコードの作成]を押下します。
[作成]を押します。
もう一方も同様にDNSレコードを作成します。
以上で証明書発行の手続きは完了です。「検証保留中」になっていますが、数分経つと「発行済み」に変わります。
[3] EC2インスタンス構築
EC2インスタンス構築に当たり、次のものを作成する必要があります。
これらを毎回手動で構築するのは面倒です。なので、CloudFormationで自動化します。
[3-1] CloudFormationのテンプレート
[3-1-1] テンプレートバージョン
テンプレートバージョンは固定です。Description
は任意です。
AWSTemplateFormatVersion: 2010-09-09 Description: >- AWS CloudFormation template for the nginx server.
[3-1-2] パラメータ
次の内容はユーザーが決められるようにします。Default
を指定しておけば毎回入力する必要がなくて楽です。
Parameters: # SSHのキー名称 KeyName: Description: Name of an existing EC2 KeyPair to enable SSH access to the instance Type: 'AWS::EC2::KeyPair::KeyName' ConstraintDescription: Can contain only ASCII characters. # インスタンスタイプをリストから選択 InstanceType: Description: EC2 instance type Type: String Default: t3.micro AllowedValues: - t3.nano - t3.micro - t3.small - t3.medium ConstraintDescription: must be a valid EC2 instance type. # EBSの容量 VolumeSize: Description: 'Volume size of EBS' Type: Number Default: 8 ConstraintDescription: expect size >= 8GB. # CIDR VpcCidr: Description: 'VPC CIDR' Type: String Default: 10.0.0.0/16 SubnetCidr: Description: 'Subnet CIDR' Type: String Default: 10.0.0.0/24 # プライベートIPアドレス PrivateIpAddress: Description: 'Private IP Address.' Type: String Default: '10.0.0.10'
[3-1-3] ネットワーク周り
VPC, サブネット, ルートテーブル, インターネットゲートウェイの設定です。特殊なのはVPCのEnableDnsHostnames
とEnableDnsSupport
をtrueにすることです。デフォルトはfalseですが、CloudFrontの設定時に必要なためtrueにします。
Resources: # VPC Vpc: Type: 'AWS::EC2::VPC' Properties: CidrBlock: !Ref VpcCidr # DNSはCloudFrontのOrigin Domain Name指定のために必要 EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: !Sub '${AWS::StackName}-vpc' # サブネット Subnet: Type: 'AWS::EC2::Subnet' Properties: VpcId: !Ref Vpc CidrBlock: !Ref SubnetCidr Tags: - Key: Name Value: !Sub '${AWS::StackName}-ec2-sb' # インターネットゲートウェイ IGW: Type: 'AWS::EC2::InternetGateway' Properties: Tags: - Key: Name Value: !Sub '${AWS::StackName}-igw' IGW2Vpc: Type: 'AWS::EC2::VPCGatewayAttachment' Properties: InternetGatewayId: !Ref IGW VpcId: !Ref Vpc # ルートテーブル RouteTable: Type: 'AWS::EC2::RouteTable' Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${AWS::StackName}-ec2-rt' RT2Subnet: Type: 'AWS::EC2::SubnetRouteTableAssociation' Properties: SubnetId: !Ref Subnet RouteTableId: !Ref RouteTable Route: Type: 'AWS::EC2::Route' Properties: GatewayId: !Ref IGW RouteTableId: !Ref RouteTable DestinationCidrBlock: 0.0.0.0/0
[3-1-4] セキュリティグループ
セキュリティグループは少々複雑です。4つセキュリティグループを作成するのには理由があります。
セキュリティグループ1は、EC2へのSSHアクセスと、EC2からnginxインストール等の作業を可能にするための設定です。これは普通の設定です。
セキュリティグループ2, 3, 4は、CloudFrontからのHTTPアクセスのみ受け付けるためです。CloudFrontは世界中にエッジサーバを持っており、どのサーバからアクセスされるか分かりません。そのため、全エッジサーバのIPアドレスをセキュリティグループに登録しておく必要があります。
セキュリティグループへの登録は、EC2構築後にシェルスクリプトで行います。CloudFormationでは中身が空のセキュリティグループを準備しておきます。3つ用意するのは、セキュリティグループのルール数に制限があるからです。デフォルトでは60ルールまでですが、CloudFrontのIPアドレスは100以上有るため、複数用意しておきます。
# セキュリティグループ1 SecurityGroup1: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: !Sub '${AWS::StackName}-ec2-sg1' GroupDescription: !Sub '${AWS::StackName}-ec2-sg1' VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${AWS::StackName}-ec2-sg1' # インバウンド: SSH許可 SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 # アウトバウンド: HTTPとHTTPS許可 SecurityGroupEgress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 # セキュリティグループ2 (for CloudFront) SecurityGroup2: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: !Sub '${AWS::StackName}-ec2-sg2' GroupDescription: !Sub '${AWS::StackName}-ec2-sg2' VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${AWS::StackName}-ec2-sg2' # セキュリティグループ3 (for CloudFront) SecurityGroup3: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: !Sub '${AWS::StackName}-ec2-sg3' GroupDescription: !Sub '${AWS::StackName}-ec2-sg3' VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${AWS::StackName}-ec2-sg3' # セキュリティグループ4 (for CloudFront) SecurityGroup4: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: !Sub '${AWS::StackName}-ec2-sg4' GroupDescription: !Sub '${AWS::StackName}-ec2-sg4' VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${AWS::StackName}-ec2-sg4'
Amazon VPC でセキュリティグループルールの制限を増やす
[3-1-5] IAMロール
IAMロールは通常不要です。わざわざ設定するのは、EC2からCloudFrontのIPアドレスをセキュリティグループに登録するためです。セキュリティグループの変更は、EC2構築後にEC2から行います。EC2にセキュリティグループ操作の権限を付与します。
# IAMロール SecurityGroupAccessRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "ec2.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" # IAMポリシー SecurityGroupAccessPolicies: Type: AWS::IAM::Policy Properties: PolicyName: SecurityGroupAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow # 自リージョン・自アカウントのセキュリティグループの変更を許可 Action: - 'ec2:AuthorizeSecurityGroupEgress' - 'ec2:AuthorizeSecurityGroupIngress' - 'ec2:DeleteSecurityGroup' - 'ec2:RevokeSecurityGroupEgress' - 'ec2:RevokeSecurityGroupIngress' Resource: !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:security-group/*' # 作成するVPCのセキュリティグループのみに限定する Condition: ArnEquals: 'ec2:Vpc': !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${Vpc}' - Effect: Allow Action: - 'ec2:DescribeSecurityGroups' - 'ec2:DescribeSecurityGroupReferences' - 'ec2:DescribeStaleSecurityGroups' - 'ec2:DescribeVpcs' Resource: '*' Roles: - !Ref SecurityGroupAccessRole # IAMインスタンスプロファイル SecurityGroupAccessInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - !Ref SecurityGroupAccessRole
[3-1-6] EC2 (前半)
ここでは、リージョンは東京(ap-northeast-1)、OSはAmazon Linux2で構築する設定にしています。UserData
はEC2構築後にjq, curlを自動インストールし、シェルスクリプトを自動生成するための設定です。
# インスタンス Instance: Type: 'AWS::EC2::Instance' # プロパティ Properties: # ap-northeast-1のAmazon Linux2 ImageId: 'ami-01748a72bed07727c' InstanceType: !Ref InstanceType # ネットワーク周りの設定 NetworkInterfaces: - AssociatePublicIpAddress: true DeviceIndex: 0 GroupSet: - Ref: SecurityGroup1 - Ref: SecurityGroup2 - Ref: SecurityGroup3 - Ref: SecurityGroup4 SubnetId: Ref: Subnet PrivateIpAddress: !Ref PrivateIpAddress # SSHのキー名称 KeyName: !Ref KeyName # EBSの設定 BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref VolumeSize VolumeType: gp2 DeleteOnTermination: true # IAM IamInstanceProfile: !Ref SecurityGroupAccessInstanceProfile # タグ Tags: - Key: Application Value: String - Key: Name Value: !Sub '${AWS::StackName}-inst' # ユーザーデータ UserData: !Base64 Fn::Sub: | #!/bin/bash yum -y update yum -y install aws-cfn-bootstrap /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource Instance --region ${AWS::Region} /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource Instance --region ${AWS::Region}
[3-1-7] EC2 (後半)
EC2構築後にjq, curlを自動インストールし、シェルスクリプトを自動生成するための設定です。スクリプトの詳細は後述しますが下記記事を参考にさせていただいています。
EC2のセキュリティグループにCloudFrontからしかアクセスを許可しない設定を追加する(改良版) | 綺麗に死ぬITエンジニア
# メタデータ Metadata: AWS::CloudFormation::Init: config: # jq, curlをインストール packages: yum: jq: [] curl: [] files: # セキュリティグループ変更用のスクリプト '/home/ec2-user/set_security_group.sh': content: Fn::Sub: | #!/bin/bash IP_RANGE=`curl -s https://ip-ranges.amazonaws.com/ip-ranges.json` IP_LIST=`echo $IP_RANGE | jq -r '.prefixes[] | if .service == "CLOUDFRONT" then .ip_prefix else empty end'` count=0 for IP in $IP_LIST; do if [ $count -lt 60 ]; then SG=${SecurityGroup2} elif [ $count -lt 120 ]; then SG=${SecurityGroup3} else SG=${SecurityGroup4} fi aws ec2 authorize-security-group-ingress --region ${AWS::Region} --group-id $SG --protocol tcp --port 80 --cidr $IP echo $IP (( count++ )) done mode: '000644' owner: 'ec2-user' group: 'ec2-user' # nginxインストール用のスクリプト '/home/ec2-user/install_nginx.sh': content: Fn::Sub: | #!/bin/bash sudo amazon-linux-extras install nginx1 -y sudo systemctl start nginx sudo systemctl enable nginx mode: '000644' owner: 'ec2-user' group: 'ec2-user'
[3-1-8] Elastic IP
サーバーを再起動するたびにパブリックIPが変わると面倒なので、Elastic IPを設定しておきます。
# Elastic IP ElasticIp: Type: AWS::EC2::EIP Properties: InstanceId: !Ref Instance Domain: vpc
[3-2] スタックの作成
テンプレートが準備できたらスタックを作成します。
テンプレートファイルはローカルからのアップロードを選択しました。S3にアップロードしたものを参照することも可能です。
スタックの名前は任意の名称を入力します。今回は'stack1'としました。
パラメータはキーペアの名称(KeyName)以外はデフォルトのままにしました。キーペアのみコンボボックスから作成済みのキーペアを選択します。
「スタックオプションの設定」は変更なしで次に進みます。
「レビュー」では末尾の機能のみ注意が必要です。今回作成したテンプレートではIAMロールをEC2に付与しています。IAMリソースを作成しても問題ないか問われるので、チェックボックスにチェックして[スタックの作成]を押下します。
数分待つとスタックの作成が完了します。
インスタンスの状態を確認すると、設定通りに作成されていることが分かります。
[4] nginxインストール
EC2にsshでログインして、install_nginx.sh
を実行します。
sh install_nginx.sh
スクリプトの中身は次の通りです。nginxはAmazon Linux 2のextrasレポジトリで配布されているものをインストールします。
#!/bin/bash sudo amazon-linux-extras install nginx1 -y sudo systemctl start nginx sudo systemctl enable nginx
インストール後にバージョンを確認すると無事インストールされたことが分かります。
nginx -v # nginx version: nginx/1.18.0
[5] セキュリティグループの設定
EC2でset_security_group.sh
を実行します。
sh set_security_group.sh
実行後にマネージメントコンソールで確認すると、インバウンドルールが追加されています。
スクリプトの中身は次の通りです。セキュリティグループのIDとリージョン名は、スタック作成時に自動で設定されます。
#!/bin/bash IP_RANGE=`curl -s https://ip-ranges.amazonaws.com/ip-ranges.json` IP_LIST=`echo $IP_RANGE | jq -r '.prefixes[] | if .service == "CLOUDFRONT" then .ip_prefix else empty end'` count=0 for IP in $IP_LIST; do if [ $count -lt 60 ]; then SG={セキュリティグループ2のID} elif [ $count -lt 120 ]; then SG={セキュリティグループ3のID} else SG={セキュリティグループ4のID} fi aws ec2 authorize-security-group-ingress --region {リージョン名} --group-id $SG --protocol tcp --port 80 --cidr $IP echo $IP (( count++ )) done
[5-1] シェルスクリプトの解説
シェルスクリプトは下記記事を参考にさせていただきました。
EC2のセキュリティグループにCloudFrontからしかアクセスを許可しない設定を追加する(改良版) | 綺麗に死ぬITエンジニア
[5-1-1] CloudFrontのIPアドレス取得
CloudFrontエッジサーバのIPアドレスはip-ranges.jsonから取得します。ip-ranges.jsonから必要な情報をjq
コマンドで取り出します。
ip-ranges.jsonはCloudFront以外の情報も含んでいます。service == "CLOUDFRONT"
でCloudFrontの情報を抽出します。また、抽出した情報はIPアドレス以外の情報も含んでいるため.ip_prefix
でIPアドレスを抽出します。
IP_RANGE=`curl -s https://ip-ranges.amazonaws.com/ip-ranges.json` IP_LIST=`echo $IP_RANGE | jq -r '.prefixes[] | if .service == "CLOUDFRONT" then .ip_prefix else empty end'`
[5-1-2] セキュリティグループのルール追加
抽出したIPアドレスからのHTTPアクセス(TCPプロトコルのポート80)を許可するルールを、セキュリティグループに追加します。
count
でIPアドレスの個数をカウントします。1セキュリティグループあたり、60ルール以内に収まるようにしています。
セキュリティグループへのルール追加はaws ec2 authorize-security-group-ingress
で行います。
count=0 for IP in $IP_LIST; do if [ $count -lt 60 ]; then SG={セキュリティグループ2のID} elif [ $count -lt 120 ]; then SG={セキュリティグループ3のID} else SG={セキュリティグループ4のID} fi aws ec2 authorize-security-group-ingress --region {リージョン名} --group-id $SG --protocol tcp --port 80 --cidr $IP echo $IP (( count++ )) done
[6] CloudFrontの設定
外部からnginxサーバーにアクセスする際は、'test.example.com'のようにドメイン形式でアクセスできるようにします。そして、外部からのアクセスはCloudFrontがHTTPSのみ受け付けて、CloudFrontからEC2へHTTPへ流します。それぞれの関係を表すと次の通りです。
サブドメイン名(test.exmaple.com) <---> CloudFront <---> EC2のパブリック IPv4 DNS
[6-1] CloudFormationのテンプレート
[6-1-1] パラメータ
次の内容はユーザーが入力します。調べ方は後述します。
パラメータ | 備考 |
---|---|
EC2インスタンスのパブリック IPv4 DNS | CloudFrontのOrigin Domain Nameに指定。 |
Certificate ManagerのARN | CloudFrontのSSL Certificateに指定。 |
ホストゾーン ID | ドメイン登録時に作成されたホストゾーン。 |
サブドメイン名 | 任意。Route53で登録したドメインのサブドメイン。 |
AWSTemplateFormatVersion: 2010-09-09 Description: >- AWS CloudFormation template for the nginx server. Parameters: # Instance InstancePublicDns: Description: Instance Public DNS Type: String Default: 'ec2-xxx' # Certificate ManagerのARN AcmArn: Description: AWS Certificate Manager ARN Type: String Default: 'arn:aws:acm:xxx' # ホストゾーンのID HostedZoneId: Description: FQDN of the hosted zone Type: String Default: 'Z0xxx' # サブドメイン名 SubDomain: Description: Sub Domain Name Type: String Default: 'xxx.example.com'
[6-1-2] CloudFront
CloudFrontは次の設定にします。
- Origin側(EC2)はHTTP Only
- Target側(CloudFront)はHTTPからHTTPSへリダイレクト
- Certificate Managerで発行した証明書を関連付け
- サブドメインを使用する
Resources: # CloudFront Distribution CloudFrontDistribution: Type: 'AWS::CloudFront::Distribution' Properties: DistributionConfig: Aliases: - !Ref SubDomain # インスタンス側はHTTP Onlyに設定 Origins: - CustomOriginConfig: OriginProtocolPolicy: http-only DomainName: !Ref InstancePublicDns Id: !Sub '${AWS::StackName}-cloudfront-distribution' DefaultCacheBehavior: # TargetOriginIdは Origins Idと一致していないとNG TargetOriginId: !Sub '${AWS::StackName}-cloudfront-distribution' ViewerProtocolPolicy: redirect-to-https # ForwardedValuesは公式非推奨(CachePolicyが推奨だが面倒) ForwardedValues: QueryString: false # SSL Certificate ViewerCertificate: SslSupportMethod: sni-only AcmCertificateArn: !Ref AcmArn MinimumProtocolVersion: TLSv1.2_2019 HttpVersion: http2 IPV6Enabled: true # Enabledは必須 Enabled: true Tags: - Key: Name Value: !Sub '${AWS::StackName}-cloudfront-dns'
MinimumProtocolVersion
とHttpVersion
, IPV6Enabled
は任意のため指定しなくても問題はありません。
代替ドメイン名 (CNAME) を追加してカスタム URL を使用する - Amazon CloudFront
[6-1-3] Route53のエイリアスレコード
サブドメイン名とCloudFrontを関連付けします。CloudFront自体のドメイン名は'*.cloudfront.net'ですが、自分が決めたサブドメイン(www.example.com等)でCloudFrontにアクセスできるようにします。
# DNS Record DnsRecord: Type: 'AWS::Route53::RecordSet' Properties: HostedZoneId: !Ref HostedZoneId Comment: 'DNS name for CloudFront' Name: !Ref SubDomain Type: A AliasTarget: # AliasTargetのHostedZoneIdはホストゾーンのIDとは別 # CloudFrontの場合は固定 HostedZoneId: Z2FDTNDATAQYW2 DNSName: !GetAtt CloudFrontDistribution.DomainName
注意点はAliasTarget
のHostedZoneId
です。CloudFrontを使用する場合は「Z2FDTNDATAQYW2」固定です。
AWS::Route53::RecordSetGroup AliasTarget - AWS CloudFormation
[6-2] スタックの作成
先程と同様にマネージメントコンソールから作成します。
[6-2-1] パラメータの調べ方
サブドメイン名は任意に決めます。それ以外のパラメータはマネージメントコンソールから確認します。
EC2インスタンスのパブリック IPv4 DNS
Certificate ManagerのARN
ホストゾーン ID
[6-2-2] スタックの作成
調べたパラメータを入力します。
以降の項目はデフォルトのまま進めます。作成を開始して5分程度で作成が完了しました。
[7] 動作確認
設定したサブドメイン名(www.example.com等)でアクセスできるのかを確認します。
ブラウザのURL欄にサブドメイン名を入力します。正常に動作していれば、nginxのテストページが表示されます。
サーバ証明書を確認すると、有効になっていることが分かります。
EC2のIPアドレスを直に入力するとアクセスできないことが確認できます。セキュリティグループでCloudFrontからのアクセスのみ許可したため、EC2に直接アクセスできなくなっています。
[8] スタックの削除
作成したスタックを削除します。削除は新しいスタックから順に行います。
'CloudFrontDistribution'の削除は作成時と同様に5分程度かかります。
終わりに
複雑な構成では無いので手動で行えば、それほど時間はかかりません。しかし、CloudFormationでやろうとすると思いのほか時間がかかりました。
CloudFrontを使うことによって、CloudFormationのテンプレートが大分複雑になった感はあります。CloudFrontのIPをセキュリティグループに設定するのは、もう少し簡単になるといいなと思いました。
ただ、テンプレートを1回作成してしまえば何度でも使い回せるのはメリットです。だいぶ時間はかかりましたが、やってみて良かったなと思います。
ちなみに、HTTPS対応はCloudFrontだけが選択肢ではありません。ALBやNLBで代替可能な場合は検討してみるのもよいでしょう。また、EC2にSSHでログインして操作を行いましたが、Session Managerを使用する選択肢もアリです。
参考資料
AWSでWebサイトをHTTPS化 全パターンを整理してみました – ナレコムAWSレシピ
CloudFormationを使ってS3とCloudFrontの構成で静的Webサイトを構築する(Origin Access Identityは使わない) - Qiita
CloudFormation で OAI を使った CloudFront + S3 の静的コンテンツ配信インフラを作る | DevelopersIO
【機械学習】matplotlibの日本語文字化け対策(Amazon Linux2)
※ 2020/05/20にQrunchで書いた記事を移行しました。
matplotlibの文字化け対策は多くの情報がありますが、Amazon Linux2向けの情報はありませんでした。他のOSと基本的な対策方針は同様です。日本語フォントをインストールし、キャッシュを削除します。
対策手順
[1] 日本語フォントのインストール
IPAexフォントの一つである'IPAGothic'をインストールします。
sudo yum -y install ipa-gothic-fonts
[2] ソースコードに追記
キャッシュの削除、及び、日本語フォントを指定するコードを追加します。
import matplotlib as mpl import matplotlib.pyplot as plt mpl.font_manager._rebuild() # キャッシュの削除 plt.rcParams['font.family'] = 'IPAGothic' # 日本語フォントを指定
[3] 設定ファイルの変更
matplotlibの設定ファイルを変更します。 この方法で上手くいくようなら、[2]ソースコードの追記は不要です。
まずは以下のソースコードで、設定ファイルの場所を把握します。
import matplotlib as mpl print(mpl.matplotlib_fname()) # /home/ec2-user/.local/lib/python3.7/site-packages/matplotlib/mpl-data/matplotlibrc
設定ファイルに以下の行を追加します。
font.family : IPAGothic
終わりに
他OS向けの対策をしても若干手順が異なり上手くいかず、思いのほか時間がかかりました。
ソースコードに追記する方法は、下記記事を参考にさせていただきました。
SageMaker上のmatplotlibを日本語表記にする | Developers.IO
備忘録
matplotlibの情報表示
import matplotlib as mpl print(mpl.get_configdir()) # 設定ディレクトリの表示 print(mpl.get_cachedir()) # キャッシュディレクトリの表示 print(mpl.matplotlib_fname()) # 設定ファイルの表示 print(mpl.font_manager.findSystemFonts()) # フォントリストの表示 print(mpl.rcParams['font.family']) # 使用中フォントの表示
IPAGothic以外のIPAexフォントのインストール
sudo yum -y install ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts
【AWS】Amazon Linux 2でJupyter Notebookを使えるようにするまで
Amazon Linux 2でJupyter Notebookを使えるようにするまで*1
※ 2020/02/26にQrunchで書いた記事を移行しました。
Jupyter Notebookは機械学習を実行する環境として有名です。 機械学習には触れてきましたが、Jupyter Notebookには触れてこなかったので使ってみようと思いました。 使用する言語はPython3です。
- なぜAmazon Linuxなのか
- [1] EC2インスタンスを作成する
- [2] 各種パッケージをインストール
- [3] Jupyter Notebookのインストールと設定
- [4] 動作確認
- [5] Jupyter Notebookを自動起動する
- 終わりに
なぜAmazon Linuxなのか
私がAWSを使い慣れているから、ただそれだけです。 Google ColaboratoryやGCP, 御自分のPCでJupyter Notebookを使われても問題ないと思います。むしろ、そちらの方が便利かもしれません。
[1] EC2インスタンスを作成する
OSがAmazon Linux2のEC2インスタンスを作成します。 NAT構成などは考えず、シンプルな1台構成の場合について書きます。
[1-1] セキュリティグループの設定
インバウンド
ブラウザからJupyter Notebookにアクセスするために、ポート8888を許可します。 ポート番号は8888以外でも可能です。このあと行うJupyter Notebookの設定と一致していれば問題ありません。
タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|
カスタム TCP ルール | TCP | 8888 | 0.0.0.0/0 |
SSH | TCP | 22 | 0.0.0.0/0 |
アウトバウンド
タイプ | プロトコル | ポート範囲 | ソース |
---|---|---|---|
HTTP | TCP | 80 | 0.0.0.0/0 |
HTTPS | TCP | 443 | 0.0.0.0/0 |
[2] 各種パッケージをインストール
[2-1] yumのupdate
お決まりの手順ですが、まずはyumのupdateを行います。
$ sudo yum -y update
[2-2] Python3のインストール
Python3をインストールします。
$ sudo yum -y install python3 python3-devel
[3] Jupyter Notebookのインストールと設定
[3-1] Jupyter Notebook用のユーザー作成
Jupyter Notebook実行用のユーザーを作成します。
$ sudo useradd jupyter $ sudo passwd jupyter
[3-2] Jupyter Notebookのインストール
[3-1]で作成したユーザーにスイッチし、jupyterをインストールします。 (1)Anacondaをインストールする方法と、(2)Jupyter Notebook単体でインストールする方法の2種類ありますが、(2)Jupyter Notebook単体でインストールする方法を採用しました。
$ su - jupyter
$ pip3 install jupyter --user
Anacondaであればデータサイエンスに必要な各種ライブラリがまとめてインストールされますが、容量を食いそうなので、Jupyter Notebook単体でインストールしておきます。
[3-3] Jupyter Notebookの設定1
jupyter_notebook_config.py
というファイルに設定情報を書いていきます。
$ mkdir .jupyter
$ vim .jupyter/jupyter_notebook_config.py
jupyter_notebook_config.pyの中身は次の通りです。
c.NotebookApp.notebook_dir = '/home/jupyter/' c.NotebookApp.ip = '*' c.NotebookApp.open_browser = False # c.NotebookApp.port = 8888 # デフォルトは8888
jupyter notebook --generate-config
を叩くと、~/.jupyter/jupyter_notebook_config.py
を雛形として作ってくれますが、雛形は使用せず最低限の情報だけを書きました。
[3-4] Jupyter Notebookの設定2
このままだとセキュリティ的によろしくないので、パスワード認証を追加します。
python3 -c 'from notebook.auth import passwd;print(passwd())' Enter password: Verify password: sha1:*****************************************************
表示されたsha1:***
をコピーし、jupyter_notebook_config.py
のc.NotebookApp.password
にペーストします。
c.NotebookApp.password = 'sha1:*****************************************************`
[4] 動作確認
[4-1] Jupyter Notebookの起動
$ jupyter notebook [I 14:36:03.057 NotebookApp] Writing notebook server cookie secret to /home/ec2-user/.local/share/jupyter/runtime/notebook_cookie_secret [W 14:36:03.245 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended. [I 14:36:03.248 NotebookApp] Serving notebooks from local directory: /home/ec2-user/.jupyter [I 14:36:03.248 NotebookApp] The Jupyter Notebook is running at: [I 14:36:03.248 NotebookApp] http://ip-10-3-0-10.ap-northeast-1.compute.internal:8888/ [I 14:36:03.248 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[4-2] ブラウザから接続
ブラウザでhttp://[EC2インスタンスのグローバルIP]:8888
にアクセスします。
JupyterNotebookが起動してればログイン画面が表示されます。
先ほど設定したパスワードを入力すると無事にログインできました。
[5] Jupyter Notebookを自動起動する
CentOS7にJupyter Notebookを導入とサービス化したJupyter notebookからローカルに構築したtensorflowを使えるようにしたを参考にさせていただきました。
[5-1] jupyterのパスを確認する
このあと作成するユニットファイルに、jupyterのフルパスを設定します。
$ which jupyter
~/.local/bin/jupyter
[5-2] ユニットファイルを作成する
ここからはec2-userで操作を行います。jupyterユーザーはsudoersではないので (jupyterユーザーをsudoersに追加する方法でも問題ありません)。
$ exit logout
jupyter用のユニットファイルを作成します。
$ sudo vim /etc/systemd/system/notebook.service
ユニットファイルの内容は以下の通りです。
[Unit] Description = Jupyter Notebook [Service] Type=simple PIDFile=/var/run/jupyter-notebook.pid ExecStart=/home/jupyter/.local/bin/jupyter notebook WorkingDirectory=/home/jupyter/ User=jupyter Group=jupyter Restart=always [Install] WantedBy = multi-user.target
[5-3] EC2インスタンス立上時に起動する
$ sudo systemctl enable notebook
Created symlink from /etc/systemd/system/multi-user.target.wants/notebook.service to /etc/systemd/system/notebook.service.
[5-4] 本当に起動しているのか確認する
$ systemctl status notebook ● notebook.service - Jupyter Notebook Loaded: loaded (/etc/systemd/system/notebook.service; enabled; vendor preset: disabled) Active: active (running) since 火 2020-02-25 16:28:19 UTC; 30s ago
終わりに
手順がそれなりに多かったため少々面倒でした。 頻繁にEC2インスタンスの削除・作成を行う場合は、コード化した方がよいでしょう。 パスワード入力のところ以外はコード化可能だと思います(パスワード入力も不可能ではありませんがやりたくはない)。
また、今回はSSL/TLSは使いませんでしたが、セキュリティ的には使った方がよいでしょう。
Running a notebook server — Jupyter Notebook 7.0.0.dev0 documentation
【AWS】Cloud9のEC2環境をCloudFormationで構築する
AWS Cloud9のEC2環境をCloudFormationで構築する*1
※ 2020/02/12にQrunchで書いた記事を移行しました。
EC2環境をCloudFormationで構築することも出来ます。
EC2環境の構築はマネージメントコンソール経由でも非常に簡単です。 なので、個人で使う分には、CloudFormationとマネージメントコンソール、どちらで構築しても時間の差はほとんどないでしょう。
CloudFormationのメリットは、CLIで構築できる点です。 複数環境を用意するときや、構築や破棄を短期間に繰り返す場合には、CloudFormationのメリットが活きるでしょう。
SSH環境はCloudFormationで構築出来ませんでした。 CloudFormationで作成したEC2インスタンスを使って、SSH環境を構築することは可能です。
[1] テンプレートファイルの書き方
[1-1] 最小限の構成
InstanceTypeのみを指定すれば、残りの項目はデフォルト値をセットしてくれます。 Logical IDはテンプレートファイル内で任意であれば、なんでも良いです。
{Logical ID}: Type: AWS::Cloud9::EnvironmentEC2 Properties: InstanceType: {インスタンスのタイプ}
[1-2] 全ての項目
AWS::Cloud9::EnvironmentEC2に全項目の説明が載っていますが、項目の数は少ないです。
{Logical ID}: Type: AWS::Cloud9::EnvironmentEC2 Properties: InstanceType: {インスタンスのタイプ} Name: {環境の名前} Description: {環境の説明} AutomaticStopTimeMinutes: {最後に使ってからシャットダウンされるまでの時間} SubnetId: {インスタンスと通信するサブネットのID} Repositories: - RepositoryUrl: {CodeCommitリポジトリのURL} PathComponent: {CodeCommitリポジトリの展開先}
[1-3] 筆者の構成
パラメータで変更できるようにしています。
C9Environment: Type: AWS::Cloud9::EnvironmentEC2 Properties: Name: !Sub '${AWS::StackName}-Cloud9-IDE' Description: !Sub '${AWS::AccountId}-${AWS::Region}-${AWS::StackName}' AutomaticStopTimeMinutes: !Ref C9StopTime InstanceType: !Ref C9InstanceType SubnetId: !Ref Subnet Repositories: - RepositoryUrl: !Sub 'https://git-codecommit.${AWS::Region}.amazonaws.com/v1/repos/${RepositoryName}' PathComponent: !Sub 'codecommit/${RepositoryName}'
[2] テンプレートファイル全体の構成
最小限の構成であればわずか6行で、t3.microのEC2環境が作成できます。
AWSTemplateFormatVersion: 2010-09-09 Resources: C9Environment: Type: AWS::Cloud9::EnvironmentEC2 Properties: InstanceType: t3.micro
参考文献
Create an AWS Cloud9 environment with AWS CloudFormation
*1:Mudassar IqbalによるPixabayからの画像