どこにでもいる30代SEの学習ブログ

主にプログラミング関連の学習内容。読んだ本の感想や株式投資についても書いてます。

【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] 前提条件

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 T3 インスタンス | AWS

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からの移行を試すには非常にありがたいです。

f:id:predora005:20210103213829p:plain

Amazon EC2 T4g Instances - Amazon Web Services

注意点

t3を始めとしたIntel系(x86)のインスタンスと、ライブラリのインストール方法が異なる場合があります。ソースからビルドしてインストールが必要な場合もあります。

numpy, pandas, matplotlibのインストール手順は下記記事にまとめています。

predora005.hatenablog.com

終わりに

Armは昨年、ソフトバンクからNVIDIAに売却されることで話題になりましたね。Armは業績が伸び悩んでいると言われていますが、今後どうなっていくのか楽しみです。

参考資料

下記記事はOpenSSLで実際の性能を測定しており面白い記事でした。

ARMベースのCPUをバースト利用できる「T4g」インスタンスの性能をOpenSSLで測定してみた | DevelopersIO

【AWS】Route53でドメインを登録してHTTPS対応のnginxサーバーをCloudFrontとCloudFormationで構築するまで

f:id:predora005:20210101231443p:plain

AWSHTTPS対応のnginxサーバーを構築するまでの手順をまとめました。

HTTPS対応のサーバーを構築する手順は多々あります。今回は「CertificateManager」でサーバ証明書を発行し「CloudFront」でHTTPS対応する構成にしました。

Route53で独自ドメインの登録を行いましたが、別のプロバイダで登録することも可能です。

f:id:predora005:20210101145635p:plain

[0] 前提条件

nginxインストールなどの操作を行うために、EC2へSSHでログインします。SSHでログインするためのキーペアは作成済みとします。

[前提条件]

  • キーペアは作成済み

[1] Route53でドメイン登録

マネージメントコンソールから、Route53で独自ドメインを登録します。

f:id:predora005:20210101150311p:plain

[1-1] ドメインの選択

ドメインの選択欄にドメイン名を入力し[チェック]を押すと、利用可能か否かと料金が表示されます。

f:id:predora005:20210101150625p:plain

[カートに入れる]を押すとカートに料金と登録期間が表示されます。登録期間を過ぎても自動延長できます。長期間使用する予定がなければ1年のままで問題ありません。画面下部の[続行]を押して次に進みます。

f:id:predora005:20210101150838p:plain

[1-2] 連絡先の入力と購入

登録者の連絡先を登録します。電話番号以外は日本語表記で問題ありません。

f:id:predora005:20210101151035p:plain

プライバシーの保護は、個人の場合は[有効化]にしておきます。

f:id:predora005:20210101151234p:plain

[続行]を押すと、次のように表示されますが問題ありませんでした。心配な場合は連絡先に登録したEメールを確認しましょう。AWSからメールが来ているはずです。

f:id:predora005:20210101151325p:plain

ドメインを登録すると自動的にホストゾーンが作成されます。ホストゾーンは12時間以上使用すると、0.5$/月かかります。しばらく使用しない場合は後で手動で削除しましょう。

ドメインの自動更新は個々の目的に応じて選びます。

f:id:predora005:20210101151440p:plain

連絡先に登録したEメールを確認し、メール内のリンクをクリックします。

f:id:predora005:20210101151806p:plain

リンクをクリックした後、[ステータスの更新]を押します。すると、ステータスが検証済みになります。

f:id:predora005:20210101151901p:plain

以上でドメインの登録は完了です。ドメイン登録が完了して使えるようになるまでには多少時間がかかります。私の場合は30分かかりました。

f:id:predora005:20210101154338p:plain

ホストゾーンが自動作成されたかを確認すると、作成されていました。

f:id:predora005:20210101152005p:plain

[2] Certificate Managerで証明書発行

続いて、Certificate Managerでサーバ証明書を発行します。証明書の発行は無料です。

なお、リージョンは「米国東部(バージニア北部)」にします。別のリージョンで発行するとCloudFrontと証明書の関連付けが出来ません。

CloudFront で SSL/TLS 証明書を使用するための要件 - Amazon CloudFront

CloudFront ディストリビューションで使用するために SSL 証明書を米国東部 (バージニア北部) リージョンに移行する

[2-1] 証明書発行

f:id:predora005:20210101152203p:plain

[パブリック証明書のリクエスト]を選択します。

f:id:predora005:20210101152249p:plain

ドメイン名を入力します。登録したドメインexample.comであれば、example.com*.example.comを登録します。ワイルドカードを使用してサブドメインも登録します。

f:id:predora005:20210101152555p:plain

検証方法は[DNSの検証]にします。

f:id:predora005:20210101152627p:plain

証明書を多数発行するなど特別な理由がなければ、タグは空欄のままで問題ありません。

f:id:predora005:20210101152846p:plain

内容を確認し[確認とリクエスト]を押します。

f:id:predora005:20210101152951p:plain

[2-2] 検証

「検証保留中」になっています。これを「発行済み」にしていきます。ドメイン名横のドロップダウンを選択します。

f:id:predora005:20210101153356p:plain

[Route53でのレコードの作成]を押下します。

f:id:predora005:20210101153945p:plain

