GitHub Actions × Laravel × Deployerで自動デプロイ

Github ActionsでLaravel製アプリケーションの自動デプロイを設定してみました。


# 前提 & やりたいこと

- 前提
 - サーバーはAWSを使用
 - GitHubにssh接続できる
 - AWS CLIのAccess key IDとSecret access keyが発行済み
 - AWS CLIのAccess key IDとSecret access key、セキュリティグループのidがactionsを設定するリポジトリのSettings -> Secretsに設定されている
 - Deployerで使うsshキーがactionsを設定するリポジトリのSettings -> Secretsに設定されている


- やりたいこと
 - 対象ブランチにプッシュされたら処理が動くようにしたい
 - PHP製デプロイツールのDeployerを使いたい
 - public IPの定期的な変更に対応したい

# 完成形
```yaml:/.github/workflows/deploy.yml
name: deploy stg

on:
  push:
    branches: [対象ブランチ名]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: cache vendor
        id: cache
        uses: actions/cache@v1
        with:
          ref: 対象ブランチ名
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-

      - name: composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist

      - name: deploy dev
        env:
          ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
          SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_ID }}
          SECURITY_GROUP: ${{ secrets.AWS_SECURITY_GROUP_ID }}
        run: |
          IP_ADDRESS=`curl -s https://api.ipify.org/`

          # AWS CLIインストール
          curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
          unzip awscliv2.zip
          sudo ./aws/install
          aws --version

          # AWS CLI設定
          printf "${ACCESS_KEY}\n${SECRET_ACCESS_KEY}\nap-northeast-1\njson\n" | aws configure --profile profileに設定したい名前
          aws configure get aws_access_key_id --profile profileの名前

          # セキュリティグループにルールを追加
          aws --profile profileの名前 ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP} --protocol tcp --port 22 --cidr "$IP_ADDRESS"/32

          # sshキーをコピー
          mkdir -p /home/runner/.ssh
          touch /home/runner/.ssh/Deployerで設定しているidentitiy file名
          echo "${{ secrets.SSH_DEPLOY_KEY }}" > /home/runner/.ssh/Deployerで設定しているidentitiy file名
          chmod 600 /home/runner/.ssh/Deployerで設定しているidentitiy file名

          # known_hostsに追加
          ssh-keyscan サーバーのIP >> ~/.ssh/known_hosts

          # deployerインストール
          curl -LO https://deployer.org/deployer.phar
          mv deployer.phar ./deployer/dep
          chmod +x ./deployer/dep

          # デプロイ
          cd ./deployer && ./dep deploy Deployerで設定しているステージ名 -vvv

          # セキュリティグループからルールを削除
          aws --profile profileの名前 ec2 revoke-security-group-ingress --group-id ${SECURITY_GROUP} --protocol tcp --port 22 --cidr "$IP_ADDRESS"/32
```

# トリガー
対象ブランチへのプッシュをトリガーにする

```yaml
on:
  push:
    branches: [対象ブランチ名]
```

# 対象ブランチにチェックアウト & vendor以下をキャッシュ
Composerをインストールするのは時間がかかるので、ここではキャッシュを使います

- [actions/cache](https://github.com/actions/cache)

```yml
# path: キャッシュしたいディレクトリorファイルへの絶対パス
# key: キャッシュする際のキー
# restore-keys: keyで指定したキーでキャッシュされたものが見つからなかった時に使われるキー
- uses: actions/checkout@v2
      - name: cache vendor
        id: cache
        uses: actions/cache@v1
        with:
          ref: 対象ブランチ名
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
```
キャッシュされたものがなければcomposer installします

```yml
      - name: composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist
```

# AWS CLI インストール & 設定 & セキュリティグループのルールにIPを追加
GitHub ActionsのIPからのssh接続を許可するために、セキュリティグループのルールを編集する必要があります。そのためにAWS CLIをインストールします。

- [インストール](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html)
- [設定](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config)

設定時に必要なAccess key IDとSecret access key、後ほどセキュリティグループのルールを編集する際に必要になるgroup-idは対象リポジトリのSettings -> Secretsから取ってきます

```yml
        env:
          ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
          SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_ID }}
          SECURITY_GROUP: ${{ secrets.AWS_SECURITY_GROUP_ID }}
