📝

LaravelをDockerで動かしてみた on AWS EC2 with CloudFormation

2023/10/11に公開

超初学者がPHPフレームワークLaravelをAWS上で動かしてみました。備忘録です。

この記事のきっかけ

  • 実務でPHP(Laravel)を使う+新しい言語を学ぶ時はDockerで動かすように意識しているため、とりあえずDockerでLaravelを動かしたい
  • AWS認定試験を受けてはいるものの資格一辺倒で名ばかりになりがちを脱するため、AWS上で動かしたい
    (所有:SAA / 受験予定:SOA)
  • やったことを記事にすると、作業中にとっ散らかった脳内を整理できる

この記事で話すこと

  • 簡単な手順(備忘録)
  • 必要だったこと、学んだことなどの所感

やったこと

下準備(Laravel on Docker編)

Dockerの基礎を学んだ

今までなんとなーく使っていたDockerについてなんとなーくだと通用しなくなってきたので、こちらで基礎を学びました。この知識が最低限だと今では感じていますが、最低限の分量が濃い。
https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59
Dockerで何かしらの環境構築を行う際にDockerfileやdocker-compose.ymlの書き方を調べて先人の知恵を借りて(あるいはChatGPT頼み)なんとか構築しますが、なぜその書き方なのかだったり書いてあること1行1行の意味だったりを理解できるようになりました。理解できるおかげで自分の欲しい環境を(簡単であれば)自力で構築することもできそうです。うれしい。

DockerでLEMP環境(Laravel+nginx+MySQL)構築

Docker内にLaravel(APP)+WEBサーバー+DBサーバーが欲しかったのでこちらを参考にしました。今回使用しているDockerfile、docker-compose.ymlはほぼこの記事通りです。
https://qiita.com/hinako_n/items/f15646ea548bcdc8ac6c
上記zenn本を一通り読んだおかげでさくさく進む。

下準備(AWS編)

せっかくなのでCloudFormationで構築してみた

SOA受験予定なのでCloudFormationを使ってみました。
サーバーレス構成しか触ったことがなくて基本的な構成の構築経験ほぼゼロなのにコンソールをすっ飛ばしていきなりコードで書くのは無茶だったかな…?!と途中で心が折れそうになりましたがコードで書くからこそ浅い理解では通用しなくて勉強になりました。
参考:
https://qiita.com/kanadeee/items/de8c1780e3c37811eb57
ついでにせっかくなのでAMIはAmazonLinux2023を使用。

さらにせっかくなのでSessionManagerでEC2に接続してみた

触ったことないものを触ってみよう精神です。
参考:
https://dev.classmethod.jp/articles/session-manager-ec2-cloudformation/
今回はパブリックサブネット上なのでVPCエンドポイントは不要。
SessionManagerでEC2に接続するためのIAMロールの割り当ては必須。

実際に動かした構成

パブリックサブネット上にEC2を立ててIGW経由でアクセスする超簡略構成です。(VPCエンドポイントを使ってSessionManagerでプライベートサブネット上のEC2に接続してみるため、EC2 on プライベートサブネット + NATゲートウェイ on パブリックサブネットとかも試しはしました)
Parametersを使わずハードコードしているのは目を瞑って…🥲

template
AWSTemplateFormatVersion: "2010-09-09"
Description: "PHP on EC2"

Resources:
# VPC
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: "Name"
          Value: "PHP-test-VPC"
# サブネット
  MyPublicSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.192.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: "Name"
          Value: "PHP-test-PublicSubnet"
      VpcId: !Ref MyVPC

# IGW設定
  MyInternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: "Name"
          Value: "PHP-test-IGW"
  AttachIGW:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref MyInternetGateway
 # ネットワーク設定/Public
  MyPublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC
      Tags:
        - Key: "Name"
          Value: "PHP-test-PublicRouteTable"
  MyPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: MyInternetGateway
    Properties:
      RouteTableId: !Ref MyPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref MyInternetGateway
  PublicSubnetRouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref MyPublicSubnet
      RouteTableId: !Ref MyPublicRouteTable

# EC2定義
  MyEC2forPHP:
    Type: "AWS::EC2::Instance"
    Properties:
      ImageId: "ami-08a706ba5ea257141"
      InstanceType: "t2.micro"
      SubnetId: !Ref MyPublicSubnet
      KeyName: "XXX"
      Tags:
        - Key: "Name"
          Value: "PHP-test-EC2"
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      IamInstanceProfile: !Ref EC2InstanceProfile
  EC2SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "PHP-test-EC2-SecurityGroup"
      VpcId: !Ref MyVPC
      Tags:
        - Key: "Name"
          Value: "PHP-test-SecurityGroup"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: "22"
          ToPort: "22"
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: "8081"
          ToPort: "8081"
          CidrIp: 0.0.0.0/0
  EC2InstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: /
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub PHP-test-EC2InstanceProfile

  # IAM定義
  EC2IAMRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub php-test-env-SSM-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