[作成]を押します。

f:id:predora005:20210101154036p:plain

もう一方も同様にDNSレコードを作成します。

f:id:predora005:20210101154109p:plain

以上で証明書発行の手続きは完了です。「検証保留中」になっていますが、数分経つと「発行済み」に変わります。

f:id:predora005:20210101154816p:plain

[3] EC2インスタンス構築

EC2インスタンス構築に当たり、次のものを作成する必要があります。

  • VPC
  • サブネット
  • ルートテーブル
  • インターネットゲートウェイ
  • セキュリティグループ
  • 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を指定しておけば毎回入力する必要がなくて楽です。

  • SSHのキーペア (コンボボックスから選択できるようになる)
  • インスタンスタイプ
  • EBSの容量
  • CIDR
  • プライベートIP
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, サブネット, ルートテーブル, インターネットゲートウェイの設定です。特殊なのはVPCEnableDnsHostnamesEnableDnsSupportを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

Amazon EC2: 特定の VPC に関連付けられた EC2 セキュリティグループの管理をプログラムによりコンソールで許可する - AWS Identity and Access Management

[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] スタックの作成

テンプレートが準備できたらスタックを作成します。

f:id:predora005:20210101182916p:plain

テンプレートファイルはローカルからのアップロードを選択しました。S3にアップロードしたものを参照することも可能です。

f:id:predora005:20210101183214p:plain

スタックの名前は任意の名称を入力します。今回は'stack1'としました。

f:id:predora005:20210101183022p:plain

パラメータはキーペアの名称(KeyName)以外はデフォルトのままにしました。キーペアのみコンボボックスから作成済みのキーペアを選択します。

f:id:predora005:20210101183557p:plain

「スタックオプションの設定」は変更なしで次に進みます。

f:id:predora005:20210101183718p:plain

「レビュー」では末尾の機能のみ注意が必要です。今回作成したテンプレートではIAMロールをEC2に付与しています。IAMリソースを作成しても問題ないか問われるので、チェックボックスにチェックして[スタックの作成]を押下します。

f:id:predora005:20210101183925p:plain

数分待つとスタックの作成が完了します。

f:id:predora005:20210101184514p:plain

インスタンスの状態を確認すると、設定通りに作成されていることが分かります。

f:id:predora005:20210101185154p:plain

[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

実行後にマネージメントコンソールで確認すると、インバウンドルールが追加されています。

f:id:predora005:20210101185537p:plain

スクリプトの中身は次の通りです。セキュリティグループの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_prefixIPアドレスを抽出します。

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)を許可するルールを、セキュリティグループに追加します。

countIPアドレスの個数をカウントします。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'

MinimumProtocolVersionHttpVersion, 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

注意点はAliasTargetHostedZoneIdです。CloudFrontを使用する場合は「Z2FDTNDATAQYW2」固定です。

AWS::Route53::RecordSetGroup AliasTarget - AWS CloudFormation

[6-2] スタックの作成

先程と同様にマネージメントコンソールから作成します。

[6-2-1] パラメータの調べ方

サブドメイン名は任意に決めます。それ以外のパラメータはマネージメントコンソールから確認します。

EC2インスタンスのパブリック IPv4 DNS

f:id:predora005:20210101195157p:plain

Certificate ManagerのARN

f:id:predora005:20210101195225p:plain

ホストゾーン ID

f:id:predora005:20210101195242p:plain

[6-2-2] スタックの作成

調べたパラメータを入力します。

f:id:predora005:20210101211706p:plain

以降の項目はデフォルトのまま進めます。作成を開始して5分程度で作成が完了しました。

f:id:predora005:20210101212230p:plain

[7] 動作確認

設定したサブドメイン名(www.example.com等)でアクセスできるのかを確認します。

ブラウザのURL欄にサブドメイン名を入力します。正常に動作していれば、nginxのテストページが表示されます。

f:id:predora005:20210101212807p:plain

サーバ証明書を確認すると、有効になっていることが分かります。

f:id:predora005:20210101213004p:plain

EC2のIPアドレスを直に入力するとアクセスできないことが確認できます。セキュリティグループでCloudFrontからのアクセスのみ許可したため、EC2に直接アクセスできなくなっています。

f:id:predora005:20210101213951p:plain

[8] スタックの削除

作成したスタックを削除します。削除は新しいスタックから順に行います。

f:id:predora005:20210101214256p:plain

'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)

f:id:predora005:20200921201935p:plain

※ 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を使えるようにするまで

f:id:predora005:20200920114859j:plain
Amazon Linux 2でJupyter Notebookを使えるようにするまで*1

※ 2020/02/26にQrunchで書いた記事を移行しました。

Jupyter Notebookは機械学習を実行する環境として有名です。 機械学習には触れてきましたが、Jupyter Notebookには触れてこなかったので使ってみようと思いました。 使用する言語はPython3です。

なぜ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.pyc.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が起動してればログイン画面が表示されます。

先ほど設定したパスワードを入力すると無事にログインできました。

f:id:predora005:20200920114447j:plain

[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

*1:Photo MixによるPixabayからの画像

【AWS】Cloud9のEC2環境をCloudFormationで構築する

f:id:predora005:20200919224031p:plain
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からの画像