```

公式ドキュメントに沿ってインストール, 設定, ルールの追加を行います

- [インストール](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html)
- [設定](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config)
- [セキュリティグループのルールに許可IPを追加](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/authorize-security-group-ingress.html)

```yml
          # public IP 取得
          IP_ADDRESS=`curl -s https://api.ipify.org/`

          # AWS CLIインストール
          curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
          unzip awscliv2.zip
          sudo ./aws/install
          aws --version

          # AWS CLI設定
          printf "${ACCESS_KEY}\n${SECRET_ACCESS_KEY}\nap-northeast-1\njson\n" | aws configure --profile profileに設定したい名前
          aws configure get aws_access_key_id --profile profileの名前

          # セキュリティグループにルールを追加
          aws --profile profileの名前 ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP} --protocol tcp --port 22 --cidr "$IP_ADDRESS"/32
```

# ssh設定

servers.ymlについては後述します。

```yml
          # sshキーをコピー
          mkdir -p /home/runner/.ssh
          touch /home/runner/.ssh/servers.ymlで設定しているidentitiy file名
          echo "${{ secrets.SSH_DEPLOY_KEY }}" > /home/runner/.ssh/servers.ymlで設定しているidentitiy file名
          chmod 600 /home/runner/.ssh/Dservers.ymlで設定しているidentitiy file名

          # known_hostsに追加
          ssh-keyscan サーバーのIP >> ~/.ssh/known_hosts
```

# Deployerインストール & デプロイ
### Deployerの設定

- [Deployer](https://deployer.org/)
- [Deployer 公式ドキュメント](https://deployer.org/docs/getting-started.html)

hostの設定はservers.ymlというファイルに分けており、actions用に以下のように記述しています。
sshの設定で "servers.ymlで設定しているidentitiy file名" と書いてある部分は、以下のidentityFileに設定したファイル名です。

```yml:servers.yml
development:
  hostname: サーバーのIP
  stage: 任意のステージ名
  user: ユーザー名
  port: 22
  identityFile: /home/runner/.ssh/file_name
  deploy_path: デプロイパス
  branch: 対象ブランチ
  repository: git@github.com:xxx/xxx.git

```

deploy.phpでは

```php:deploy.php
inventory('./config/servers.yml');
```
のように呼び出しています。
actionsを使用する時のdeploy.phpの注意点ですが、git_ttyをfalseに設定しておいてください。

```php
set('git_tty', false);
```

ただ、デフォルトでfalseになっているようなので、とくに何もなければ明示的に記述しなくても大丈夫そうです。

### Deployerインストール & デプロイ

```yml
          # deployerインストール
          curl -LO https://deployer.org/deployer.phar
          mv deployer.phar ./deployer/dep
          chmod +x ./deployer/dep

          # デプロイ
          cd ./deployer && ./dep deploy servers.ymlで設定しているステージ名 -vvv

```

# セキュリティグループのルールに追加したIPを削除
デプロイのためにssh接続を許可したIPをセキュリティグループから削除します

- [セキュリティグループのルールから許可IPを削除](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/revoke-security-group-ingress.html)

```yml
          # セキュリティグループからルールを削除
          aws --profile profileの名前 ec2 revoke-security-group-ingress --group-id ${SECURITY_GROUP} --protocol tcp --port 22 --cidr "$IP_ADDRESS"/32
```

これで終わりです

# 参考

- [GitHubでssh接続する手順~公開鍵・秘密鍵の生成から~](https://qiita.com/shizuma/items/2b2f873a0034839e47ce)
- [SSHのknown_hostsをスマートに更新する](https://qiita.com/kawaz/items/20983ec286088a1ae5c7)
- [GithubのActionsでLaravelのデプロイ](https://qiita.com/FattyRabbit/items/17a91f249ea088222db5)
- [GitHub ActionsでEC2へのデプロイ自動化(CI/CD)](https://qiita.com/gdtypk/items/aea503db22dfc79f4e9e)
- [cache アクション](https://nju33.com/github-actions/cache%20%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)
- [Github Actions でブランチの操作を行う](https://www.ritolab.com/entry/206)
- [actions/cache](https://github.com/actions/cache)
- [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html)
- [Installing, updating, and uninstalling the AWS CLI version 2 on Linux](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html)
- [Named profiles
](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html)
- [authorize-security-group-ingress](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/authorize-security-group-ingress.html)
- [revoke-security-group-ingress](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/revoke-security-group-ingress.html)