4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actionsを活用したDjangoアプリ開発:CloudFormationテンプレートでECRからECSへの自動本番デプロイ

Last updated at Posted at 2024-12-08

はじめに

この記事では、GitHub Actionsを活用したDjangoアプリ開発において、CloudFormationを用いてインフラをコード化し、ECSがECRからDockerイメージを自動的にプルして本番環境にデプロイする手順を紹介します。

この記事は、過去の技術検証内容を組み合わせた実践的な応用例です。

今回の内容は少し難易度が高いかもしれませんが、自分の備忘録も兼ねて、手順や実施内容を詳しくまとめていきます。

補足:過去記事の組み合わせによる検証

本記事では、以下の記事で取り上げた技術検証をマージして進めています。

CloudFormationでインフラをコード化し、ECSからECRのイメージを自動プルするデプロイ手順

DjangoプロジェクトとDocker関連ファイルを一括作成するシェルスクリプトで環境構築を自動化

GitHub Actionsを使用したECRへの自動プッシュ設定(CI/CD)

やりたいこと

今回のゴールを言葉で説明するのは少し難しいため、以下のイラストで概要を示しています。

スクリーンショット 2024-12-08 11.47.41.png

CloudFormationテンプレートを使用して、ECSクラスタやタスク定義を自動で作成し、GitHub Actionsを利用してDjangoアプリのDockerイメージをECRに自動プッシュしています。

最終的に、ECSがECRからイメージを取得し、本番環境へデプロイする流れとなります(以下、CloudFormationテンプレートは一部簡略化していますので、ご了承ください)。

CloudFormationテンプレートの準備

以下は、今回使用したCloudFormationテンプレートです。このテンプレートには、ECSクラスター、タスク定義、IAMロールなどの必要なリソース設定が含まれています。

test.yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  # VPCの作成
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: "Name"
          Value: "honda-vpc"

  # サブネットの作成
  PublicSubnet1:
    Type: "AWS::EC2::Subnet"
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.0.0/20"
      AvailabilityZone: "ap-northeast-1a"
      Tags:
        - Key: "Name"
          Value: "honda-subnet-public1-ap-northeast-1a"

  # インターネットゲートウェイの設定
  InternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: "Name"
          Value: "honda-igw"

  AttachInternetGateway:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # ルートテーブルの設定
  PublicRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: "honda-rtb-public"

  PublicRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

  SubnetRouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  # ECS用セキュリティグループ
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "Allow HTTP traffic"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"
        - IpProtocol: "tcp"
          FromPort: 8000
          ToPort: 8000
          CidrIp: "0.0.0.0/0"  # 追加: ポート8000も許可

  # ECSクラスターの作成
  ECSCluster:
    Type: "AWS::ECS::Cluster"
    Properties:
      ClusterName: "my-ecs-cluster"

  # ECSタスク定義
  ECSTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      Family: "my-app-task"
      RequiresCompatibilities:
        - FARGATE
      NetworkMode: "awsvpc"
      Cpu: "256"
      Memory: "512"
      ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
      ContainerDefinitions:
        - Name: "my-docker-repo"
          Image: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-docker-repo:latest"
          PortMappings:
            - ContainerPort: 8000  # 変更: コンテナのポートを8000に
              HostPort: 8000       # 変更: ホストのポートも8000に
              Protocol: "tcp"

  # ECSタスク実行ロール
  ECSTaskExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ecs-tasks.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "ecsTaskExecutionPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "ecr:GetAuthorizationToken"
                  - "ecr:GetDownloadUrlForLayer"
                  - "ecr:BatchGetImage"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "s3:GetObject"
                Resource: "*"

  # ECSサービスの作成
  ECSService:
    Type: "AWS::ECS::Service"
    Properties:
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref ECSTaskDefinition
      DesiredCount: 1
      LaunchType: "FARGATE"
      NetworkConfiguration:
        AwsvpcConfiguration:
          Subnets:
            - !Ref PublicSubnet1
          SecurityGroups:
            - !Ref SecurityGroup
          AssignPublicIp: "ENABLED"

※今回は、過去の記事で作成したテンプレートを基に、コンテナとホストのポート設定、およびセキュリティグループ周りの設定を修正しました。

