LoginSignup
5
1

More than 1 year has passed since last update.

【AWS】LaravelアプリをEC2デプロイ④【CircleCI / CodeDeploy編】

Last updated at Posted at 2022-06-03

0. はじめに

大阪のLaravel初学者サウナーこと、kazumakishimoto(@kazuma_dev)です!
CircleCI / CodeDeployの自動デプロイ方法です。

0-1. 前回記事

  • 【AWS】LaravelアプリをEC2デプロイ【まとめ編】

  • 【AWS】LaravelアプリをEC2デプロイ①【CloudFormation / EC2 / RDS編】

  • 【AWS】LaravelアプリをEC2デプロイ②【Route53編】

  • 【AWS】LaravelアプリをEC2デプロイ③【ACM / ELB編】

0-2. 全体の流れ

1.CircleCI
2.CodeDeploy
補足
Reference
次回記事

0-3. 本記事の対象者

  • CircleCI / CodeDeployで自動デプロイしたい方

0-4. 事前準備

  • AWSアカウント作成済み
  • CircleCIアカウント作成済み
  • リージョンはアジアパシフィック(東京)ap-northeast-1
  • grflhogeはサンプル名なので適宜変更して下さい

0-5. 要件

  • CircleCIで自動デプロイ
  • CodeDeployで自動デプロイ

0-6. 本番環境

ツール バージョン
OS Amazon Linux 2
nginx 1.12
PHP 7.4.28
Laravel 6.20.44
MySQL 5.7.37
Composer 1.10.26
Node.js 13.14.0

0-7. AWS構成図

aws

1. CircleCI

1-1. 秘密鍵と公開鍵の作成

ec2-user
$ ssh ec2-user@xx.xxx.xxx.xxx -i ~/.ssh/hoge.pem
$ ssh-keygen -m pem -C ""
Enter×2

1-2. 公開鍵をGitHubへ登録

image.png
image.png

  • id_rsa.pubの内容を貼り付け
ec2-user
$ cat ~/.ssh/id_rsa.pub

image.png

ec2-user
$ ssh git@github.com
PTY allocation request failed on channel 0
Hi あなたのGitHubアカウント名/grfl! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.

1-3. 秘密鍵をCircleCIへ登録

image.png
image.png

  • id_rsaの内容を貼り付け
ec2-user
$ cat ~/.ssh/id_rsa

image.png
image.png

1-4. 公開鍵認証

ec2-user
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys

1-5. 環境変数

  • HOST_NAMEにEC2のパブリックIP(EIP)追加
    image.png
    image.png
    image.png

1-6. config.yml

grfl/.circleci/config.yml
version: 2.1

executors:
  laravel-circleci:
    docker:
      - image: circleci/php:7.4-node-browsers
      - image: circleci/mysql:5.7
    environment:
      - APP_DEBUG: true
      - APP_ENV: testing
      - APP_KEY: base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
      - DB_CONNECTION: circle_testing
      - MYSQL_ALLOW_EMPTY_PASSWORD: true
    working_directory: ~/repo

commands:
  install-dockerize:
    steps:
      - run:
          name: Install dockerize
          command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
          environment:
            DOCKERIZE_VERSION: v0.6.1
  install-php-extensions:
    steps:
      - run:
          name: Install PHP Exetensions
          command: sudo docker-php-ext-install pdo_mysql
          working_directory: src
  restore-cache-composer:
    steps:
      - restore_cache:
          key: v1-dependencies-{{ checksum "src/composer.json" }}
  install-composer:
    steps:
      - run:
          name: Install Composer
          command: composer install -n --prefer-dist
          working_directory: src
  save-cache-composer:
    steps:
      - save_cache:
          key: v1-dependencies-{{ checksum "src/composer.json" }}
          paths:
            - vendor
  npm-ci:
    steps:
      - run:
          name: npm CI
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
          working_directory: src
  restore-cache-npm:
    steps:
      - restore_cache:
          key: npm-cache-{{ checksum "src/package-lock.json" }}
  npm-run-dev:
    steps:
      - run:
          name: Run npm
          command: npm run dev
          working_directory: src
  save-cache-npm:
    steps:
      - save_cache:
          key: npm-cache-{{ checksum "src/package-lock.json" }}
          paths:
            - node_modules
  migration-seeding:
    steps:
      - run:
          name: Migration & Seeding
          command: php artisan migrate --seed
          working_directory: src
  test-unittest:
    steps:
      - run:
          name: Run PHPUnit
          command: vendor/bin/phpunit
          working_directory: src

