LoginSignup
9
12

More than 3 years have passed since last update.

典型的なwebサービスのawsを使ったインフラ構成を考えてみるS3, ECS

Last updated at Posted at 2019-12-15

概要

フロントエンドをVueを使って作り、APIなどのバックエンドをpythonのFlaskという軽量なフレームワークを使って作りました。今回は、ローカル環境下で動いていたアプリケーションをgithubから自動的にAWSの方にdeployすることができるようなインフラ構成を設計してみます。

不十分な点などありましたら、アドバイスいただけると幸いです。

使うもの

AWS関連

  • EC2
  • RDS
  • ECR
  • ECS(fargate)
  • S3
  • CloudFront
  • Route53
  • IAM

CI/CD関連

  • Circle CI

システム構成図

インフラ のコピー-Page-1.png

まず、public subnetとprivate subnetを持つVPCを作成します。外部からのアクセスを許容するpublic subnetには、ロードバランサーと踏み台(ログイン)サーバーを設置します。外部から直接アクセスできないprivate subnetには、APIサーバーとデータベースを置きます。

次に、API serverであるFlaskアプリとproxy serverとして利用するNginxは、コンテナ化して、ECS(Forgate)で管理します。Forgateを使うことで、自動でコンテナのスケーリングや再起動などをしてくれます。RDSは、AWSのamazon Auroraを利用します。こちらも、定期的にレプリカを作成してくれるので、万が一データを損失する場合やデータベースにアクセスできなくなった場合にも安心です。amazon Auroraにアクセスできるのは、(APIサーバーと)踏み台サーバーのEC2からのみで、外部からデータベースをいじることはできません。Login serverのEC2のインスタンスは、秘密鍵が保存されているローカルPCからのみアクセスできます。

フロントエンドのコードは、S3にデプロイし、CloudFront経由で配信します。あとは、Route53でのドメイン設定や、Certificate Managerでの証明書取得、ロードバランサーの設置など、細々した設定をしてあげると完成です。

CI/CD関連

インフラ のコピー-Page-2 (1).png

Circle CIが非常に優秀で、githubにpushすると、ECRにpushして、ECSにdeployまでしてくれます。具体的には、公式ドキュメントを一読することをお勧めします。

config.yml
version: 2.1
orbs:
  aws-ecr: circleci/aws-ecr@0.0.2
  aws-ecs: circleci/aws-ecs@0.0.3
workflows:
  build-and-deploy:
    jobs:
      - aws-ecr/build_and_push_image:
          account-url: "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com"
          repo: "${AWS_RESOURCE_NAME_PREFIX}"
          region: ${AWS_DEFAULT_REGION}
          tag: "${CIRCLE_SHA1}"
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build_and_push_image
          aws-region: ${AWS_DEFAULT_REGION}
          family: "${AWS_RESOURCE_NAME_PREFIX}-service"
          cluster-name: "${AWS_RESOURCE_NAME_PREFIX}-cluster"
          container-image-name-updates: "container=${AWS_RESOURCE_NAME_PREFIX}-service,tag=${CIRCLE_SHA1}"

上記.circleci/config.ymlをgithubにpushするとcircleCIの設定が適応されます。CircleCIで定めた環境変数は、CircleCIのダッシュボードのEnvironment Variables
から設定します(後述)。

S3へのdeploy

  • IAMでS3へのアップロード用のuserを作り、AmazonS3FullAccess権限を与える
  • aws cliからuploadするscriptsを書く

こちらに関しては、たくさん説明記事があるので、細かい内容は省略します。
参考: Amazon S3でSPAをサクッと公開する

インフラ構築までの流れ

VPCの作成

  • IPv4 CIDR 10.2.0.0/16

subnetの作成

  • IPv4 CIDR 10.2.0.0/20(public-subnet-a)
  • IPv4 CIDR 10.2.16.0/20(public-subnet-c)
  • IPv4 CIDR 10.2.32.0/20(private-subnet-a)
  • IPv4 CIDR 10.2.48.0/20(private-subnet-c)

アベイラビリティゾーンA, Cに2つずつ、public subnetと private subnetを設置します。CIDRの設定に気をつけてください。

Internet Gatewayの生成

  • Internet Gatewayを生成して、先ほど生成したVPCにアタッチする

ルートテーブルの生成

  • ルートテーブルを新規に生成
  • 送信先 0.0.0.0/0 をインターネットゲートウェイ (igw-xxxxxxxx) にルーティング(ルートの変更より追加)
  • public subnetとそのルートテーブルを紐付ける

