❤️

AWSを使ってWebスクレイピング用の環境を構築する

11 min read

モチベーション

前回の記事で、データ収集の環境をAWSを使った方がコストを抑えられそうであることがわかったので、今回は0から実際に環境を構築してみる。
AWS初心者なので構築中に色々トラブったが、最後まで構築できたのでそこで得た知見をまとめてみる。

ゴール

今回作る環境は以下。インターネットからEC2に接続できるようにすること、EC2にスクレイピング用のPython3、Gitをインストールすること、EC2からRDBに接続できることが今回のゴール。

なお、2020年12月に公開されたAWS CloudShellを使ってコマンドベースで環境を構築していく。
(新しいサービスが出たら使ってみようと試みるのが私の流儀)

AWS CloudShellの導入

  1. AWSマネジメントコンソールにログインし、リージョンをCloudShellが利用できるところ[1]に設定し、ヘッダーにある以下の赤文字で囲ったアイコンをクリックする。
  2. アイコンクリック後、CloudShellのページに遷移する。デフォルトでAWSCLI、Python、Node.jsなどが使えるようだ。

事前準備はこれだけ。インストールもしなくていいしブラウザだけで準備が終わるのは素晴らしいと思う。

試しにコマンドを打ってみる

お試しで一つコマンドを打ってみる。

CLIのバージョンを確認する

$ aws --version
aws-cli/2.0.58 Python/3.7.3 Linux/4.14.209-160.335.amzn2.x86_64 exec-env/CloudShell exe/x86_64.amzn.2

CLIのバージョンは2のようだ。

実装手順

細かい実装に入る前に、手順を記載する。

  1. VPCを作成する
  2. パブリックサブネット、プライベートサブネットを2つずつ作成する
  3. パブリックサブネットにEC2を構築する
  4. インターネットゲートウェイを作成する
  5. VPCにインターネットゲートウェイをアタッチする
  6. パブリックサブネットのルートテーブルを編集する
  7. サブネットグループを作成する
  8. RDSを作成する
  9. EC2からRDSに接続できるか確認する
  10. PythonとGitをインストールする

構築

何をするかイメージがついたので、実行していく。

VPCの作成

まずはVPCを作成する。今回は、CIDRブロック10.0.0.0/21を使用する。
(余談だが、AWSのブロックサイズ(/数字の部分)は16~28までなので注意すること。)

aws ec2 create-vpc --cidr-block 10.0.0.0/21

このコマンドを打つと以下のようなJSON形式で出力が返ってくる。その中から、「VpcId」の値をメモしておく。

{
    "Vpc": {
        "VpcId": "vpc-XXXXXXXXXX"
    }
}

パブリックサブネット、プライベートサブネットを2つずつ作成

以下のようなパブリックサブネットとプライベートサブネットを作成する。

サブネット アベイラビリティゾーン(以下、AZ) CIDR
publicSubnet01 ap-northeast-1a 10.0.0.0/24
publicSubnet02 ap-northeast-1c 10.0.1.0/24
privateSubnet01 ap-northeast-1a 10.0.2.0/24
privateSubnet02 ap-northeast-1c 10.0.3.0/24

サブネットを作成するコマンド

aws ec2 create-subnetコマンドを使う。その時に指定するパラメータは以下。

パラメータ名 意味
vpc-id VPCのID
cidr-block CIDRブロック
availability-zone サブネットの所属先AZ

例として先ほど作成したVPCに対して、CIDRブロック「10.0.0.0/24」、所属先AZ「ap-northeast-1a」のサブネットを作成する

aws ec2 create-subnet --vpc-id vpc-XXXXXXXXXX --cidr-block 10.0.0.0/24 --availability-zone ap-northeast-1a

作成したサブネットに名前「publicSubnet01」をつけておく

aws ec2 create-tags --resources subnet-XXXXXXXXXX --tags Key=Name,Value=publicSubnet01

ここまででできた構成図は以下。ここまででEC2やRDSを入れるための箱が出来上がったイメージ。

パブリックサブネットにEC2を構築する

ここから実際にEC2の構築を行っていく。

キーペアを作成する

EC2に接続するための鍵を作成する。

aws ec2 create-key-pair --key-name MyKeyPair --query 'KeyMaterial' --output text > MyKeyPair.pem

他のユーザーが読み書きできないようにするため、作成した鍵の権限を変更する。

chmod 400 MyKeyPair.pem

セキュリティグループを作成する

以下のようなセキュリティグループ(以下SG)を作成する。

名前 タイプ ポート範囲 プロトコル ソース
SSH-SG-1 SSH 22 TCP 0.0.0.0/0
aws ec2 create-security-group --group-name SSH-SG-1 --vpc-id vpc-XXXXXXXXXX

作成に成功したら「GroupId」が返ってくるのでメモする。

{
    "GroupId": "sg-XXXXXXXXXX"
}

上のコマンドを叩いただけではSSH接続はできないのでアクセスできるようにルートを追加する。