jobs:
  build:
    executor:
      name: laravel-circleci
    steps:
      - checkout
      - install-dockerize
      - install-php-extensions
      - restore-cache-composer
      - install-composer
      - save-cache-composer
      - restore-cache-npm
      - npm-ci
      - save-cache-npm
      - npm-run-dev
      - migration-seeding
      - test-unittest
  deploy:
    docker:
      - image: circleci/php:7.4-node-browsers
    steps:
      - checkout
+     - add_ssh_keys
      - run:
+         name: aws deploy
          command: |
+           ssh -o StrictHostKeyChecking=no -t ec2-user@${HOST_NAME} "cd grfl && \
+             git pull origin master && \
+             cd /var/www/grfl/src && \
+             composer install -n --no-dev --prefer-dist && \
+             npm ci && \
+             npm run prod && \
+             php artisan migrate --force && \
+             php artisan config:cache"

workflows:
  version: 2
  build_deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build
          filters:
            branches:
              only:
                - master

1-7. 自動デプロイ(CircleCI)

  • ローカルのmasterブランチに、リモートのmasterブランチの内容を反映しておく
  • commit & push → deploy!
  • 下記のようなエラーログがCircleCIで発生する場合、権限や所有権を確認してください

1-7-1. エラーログ

Warning: Permanently added '**************' (ECDSA) to the list of known hosts.
error: cannot open .git/FETCH_HEAD: Permission denied
error: unable to unlink old '.circleci/config.yml': Permission denied
npm WARN prepare removing existing node_modules/ before installation
[..................] / : WARN prepare removing existing node_modules/ before innpm ERR! code EACCES
npm ERR! syscall rmdir
npm ERR! path /var/www/****/src/node_modules/@ampproject
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, rmdir '/var/www/****/src/node_modules/@ampproject'
npm ERR!  [OperationalError: EACCES: permission denied, rmdir '/var/www/****/src/node_modules/@ampproject'] {
npm ERR!   cause: [Error: EACCES: permission denied, rmdir '/var/www/****/src/node_modules/@ampproject'] {
npm ERR!     errno: -13,
npm ERR!     code: 'EACCES',
npm ERR!     syscall: 'rmdir',
npm ERR!     path: '/var/www/****/src/node_modules/@ampproject'
npm ERR!   },
npm ERR!   isOperational: true,
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'rmdir',
npm ERR!   path: '/var/www/****/src/node_modules/@ampproject'
npm ERR! }
npm ERR! 
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR! 
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/****/.npm/_logs/2022-05-31T09_51_53_058Z-debug.log

1-7-2. 権限変更

ec2-user
$ cd /var/www/grfl
$ sudo chmod 777 .git
$ sudo chown -R ec2-user:ec2-user .git/
$ sudo chown -R ec2-user:ec2-user .circleci
$ cd /var/www/grfl/src
$ sudo chown -R ec2-user:ec2-user node_modules

2. CodeDeploy

2-1. S3バケット作成

image.png
image.png

2-2. IAMロール作成(CodeDeploy用)

image.png
image.png
image.png
image.png

2-3. CodeDeploy作成

image.png
image.png
image.png
image.png

2-4. IAMロール作成(EC2用)

image.png
image.png
image.png
image.png
image.png
image.png
image.png

json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::grfl-codedeploy-ap-northeast-1-xxxxxxxxxxxx",
                "arn:aws:s3:::grfl-codedeploy-ap-northeast-1-xxxxxxxxxxxx/*"
            ]
        }
    ]
}

image.png
image.png
image.png