補足事項:Django アプリケーションの ALLOWED_HOSTS 設定について

本番環境へのデプロイに際し、Djangoプロジェクトの準備や設定は以下の記事で実施済みであることを想定しています。

しかし、本番環境でのデプロイ時に設定ファイルの修正が必要となった箇所がありましたので、その内容を紹介します。

発生したエラー

本番環境にCloudFormationテンプレートを使ってデプロイした際、以下のエラーが発生しました。

エラー.
DisallowedHost at /
Invalid HTTP_HOST header: '43.207.164.191:8000'. You may need to add '43.207.164.191' to ALLOWED_HOSTS.

このエラーは、DjangoのALLOWED_HOSTS設定が適切に設定されていないことを示しています。

具体的には、HTTPリクエストのHostヘッダーに含まれる値(例: IPアドレス:8000)が、Djangoの許可リストに登録されていないため、DisallowedHostエラーが発生しました。

エラーの修正方法

開発環境などで、IPアドレスが変動する場合、一時的な対応としてすべてのホストを許可する方法があります。以下のように、settings.pyのALLOWED_HOSTSを修正します。

/my_django_project/myproject/settings.py
# settings.py
ALLOWED_HOSTS = ['*']

この設定により、すべてのホストからのリクエストが許可されるようになります。

ただし、本番環境ではセキュリティリスクを回避するため、具体的なホスト名またはIPアドレスのみを設定することを強く推奨します。

本番環境では、以下のように明確にホストを指定するようにしてください。

/my_django_project/myproject/settings.py
# settings.py
ALLOWED_HOSTS = ['43.207.164.191', 'example.com']

実際にやってみた

上記のCloudFormationテンプレートを使って、ECSクラスターを作成し、タスク定義とサービスを設定しました。

CloudFormationのサービス画面からスタックの作成に進み、作成したyamlファイルをアップロードします。

スクリーンショット 2024-12-08 12.12.25.png

スタック名は任意で設定できます。ここでは私の名前「honda」としています。

スクリーンショット 2024-12-08 12.12.47.png

画面遷移後のスタックオプション設定については、個々の設定に依存するため、私は設定せずに進めていきます。

ただ、スタックの中でIAMを使う場合は、以下のように確認が求められますでの、✅を入れて進んでください。

スクリーンショット 2024-11-13 20.19.18.png

最後に確認を行い、問題がなければ最下部までスクロールし、「送信」をクリックして完了です。

スクリーンショット 2024-12-08 12.13.30.png

スタックを実行後、イベントタブで成功したことが確認できました(しばらく時間がかかります)。

スクリーンショット 2024-12-08 12.15.29.png

デプロイ後の動作確認

デプロイが完了したら、タスクが正常に実行されていることを確認します。ここでは、ECRのイメージコンテナからプルされていることも合わせて確認しました。

スクリーンショット 2024-12-08 12.16.37.png

ECSページの「クラスター」→「タスク」タブから、実行中のタスクを選択し、タスクのパブリックIPを確認します。

image.png

ここで、タスクが使用しているパブリックIP(例:54.250.2.18)を確認したら、ブラウザでアクセスしてみます。

http://54.250.2.18:8000/

Djangoの初期ページ("The install worked successfully!")が表示され、アプリケーションが正しく動作していることを確認できました!

スクリーンショット 2024-12-08 12.18.15.png

技術検証自体はスムーズに行えましたが、複雑なアウトプットを1つの記事として整理するのは非常に力を使いました!笑

まとめ

この記事では、CloudFormation、GitHub Actions、ECR、ECSを組み合わせて、Djangoアプリケーションのインフラ構築と自動デプロイの手順を解説しました。

これらの仕組みを取り入れることで、本番環境へのデプロイが大幅に簡略化されます。

今後も技術検証を続けていきますので、この記事がどなたかの技術的な支えとなれば幸いです。

おまけ:私はLambda関数がめちゃくちゃ好き

私はLambda関数が大好きで、自宅でもいろいろな検証を行っています。興味のある方は、ぜひ読んで技術検証に挑戦してみてください!

4
5
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
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?