ルートを追加する

SSHでどこからでもアクセスできるようにする。
--group-idのところは事前に作成したSGのGroupIdを指定する。

aws ec2 authorize-security-group-ingress --group-id sg-XXXXXXXXXX --protocol tcp --port 22 --cidr 0.0.0.0/0

もしHTTPで通信したい場合は--portを80に変更する。

0.0.0.0/0を使用するとSSH経由でどこでも誰でもアクセスできるようになるため、本番環境では使わないこと。

EC2インスタンスを起動する

キーペアとSGを作成したのでEC2を起動してみる。
少しパラメータが複雑なので補足。

パラメータ名 意味
image-id AMIイメージのID
count 起動するインスタンス数
instance-type 起動するインスタンスのタイプ
key-name キーペアの名前
security-group-ids 適用するSG
subnet-id 起動先のサブネット
aws ec2 run-instances --image-id ami-0992fc94ca0f1415a --count 1 --instance-type t3a.medium --key-name MyKeyPair --security-group-ids sg-XXXXXXXXXX --subnet-id subnet-XXXXXXXXXX

起動完了に時間がかかるので、完了するまで待つ。
(コマンドを叩くとJSON形式で値が返ってくるのでインスタンスIDを探してメモしておく。)
完了したか確認するためには以下のコマンドを叩いてインスタンスの状態を確認する。

aws ec2 describe-instances --instance-id i-XXXXXXXXXX

ちょっと寄り道

image-idの探し方

マネジメントコンソールから探すやり方とコマンドを叩いて探すやり方がある。私はなんとなく画面から見た方が楽そうだと思ったのでマネジメントコンソールから探した。

マネジメントコンソールから探す

  1. EC2ダッシュボードの左メニューから「インスタンス」を選択する。
  2. 画面右上の「インスタンスを起動」を選択する。
  3. AMIの選択ページに遷移する。image-idがAMI名の横に記載されている

コマンドで探す

describe-imagesコマンドを叩くことで確認できる。
色々オプションを指定できる。よく使うと思われるオプションの説明を以下に記入する。

パラメータ名 意味
region リージョン
owners イメージのオーナー
filters 抽出条件。アーキテクチャやAMIの名前を指定することで特定のAMIを検索できる
output 検索結果の出力形式
aws ec2 describe-images --region [region] --owners [owner] --filters '[condition]' --output [output]

インターネットゲートウェイを作成する

EC2を起動させることに成功したからと言って安心してはいけない。SSHでEC2に接続するためには、インターネットゲートウェイを使ってインターネットからの接続を受け入れるようにしないといけないからだ。これからインターネットゲートウェイを作成し、インターネットからの接続を受け入れられるようにする。

作り方

create-internet-gatewayコマンドを叩く。

aws ec2 create-internet-gateway

これでインターネットゲートウェイが作成できた。返される出力からInternetGatewayIdをメモしておく。

{
    "InternetGateway": {
        "InternetGatewayId": "igw-XXXXXXXXXX",
    }
}

VPCにアタッチする

最初に作ったVPCにインターネットゲートウェイをアタッチする。

aws ec2 attach-internet-gateway --vpc-id vpc-XXXXXXXXXX --internet-gateway-id igw-XXXXXXXXXX

ルートテーブルを作成する

aws ec2 create-route-table --vpc-id vpc-XXXXXXXXXX

VPC以外の通信の向き先をインターネットゲートウェイにする

aws ec2 create-route --route-table-id rtb-XXXXXXXXXX --destination-cidr-block 0.0.0.0/0 --gateway-id igw-XXXXXXXXXX

ルートテーブルをパブリックサブネットに関連づける

aws ec2 associate-route-table  --subnet-id subnet-XXXXXXXXXX --route-table-id rtb-XXXXXXXXXX

Elastic IPアドレスを作成し、EC2に関連づける

IPアドレスを取得する

aws ec2 allocate-address

返ってくる「AllocationId」をメモする。

Elastic IPアドレスを付与する

aws ec2 associate-address --instance-id i-XXXXXXXXXX --allocation-id eipalloc-XXXXXXXXXX

OKだとAssociationIdが返ってくる。

EC2インスタンスに接続する

ssh -i "作成したキーペア" ec2-user@EC2に紐づけられたパブリックIPアドレス

成功したら以下が表示される。


       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/

これでEC2インスタンスをパブリックサブネット上に起動し、インターネットゲートウェイを経由して自分のPCからSSH接続ができるようになった。現段階の構成図は以下。残るはRDS。

RDSを作成する

構築もいよいよ大詰め。これからRDSを作成する。

サブネットグループを作成する

RDSを作るときにはサブネットグループが必要なので作成する。

aws dax create-subnet-group --subnet-group-name my-subnet-group --subnet-ids subnet-XXXXXXXXXX subnet-XXXXXXXXXX

セキュリティグループを作成する

RDS用のSGを作成する。