記事を参考にする際はあらゆる条件・環境をその記事に合わせることがセオリーなのは承知の上でいろいろはみ出してみたら当たり前にいろいろ噛み合わず、ハマる。
基本的なネットワーク設定など文言上は理解していてもいざ構築すると抜け落ちていたり(プライベートサブネット上ではインターネットからのDLができないからNATゲートウェイを使うとか、そういうレベルで)実際に手を動かすことで改めて勉強になりました。

EC2上にDockerとgitをインストール

セッションマネージャーでEC2に接続後、GitHub上のLaravelプロジェクトをEC2にgit cloneしてDockerで起動したいのでDocker Engine+Docker Composeとgitをインストールします。

Docker EngineとDocker Composeをインストール

参考:
https://densan-hoshigumi.com/aws/amzn2023-docker-install
まずはDocker Engineをインストール。

$ sudo dnf install -y docker #Docker Engineをインストール
$ sudo systemctl enable --now docker #システム起動時のDocker自動起動を有効化+起動
$ systemctl status docker #Dockerサービスの状態を確認

できました。

ec2-userでroot権限なしでdockerコマンドを操作できるように。

$ sudo usermod -aG docker ec2-user #Docker Engineインストール時に作成されたdockerグループにec2-userを追加
$ sudo su ec2-user #ec2-userに切り替え
$ docker -v  #dockerコマンドが使えることを確認

続いてDocker Composeをインストール。
上記記事を参考にしつつ、Docker公式も確認して最新バージョンをインストール。
https://docs.docker.com/compose/install/linux/#install-using-the-repository

$ DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker}
$ sudo mkdir -p $DOCKER_CONFIG/cli-plugins
$ sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.3/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose #ここはDocker公式ドキュメント記載の最新バージョンに置き換える⚠️
$ sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose #docker-composeの実行権限を適用

gitをインストール

$ sudo dnf install -y git

git configでgitの設定+SSHキーの作成でgitのセットアップは完了。

Laravelプロジェクトをインストール+Dockerで動かす on EC2

ec2-userでログインしてLaravelプロジェクトをgit clone + docker compose upで起動。
下準備(Laravel on Docker編)で作成したものをそのままEC2上で動かす感じです。だんだん見えてきてうれしい。

$ git clone git@github.com:XXXXX/XXXXXX.git #git clone
$ cd XXXXX #プロジェクトのルートディレクトリに移動
$ docker compose up 

今回はパブリックサブネット上のEC2で簡単に動かしているので、http://{パブリック IPv4 アドレス}:8081にアクセスして確認!

ハマりポイント

  • Laravel関連エラー
    composer create-projectでプロジェクト作成すると.gitignore記載のgit管理されないディレクトリとファイルがいくつかあるのでEC2上にgit cloneした時にセットする必要がある、など。初歩的ですが実際に動かしてみると初めての時は見落としがち。
  • EC2無料枠t2.microでCPU使用率が100%近くに
    dockerコンテナ内で(Laravel関連エラー対処のため)compose updateしようとすると何もできなくなったのでコンソールのモニタリングを確認したところCPU使用量が一気に増してクレジットも使い切っていました。
    コンテナ内ではなくEC2上にphpとcomposerをインストールして操作したら大丈夫でした。

まとめ

Laravel on DockerをAWSで動かすベストプラクティスではないだろうということは百も承知で(何よりネットワーク設定がガバガバ)、学習のためにいろいろ触ってみました。

やったこと(まとめ)

  • DockerでLEMP環境構築
  • CloudFormationでEC2立ち上げ+SessionManager使用
  • EC2にLaravelデプロイ+Docker起動

所感

  • Docker学び直してよかった
    もうDocker怖くない(多分)
  • 座学で知っていても実際に触ると何も分からない
    CloudFormationのテンプレートを書いていて、何も分かってないなあと痛感しました。とはいえ初学者こそインフラをコードで書いてみると得られるものが大きいとも感じました。またやりたい、やる。
  • 認定試験の勉強で知っていること、実際に手を動かして分かることで学びの相乗効果
    試験勉強で知ったことを構築中に見ると「あ、あれ!」となるし、逆に手を動かして知っていることを試験問題で見た時も「あ、あれ!」となります。このバランスがかなり楽しい。

今は新しい言語やフレームワークを学ぶ時は文法や書き方だけではなく動作の仕組みやインフラ面を理解したいと考えています。まず動く、その上でコードをいじる方が効率がいいと感じているからです。何かあった時に動かない理由を下からつぶしていく、みたいな。
今回はLaravelの挙動についてはあまり触れていませんが、まず「動かす」ことができたので次はLaravelそのものの学習を進めていこうと思います。
Docker学び直し+AWS上で動かしてみるということをやってみていろいろと解像度が上がりました。この一連の流れで同時に学べることが多くあったのもよかったです。

本当はもっとこうすべき、とかこうした方がいい、とかこういうこともあるよーみたいなことがあれば教えてもらえるとうれしいです。

GitHubで編集を提案

Discussion