※ デフォルトでは、subnetを生成するとプライベートルートテーブルが選択されます。したがって、自分でルートテーブルを作って、public subnetとの紐付けを行う必要があります。

NAT Gatewayの作成

  • NAT Gatewayを作成して、public subnetの一方にアタッチする
  • これにより、private subnetへアクセスできるようになる

※VPCとsubnet,Internet GWなどの詳細な設定方法は公式ドキュメントを参照してください。

ECRでレポジトリを作成

  • circleciに権限を付与する
    IAMでcircleciによるdeploy用のuserを作成します。
    今回は、ECR/ECSに関する権限を付与します。

    • AmazonEC2ContainerRegistryFullAccess
    • AWSCodeDeployRoleForECS
    • AmazonEC2ContainerServiceFullAccess
    • AmazonECSTaskExecutionRolePolicy
    • AWSDeepRacerCloudFormationAccessPolicy
  • Environment Variablesの設定

    • AWS_ACCOUNT_ID(ex:754569708956)
    • AWS_DEFAULT_REGION(ex:ap-northeast-1)
    • AWS_RESOURCE_NAME_PREFIX(ex:flask-app)
    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY

AWS_RESOURCE_NAME_PREFIXは、ECRのレポジトリの名前と同じにしないとエラーが出ます

nginxコンテナをアップロード

  • こちらは、頻繁に変更しないと思うので、circleCIには含めず手動で行う
  • 詳細は公式ページを参照のこと
  • confファイルで、nginxはproxy serverとしての設定する
ディレクトリ構成
nginx
├── Dockerfile
└── conf
    └── default.conf
Dockerfile
FROM nginx
COPY conf/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
ENTRYPOINT nginx -g 'daemon off;'
default.conf
server {
    listen       80;
    server_name  localhost;

    location / {
        #root   /usr/share/nginx/html;
        #index  index.html index.htm;
        proxy_pass      http://127.0.0.1:5000;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

ECS Fargateの作成

  • fargateでclusterを作成する VPCは先ほど作った物を使いますので、ここで新しく作成する必要はありません。
  • fargateでtaskを作成する
    タスク実行ロールは、ecsTaskExecutionRoleを指定します。ECRで登録したdocker imageのURLを貼り付けます。コンテナのportを公開するのを忘れないようにしましょう(flask container port:5000)。

  • fargateでserviceを作成する

    • subnetは先ほど作ったprivate subnetを二つ割り当てます
    • fargate service用のsecurity groupを作成する(port:80)
    • EC2でロードバランサーを作成する
    • ELB用のsecurity groupを作成する(port:80)
    • target groupを作成する(port:80, ターゲットの種類:ip)
    • ロードバランス用のコンテナではnginxのcontainerを指定して、ターゲットグループは先ほど作った物を指定

※ flask-app用のコンテナの名前は、AWS_RESOURCE_NAME_PREFIX-serviceと同じ物にしないと、circleCIのdeployの時にエラーになります

RDBとの連携

  • 踏み台サーバーを立てる(EC2)
    • mysql-clientをinstallする
  • RDSでamazon Auroraを選択する
    • aurora DB用のsecurity groupを作成する(port:3306)
    • private subnetをまとめたsubnet groupを作成する
  • 踏み台サーバーからendpointに向けてログインできるか確認する
  • ECSのコンテナの方で環境変数を設定する
    • DB_NAME
    • DB_USER
    • PASSWORD
    • HOST

エラーハンドリング

  1. CannotPullContainerError: Error response from daemon: Get... : net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
    NAT GWがきちんと設定されていなかったら、このエラーが出ます。NATの設定を見直してみましょう。参考

  2. Task failed ELB health checks in (target-group...)
    ELBのhealth checkに失敗すると表示されます。ELBを設定した時に、defaultでは、/がhealth checkのendpointになります。APIサーバーで/のPATHでGETを用意していていなかったら、ここでエラーになるので、ELBの設定の時に、PATHを変更するか、APIサーバーの方で、Health check用のAPIを作るようにします。

まとめ

AWSのサービスをうまく利用することで、再現性が高くスケーラブルなアプリケーションを作成することができました。また、terraformなどを使って、awsの設定自体も出来るだけ、コードに落とせるようにしたらいいなあと思いました。

9
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12