2-5. CodeDeployAgentインストール

ec2-user
$ ssh ec2-user@xx.xxx.xxx.xxx -i ~/.ssh/hoge.pem
$ sudo yum install ruby -y
$ wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install
$ chmod +x ./install
$ sudo ./install auto
$ sudo service codedeploy-agent status
The AWS CodeDeploy agent is running as PID xxxxx

$ sudo service codedeploy-agent restart
10〜20秒待機
[ec2-user@ip-xxx-xxx-xxx-xxx ~]$

2-6. IAMユーザー作成&環境変数(CircleCI用)

2-6-1. IAMユーザー作成

image.png
image.png
image.png
image.png
image.png

2-6-2. 環境変数

image.png
image.png
image.png
image.png
image.png

2-6-3. ポリシー作成

image.png
image.png
image.png

JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::grfl-codedeploy-ap-northeast-1-xxxxxxxxxxxx/*"
        }
    ]
}

image.png

2-7. appspec.yml

grfl/appspec.yml
version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/grfl/src

permissions:
  - object: /var/www/grfl/src
    owner: grfl
    group: grfl

hooks:
  AfterInstall:
    - location: ./scripts/after_install.sh
      timeout: 60
      runas: grfl

2-8. after_install.sh

grfl/scripts/after_install.sh
#!/bin/bash

set -eux

cd /var/www/grfl/src
php artisan migrate --force
php artisan config:cache
ec2-user
$ cd /var/www/grfl/src
$ chmod +x scripts/after_install.sh
$ ls -l scripts
-rwxr-xr-x  1 xxxx  xxxx  67 May  4 18:22 after_install.sh

2-9. config.yml

.circleci/config.yml
version: 2.1

executors:
  laravel-circleci:
    docker:
      - image: circleci/php:7.4-node-browsers
      - image: circleci/mysql:5.7
    environment:
      - APP_DEBUG: true
      - APP_ENV: testing
      - APP_KEY: base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
      - DB_CONNECTION: circle_testing
      - MYSQL_ALLOW_EMPTY_PASSWORD: true
    working_directory: ~/repo

commands:
  install-dockerize:
    steps:
      - run:
          name: Install dockerize
          command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
          environment:
            DOCKERIZE_VERSION: v0.6.1
  install-php-extensions:
    steps:
      - run:
          name: Install PHP Exetensions
          command: sudo docker-php-ext-install pdo_mysql
          working_directory: src
  restore-cache-composer:
    steps:
      - restore_cache:
          key: v1-dependencies-{{ checksum "src/composer.json" }}
  install-composer:
    steps:
      - run:
          name: Install Composer
          command: composer install -n --prefer-dist
          working_directory: src
  save-cache-composer:
    steps:
      - save_cache:
          key: v1-dependencies-{{ checksum "src/composer.json" }}
          paths:
            - vendor
  npm-ci:
    steps:
      - run:
          name: npm CI
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
          working_directory: src
  restore-cache-npm:
    steps:
      - restore_cache:
          key: npm-cache-{{ checksum "src/package-lock.json" }}
  npm-run-dev:
    steps:
      - run:
          name: Run npm
          command: npm run dev
          working_directory: src
  save-cache-npm:
    steps:
      - save_cache:
          key: npm-cache-{{ checksum "src/package-lock.json" }}
          paths:
            - node_modules
  migration-seeding:
    steps:
      - run:
          name: Migration & Seeding
          command: php artisan migrate --seed
          working_directory: src
  test-unittest:
    steps:
      - run:
          name: Run PHPUnit
          command: vendor/bin/phpunit
          working_directory: src

jobs:
  build:
    executor:
      name: laravel-circleci
    steps:
      - checkout
      - install-dockerize
      - install-php-extensions
      - restore-cache-composer
      - install-composer
      - save-cache-composer
      - restore-cache-npm
      - npm-ci
      - save-cache-npm
      - npm-run-dev
      - migration-seeding
      - test-unittest

  deploy:
    docker:
      - image: circleci/php:7.4-node-browsers
    environment:
      AWS_DEFAULT_REGION: ap-northeast-1
    steps:
      - checkout
      - run: sudo composer self-update --1
      - restore_cache:
          key: composer-no-dev-v1-{{ checksum "src/composer.lock" }}
      - run:
          command: composer install -n --no-dev --prefer-dist
          working_directory: src
      - save_cache:
          key: composer-no-dev-v1-{{ checksum "src/composer.lock" }}
          paths:
            - vendor
      - restore_cache:
          key: npm-v1-{{ checksum "src/package-lock.json" }}
      - run:
          name: npm ci
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
          working_directory: src
      - save_cache:
          key: npm-v1-{{ checksum "src/package-lock.json" }}
          paths:
            - node_modules
      - run:
          command: npm run prod
          working_directory: src
      - run:
          name: archive artifacts
          command: zip -ryq grfl.zip .
          working_directory: src
      - aws-cli/install
      - run:
          name: upload artifacts to s3
          command: aws s3 cp grfl.zip s3://${AWS_S3_BUCKET_NAME}
          working_directory: src
      - run:
          name: deploy to prod
          command: |
            aws deploy create-deployment \
            --application-name grfl \
            --deployment-group-name grfl \
            --s3-location bucket=${AWS_S3_BUCKET_NAME},key=grfl.zip,bundleType=zip
          working_directory: src

orbs:
  aws-cli: circleci/aws-cli@1.0.0

workflows:
  version: 2
  build_deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build
          filters:
            branches:
              only:
                - master

2-10. EC2デプロイ先ディレクトリ退避

  • 既にCodeDeploy以外の方法でデプロイされているディレクトリにCodeDeployでデプロイすると失敗するケースがあるため
ec2-user
$ ssh ec2-user@xx.xxx.xxx.xxx -i ~/.ssh/hoge.pem
$ cd /var/www
$ mv grfl grfl-ssh-deploy
$ mkdir grfl
$ cd /var/www/grfl
$ mkdir src
$ cd /var/www
$ cp grfl-ssh-deploy/src/.env grfl/src/.env
$ ls -l grfl/src
-rwxrwxr-x 1 ec2-user ec2-user 1913 Jun  1 08:33 .env

2-11. 自動デプロイ(CodeDeploy)

  • ローカル開発ブランチでcommit & push
  • GitHubでPR作成
  • CircleCIのbuildジョブが完了したら、PRをマージ

2-11-1. エラーログ

image.png
image.png
image.png

2-11-12. ヘルスチェック確認

image.png
image.png
image.png

2-11-3. S3バケット確認

image.png
image.png

2-12. 動作確認

ec2-user
$ cd /var/www/grfl/src
$ sudo chmod -R 777 storage
$ sudo chmod -R 777 bootstrap/cache

image.png

補足

開発環境(FW/ツールのバージョンなど)

ツール バージョン
Vue.js 2.6.14
jQuery 3.4.1
PHP 7.4.1
Laravel 6.20.43
MySQL 5.7.36
Nginx 1.18.0
Composer 2.0.14
npm 6.14.6
Git 2.33.1
Docker 20.10.11
docker-compose v2.2.1
PHPUnit 8.0
CircleCI 2.1
heroku 7.59.4
MacBook Air M1,2020
macOS Monterey 12.3
Homebrew 3.3.8

ディレクトリ構造

【ルートディレクトリ】
├─ .circleci
│   └─ config.yml
├─ aws / CloudFormation
│   └─ ec2.yml
├─ docker
│   └─ mysql
│   └─ nginx
│   └─ php
│   └─ phpmyadmin
├─ src
│   └─ 【Laravelのパッケージ】
└─ docker-compose.yml

Reference

次回記事

  • 【AWS】LaravelアプリをEC2デプロイ⑤【SNS / Chatbot編】

  • 【AWS】LaravelアプリをEC2デプロイ⑥【S3編】

  • 【AWS】LaravelアプリをEC2デプロイ⑦【API編】

  • 【AWS】お役立ちリンク集【随時更新】

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