GitHub ActionsでLaravelのアプリケーションをロリポップ！マネージドクラウドにデプロイしてみる

## 概説
個人開発しているLaravelのアプリケーションの稼働環境として「[ロリポップ！マネージドクラウド](https://mc.lolipop.jp/)」を選択しました。
以前、GitHub Actionsを使って、静的HTML一式をロリポップ！マネージドクラウドにデプロイする方法を[ブログにまとめている](https://tech.innovator.jp.net/entry/2020/02/03/124225)のですが、今回はLaravelのアプリケーションをデプロイしてDBのマイグレーション実行までやってみたので、メモとして残しておきます。

## 前提
今回はGitで管理しているsrcディレクトリをロリポップ！マネージドクラウドの/var/wwwディレクトリ配下へrsyncを使ってデプロイします。

- デプロイ元となるGitリポジトリのディレクトリ構成

```
.
├── .git
│   ├── COMMIT_EDITMSG
│   ・
│   ・
├── .github
│   └── workflows
│       └── deploy.yml ← GitHub Actionsの設定ファイル
├── .gitignore
├── README.md
└── src ← デプロイ元ディレクトリ（Laravelのアプリケーションディレクトリ）
    ├── app
    │   ├── Console
    │   │   └── Kernel.php
    │   ├── Exceptions
    │   │   └── Handler.php
    │   ├── Http
    │   │   ├── Controllers
    ・
    ・
```

- デプロイ先となるロリポップ！マネージドクラウドのディレクトリ構成

```
/var/www/
├── html -> /var/www/src/public ← ロリポップ！マネージドクラウド環境のDocumentRoot
└── src ← デプロイ先ディレクトリ
```

注意点として、ロリポップ！マネージドクラウドでは「/var/www/html」が固定のDocumentRootとなっているため、「/var/www/src/public」のシンボリックリンクを作成する必要があります。
初回デプロイの前は「/var/www/src/public」が存在しないため、デプロイが成功したあとに以下の手順でシンボリックリンクを作成します。
デフォルトで準備されている「htmlディレクトリ」を残す必要がない場合はmvではなく `$ rm -fr /var/www/html` でよいでしょう。

```
$ mv /var/www/html /var/www/html_orig
$ ln -s /var/www/src/public /var/www/html
```

## 設定手順
### GitHubの「Secrets」の設定
- マネージドクラウドのダッシュボードで「SSH / SFTP」の内容を確認します。
![https___qiita-image-store_s3_ap-northeast-1_amazonaws_com_0_23093_85f0ae39-3f71-3bee-1c20-c9e4c16353c5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/266f4356-d342-4238-ff7a-f46b717f2705.png)


- GitHubの「Secrets」の画面で以下のように設定します。
    - SSH_HOST：マネージドクラウドの「ホスト名」
    - SSH_PORT：マネージドクラウドの「ポート」
    - SSH_USERNAME：マネージドクラウドの「ユーザー名」
    - SSH_PRIVATE_KEY：マネージドクラウドのSSH接続用の「秘密鍵」の内容
        - Macの場合はpbcopyコマンドでコピー可能です。
        - `$ cat ~/.ssh/マネージドクラウドの秘密鍵 | pbcopy`
    - SSH_KNOWN_HOSTS：ssh-keyscanの実行結果
        - `$ ssh-keyscan -p マネージドクラウドの「ポート」 -H マネージドクラウドの「ホスト名」`
        - 「xxxxx.mc.lolipop.jp: Connection closed by remote host」が表示される場合は失敗しているので再実行してください。
        - 一度、マネージドクラウドの環境にSSH接続を行いログアウトしたあとに `ssh-keyscan` を実行すると成功するはずです。
![https___qiita-image-store_s3_ap-northeast-1_amazonaws_com_0_23093_d22f1fe4-12b6-f19d-7df5-dd0dd1dc68da.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/86279f5d-06ed-78ce-b9f6-83c5132eacc8.png)


### マネージドクラウドの「環境変数」の設定
- マネージドクラウドのダッシュボードで「データベース」の内容を確認します。
![https___qiita-image-store_s3_ap-northeast-1_amazonaws_com_0_23093_91398669-d353-7ff5-b66f-b7420ab1fd88.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/9ec4a0b9-69d2-67a6-3c07-f9f9307ffc57.png)

- パスワードについてはダッシュボード画面で表示されないので、プロジェクト作成時に設定した値を参照してください。
![https___qiita-image-store_s3_ap-northeast-1_amazonaws_com_0_23093_ae32f99a-96cc-d9b8-937e-aa4cbb5498c2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/017e925f-2f0b-5c8e-f409-bb742d64a633.png)

- マネージドクラウドの「環境変数の設定と管理」の画面で以下のように設定します。
    - DB_DATABASE：マネージドクラウドの「データベース名」
    - DB_USERNAME：マネージドクラウドの「ユーザー名」
    - DB_PASSWORD：マネージドクラウドのプロジェクト作成時に設定した「データベースパスワード」
    - DB_HOST：マネージドクラウドの「データベースのホスト名」
    - DB_PORT：3306
![https___qiita-image-store_s3_ap-northeast-1_amazonaws_com_0_23093_cd9d4957-958b-af97-2e5b-e121e15da634.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/95baec6c-4896-88c8-873d-feab80e575e7.png)


### 本番環境用.envの雛形ファイルの設定
- `src/.env.production` のデータベースに関連する箇所を以下のように記述しておきます。（マネージドクラウドの環境変数で設定した項目については削除）
- 今回、秘匿すべき情報についてはマネージドクラウドの環境変数に設定することにしましたが、このあたりはプロジェクトのポリシーなどもあるかと思いますので、環境ごとのルールに合わせてもらえると。</font>

```:src/.env.production
DB_CONNECTION=mysql
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=laravel
#DB_USERNAME=root
#DB_PASSWORD=
```

### GitHub Actionsの設定
以下のように `.github/workflows/deploy.yml` を準備しました。

```:yaml
name: Deploy

on:
  push:
    branches:
      - master

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: composer
        run: |
          cd src
          composer install --prefer-dist --no-interaction
      - name: npm
        run: |
          cd src
          npm install
          npm run prod
      - name: dot env copy
        run: |
          cd src
          cp .env.production .env
      - name: ssh key generate
        run: |
          mkdir -p ~/.ssh/
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
      - name: rsync deploy
        run: rsync -av -e "ssh -i ~/.ssh/id_rsa -p ${{ secrets.SSH_PORT }}" src ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:/var/www/
      - name: database migration
        run: ssh -i ~/.ssh/id_rsa -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} 'cd /var/www/src && php artisan migrate --force'
```

#### `.github/workflows/deploy.yml` の解説

```
name: Deploy
```
このアクションの名前です。今回は単純に「Deploy」としました。

```
on:
  push:
    branches:
      - master
```
masterブランチにpushされた場合にこのアクションが実行されるようにしています。

```
    runs-on: ubuntu-latest
```
`runs-on` で指定した仮想マシンの詳細については公式のページに解説がありますので、そちらを参照してください。
- [サポートされているランナーとハードウェアリソース](https://help.github.com/ja/actions/reference/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources)
- [GitHubホストランナーにインストールされるソフトウェア](https://help.github.com/ja/actions/reference/software-installed-on-github-hosted-runners)
- [サポートされているパッケージエコシステム
](https://help.github.com/ja/github/visualizing-repository-data-with-graphs/listing-the-packages-that-a-repository-depends-on#supported-package-ecosystems)

```
      - uses: actions/checkout@v2
```
公式の [actions/checkout](https://github.com/marketplace/actions/checkout) を使って、git checkoutを行います。

```
      - name: composer
        run: |
          cd src
          composer install --prefer-dist --no-interaction
```
composerパッケージのインストールを行います。

````
      - name: npm
        run: |
          cd src
          npm install
          npm run prod
```
npmパッケージのインストールとアセットファイルの生成を行います。

```
      - name: dot env copy
        run: |
          cd src
          cp .env.production .env
```
本番環境用.envの雛形ファイルを元に `.env` を作成します。

```
      - name: ssh key generate
        run: |
          mkdir -p ~/.ssh/
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
```
事前にSecretsの画面で登録しておいた秘密鍵を `~/.ssh/id_rsa` としてファイルに出力し、ファイルの権限を600に設定します。

SSH接続時のオプションで `-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no` を設定することで接続時の警告を無視することができますが、中間者攻撃の観点から `~/.ssh/known_hosts` に登録することにします。
`ssh-keyscan -p ${{ secrets.SSH_PORT }} -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts` とやりたいところですが、マネージドクラウド側の都合（[参考情報](https://diary.shu-cream.net/2020/05/02/github-actions-for-lolipop-mc.html)）によりssh-keyscanに失敗する場合があるようなので手元の環境でssh-keyscanを実行した結果を `SSH_KNOWN_HOSTS` として設定しておき、その内容を `~/.ssh/known_hosts` に書き込みます。

```
      - name: rsync deploy
        run: rsync -av -e "ssh -i ~/.ssh/id_rsa -p ${{ secrets.SSH_PORT }}" src ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:/var/www/
```

対象ディレクトリの `src` をrsyncコマンドを使ってデプロイします。
`--delete` オプションを利用していない理由は `php artisan storage:link` で設定している `/var/www/src/public/storage` のシンボリックリンクが削除されないようにするためです。（デプロイの前にシンボリックリンクを作成してrsyncするとうまくいくかもしれません）

```
      - name: database migration
        run: ssh -i ~/.ssh/id_rsa -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} 'cd /var/www/src && php artisan migrate --force'
```
最後はDBのマイグレーションです。デプロイ先のサーバーにSSH接続して実行しています。

## GitHub Actionsの動作確認
一連の処理に成功すると、GitHubの「Actions」の画面で「Complete job」の文字が確認できます。
![https___qiita-image-store_s3_ap-northeast-1_amazonaws_com_0_23093_052f61b1-daaa-67a8-3a01-3528e7494f8a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23093/dbae40d7-c1b4-906f-4a10-24b995849b2e.png)

## 参考URL
- [LOLIPOPマネージドクラウドで、Laravel環境を構築する](https://qiita.com/yasuken/items/8184e8e744d6e0d2d1c5)
- [ロリポップマネージドクラウドでLaravelアプリのセットアップ手順](https://qiita.com/take2isk/items/85fe3b6cb747243fd4ff)
- [GitHub Actionsで「ロリポップ！」「ヘテムル」をもっと便利に使おう](https://tech.pepabo.com/2020/03/11/github-actions-for-lolipop-and-heteml/)
- [GitHub ActionsでRailsアプリをロリポップマネージドクラウドにデプロイする](https://diary.shu-cream.net/2020/05/02/github-actions-for-lolipop-mc.html)
- [GitHub Actions で快適なデプロイを実現する(rsync・laravel)](https://www.agent-grow.com/self20percent/2019/09/24/github-actions-rsync/)
- [GitHub Actions で LaravelのCI/CD環境を構築する（MySQL, Deployer）](https://qiita.com/wim/items/7eb8386672fd114c2576)
- [GitHub Actions で Laravel + Vue 環境を自動デプロイ](https://qiita.com/caesar_cat/items/f27f344322728f4296c0)
- [deployerを使って簡単にLaravelのアプリケーションをロリポップ!マネージドクラウドにデプロイする](https://www.fendo181.me/entry/2018/12/20/084500)
- [「SSHホスト鍵が変わってるよ！」と怒られたときの対処](https://qiita.com/hnw/items/0eeee62ce403b8d6a23c)