aws ec2 create-security-group --group-name RDS-SG-1 --vpc-id vpc-XXXXXXXXXX --description "RDS"

次にインバウンドルールを変更する。

aws ec2 authorize-security-group-ingress --group-id sg-XXXXXXXXXX --protocol tcp --port 3306 --cidr 0.0.0.0/0

RDSインスタンスを作成する

今回作成するRDSの設定は以下。

パラメータ名 意味 今回設定する値
db-instance-identifier DB インスタンス識別子 my-scraping-db-instance
db-instance-class 起動するDBインスタンスのクラス db.t2.micro
engine 起動するDBエンジン MySQL
engine-version 起動するDBエンジンのバージョン 8.0.11
allocated-storage ストレージ容量 20
master-username マスターユーザー名 root
master-user-password マスターユーザーのパスワード password
backup-retention-period バックアップ保持期間 3
engine-version 起動するDBエンジンのバージョン 8.0.21
vpc-security-group-ids SG 上で作成したSG
availability-zone AZ ap-northeast-1a
publicly-accessible 外部からのアクセスを許可するか 許可しない(空白)
db-subnet-group-name DBを起動するサブネットグループ名 my-subnet-group
aws rds create-db-instance \
    --db-instance-identifier my-scraping-db-instance \
    --db-instance-class db.t2.micro \
    --engine MySQL \
    --engine-version 8.0.21 \
    --allocated-storage 20 \
    --master-username root \
    --master-user-password password \
    --backup-retention-period 3 \
    --vpc-security-group-ids sg-XXXXXXXXXX \
    --availability-zone ap-northeast-1a \
    --publicly-accessible \
    --db-subnet-group-name my-subnet-group

実行してみたらエラーになった。

An error occurred (InvalidVPCNetworkStateFault) when calling the CreateDBInstance operation: Cannot create a publicly accessible DBInstance.  The specified VPC does not support DNS resolution, DNS hostnames, or both. Update the VPC and then try again

エラー文言みる限り、DNS名前解決かDNSホスト名が無効になっているっぽいので確認する。
まずはDNS名前解決が有効になっているか確認する。

aws ec2 describe-vpc-attribute --region ap-northeast-1 --vpc-id vpc-XXXXXXXXXX --attribute enableDnsSupport

有効になっていることを確認できた。

{
    "VpcId": "vpc-XXXXXXXXXX",
    "EnableDnsSupport": {
        "Value": true
    }
}

DNSホスト名はどうだろうか。

aws ec2 describe-vpc-attribute --region ap-northeast-1 --vpc-id vpc-XXXXXXXXXX --attribute enableDnsHostnames

こちらは無効になっていたのでこれが犯人っぽい。

{
    "VpcId": "vpc-XXXXXXXXXX",
    "EnableDnsHostnames": {
        "Value": false
    }
}

犯人がわかったので、DNSホスト名を有効に変更する。

aws ec2 modify-vpc-attribute --region ap-northeast-1 --vpc-id vpc-XXXXXXXXXX --enable-dns-hostnames '{"Value":true}' 

有効になったことを確認してから、再度RDSを作成コマンドを打ったら成功した。
これで全てのピースが揃った。あとは疎通確認と必要なツールをインストールする。

EC2からRDSに接続できるか確認する

EC2にログインし、mysqlをインストールする

-- 先にアップデートする。
sudo yum update
sudo yum install mysql

インストールが完了したら末尾に「Complete!」が出てくる。

ログインしてみる

mysql -h <RDSエンドポイント名> -P 3306 -u <マスターユーザ名> -p

無事に接続できた。一安心。

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 17
Server version: 8.0.21 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 

PythonとGitをインストール

総仕上げにPython3とGitをインストールする。

-- Python3のインストール。
sudo yum install python3 -y
-- インストールできたか確認。
python3 --version

-- Gitのインストール。
sudo yum install git
-- インストールできたか確認。
git --version

二つともインストールできたことを確認できたのでこれにて環境構築は終了。
お疲れ様でした!

構築してみた所感

マネジメントコンソールから環境を作っていた時はあまり意識していなかったことがCLIで作った時は考慮しないといけなかったので、かなり大変だったが勉強になった。
また、CLIのエラー文がわかりやすかったため、エラーが出ても特につまづくことなく原因をサクッと掴むことができた。自分はマネジメントコンソールからよりもコマンドを使って環境構築する方が向いているかもしれない。
次回はWebサービスのインフラを構築しようと思う。コマンド叩くのも大変なのでCloudFormationを使って自動化したい。

友人にできたことを伝えてみた

私「前話してたやつ、ちゃんとできたで!」
友人「おぉ、やるやん、ありがとう」
なんかちょっと腹がたったけど「どうも」とだけ返しておいた。

おわり。

脚注
  1. 利用可能なリージョンはUS East(バージニア), US East(オハイオ), US West(オレゴン), Europe(アイルランド),Asia Pacific(東京)の5つ。 ↩︎