AWS(EC2)への自動デプロイをDeployerからGitHub Actionsに移行する

初心者が実務でEC2へのデプロイをDeployerからGitHub Actionsに移行した時の手順まとめ的なメモです。
#背景と目的
- 今回対象のプロジェクトは`Deployer`を使用して本番環境およびテスト環境にdeployを実行している。
- この場合、デプロイを行う人がローカルでコマンドを手動で実行することになる。
- これをGitHub Actionsを導入して「main(stag)ブランチが更新されたら自動でdeployされる」という状態に持っていく。

# やること
- 踏み台サーバー経由でAWS EC2に接続できるようにする
- GitHub Actionsの自動デプロイの設定を行う
- Apacheのドキュメントルートの設定を行う
- SSL/TSLの自動更新設定を確認する

# 具体的な手順
## AWSにIAMユーザーとしてログイン
まずはAWSのマネジメントコンソールにログインするところから。
`アカウントID(の入ったURL)`と`ユーザー名`と`パスワード`を管理者に共有してもらい、ログインする。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/b97727f3-eaa7-a10d-7f3c-07c7e056120b.png" width=50%>
ログインしたら自分でパスワードの変更を行うこと。
右上のナビゲーションバーでユーザー名を選択して`[セキュリティ資格情報]`を選択すれば変更できる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/959d996e-632f-3d5b-daa8-f56e5c483d74.png" width=25%>

## 1. セキュリティグループの設定
セキュリティグループのインバウンドルールを変更する。これは今回のデプロイの設定とは関係ないが、もともと踏み台サーバーのIPアドレスの登録設定が最適になかったことによる作業である。

EC2のダッシュボードから`[セキュリティグループ]`を選択し、対象のグループ名を選択する。
次にインバウンドルールのタブから`[インバウンドルールを編集]`→`[ルールを追加]`の順に選択して、SSHのインバウンドルールを設定する。(踏み台サーバーのIPアドレスと名称)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/3cd5e7fb-51f3-8e0e-61ce-cdbdc1fcff2c.png" width=60%>

## 2. IAMユーザーの作成
GitHub Actionsに割り当てるアタッチポリシーを割り当てる。
`IAM`→`ユーザー`→`ユーザーを追加`の順に選択していき..
「物件名-github-actions-deploy」のような名前のIAMユーザーを作成してアクセス権限を割り当てる。

```:今回割り当てるアクセス権限のポリシー
AmazonVPCFullAccess
AmazonEC2ContainerServiceRole
```
以下のような感じで設定できればOK。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/4e1bae6e-1ad3-dda6-9896-321ba0ef5043.png" width=70%>

## 3. 自分のマシンからSSH接続確認
### 踏み台サーバーの設定
踏み台サーバーのプロキシの設定をする。

```
$ cd /.ssh
$ vi config
```
でconfigファイルにプロキシの設定を追加。追加する設定内容は管理者確認する。

```:.ssh/config
### xxx(プロジェクト名) ########################################
Host <テスト環境のHost名>
  HostName <サーバーのIP>
  User <ユーザー名>
  Port 22
  IdentityFile ~/.ssh/<ローカルで作成した秘密鍵のファイル名>
  ProxyCommand ssh -W %h:%p -p <プロキシのパスワード> <プロキシの名前>
Host <本番環境のHost名>
  HostName <サーバーのIP>
  User <ユーザー名>
  Port 22
  IdentityFile ~/.ssh/<ローカルで作成した秘密鍵のファイル名>
  ProxyCommand ssh -W %h:%p -p <プロキシのパスワード> <プロキシの名前>
#####################################################
```

### 鍵を作成して公開鍵を管理者に共有する
自分のマシンでキーペアを作成する。

```
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/username/.ssh/id_rsa): /Users/username/.ssh/xxx-develop
```
作成したら、

```
$ vi xxx-develop.pub
```
で公開鍵を開いてコピーし、管理者に登録してもらう。
登録されれば`$ ssh Host名`でssh接続できるようになる。

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

なお上記はテスト環境での作業なので本番環境も同様に行っていく。
その際は⌘+Dで左右にターミナルを分割して作業すると良い。

## 4. そのままsshでEC2のクレデンシャルセット
SSHで接続した状態で、`$ aws configure`を実行し、クレデンシャルをセットする。

```
$ aws configure
AWS Access Key ID [None]: アクセスキーを貼り付け
AWS Secret Access Key [None]: シークレットアクセスキーを貼り付け
Default region name [None]: リージョン名(ap-northeast-1とか)
Default output format [None]:特になし(入力不要)
```
これで与えられた権限にて作業ができるようになる。

## 5. GitHubリポジトリに設定する
### ①作業する前にイメージ(AMI)を作成する
現在の状態のインスタンスのバックアップとして、AMIを作成する。
(これは先にやっても良い)

`インスタンス`→`アクション`→`イメージを作成`の順に選択。
イメージ名は今回は「プロジェクト名_ボリュームのサイズ_日付(20210401等)」の命名規則とする。
イメージの説明はイメージ名と同じものを貼り付けでOK。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/2cc34764-9e06-6258-43ee-ec66ca2fab58.png" width=70%>

イメージ作成時にダウンタイム(インスタンスが停止)が通常1〜2分程度発生するので留意。
本番環境でボリュームが大きいインスタンスを扱う時は、上長に確認すること。

### ②サーバで鍵を作成する
今回は`~/.ssh/github/id_rsa.pub`という鍵がもともとあったが、
無い場合は`$ ssh-keygen -t rsa`でキーペアを作成する。

### ③作った公開鍵をサーバの `authorized_keys`に追記
```
$ vi authorized_keys
```
`~/.ssh/authorized_key`を開いて、サーバーの公開鍵`id_rsa.pub`の中身を追記する。
すでに別案件等の記述がある場合はその下に追記する。

### ④作った公開鍵をGitHubリポジトリに設定
`setting` → `DeployKeys` → `Add deploy key` で公開鍵を設定する。
もしリポジトリに対する権限がなくて作業出来ない場合は、管理者に情報を送って設定してもらう。
TitleはわかりやすいものでOK。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/187011cd-1c06-60f4-2c07-61e2d759ce47.png" width=70%>

### ⑤SSH接続テスト
サーバーでSSH接続できるかテスト。

```
$ ssh -T git@github.com
Hi 〇〇! You've successfully authenticated, but GitHub does not provide shell access.
```
こんな感じになれば接続成功。

### ⑥GitHubリポジトリにActions secretsを設定
リポジトリの`Settings`タブの`Secrets`を選択。ここのNameとValueを登録する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/4e7e9404-acba-00c2-bcfe-9d003fd166a2.png" width=80%>
下記の左側をNameに、右側をValueにひとつずつ設定していく。

#### ・設定するNameとValue
```:共通の設定
USER_NAME：ユーザー名
```
```:develop環境の設定
DEVELOP_ACCESS_KEY：作ったIAMのアクセスキー
DEVELOP_SECRET_ACCESS_KEY：作ったIAMのシークレットキー
DEVELOP_HOST_NAME：サーバIP
DEVELOP_SECURITY_GROUP：セキュリティグループID
DEVELOP_PRIVATE_KEY：サーバの秘密鍵(.pubじゃない方)
```
```:production環境の設定
PRODUCTION_ACCESS_KEY：作ったIAMのアクセスキー
PRODUCTION_SECRET_ACCESS_KEY：作ったIAMのシークレットキー
PRODUCTION_HOST_NAME：サーバIP
PRODUCTION_SECURITY_GROUP：セキュリティグループID
PRODUCTION_PRIVATE_KEY：サーバの秘密鍵(.pubじゃない方)
```
上記はそのまま続けて入力すればOK。
この時、改行や空白の違いで動かなくなり、どれが間違っているかもわからなくなるので慎重に作業すること。
サーバーの秘密鍵は`-----BEGIN RSA PRIVATE KEY-----`や`-----END RSA PRIVATE KEY-----`も含めてコピーして貼り付けることを忘れずに。(-の数が合ってるかも確認すると良い)

#### ・Actions Secretsの完成イメージ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/a712d4a5-02a7-500e-16b4-e5d912646fee.png" width=70%>

## 6. EC2に新しいドキュメントルート作成
現状のEC2のサーバー内の構成を確認し、`projdir`というディレクトリを新規で追加する。
これからの作業は、そこにGitHubからpullしたドキュメントが入るように設定していくというもの。

```:/home/ユーザー名の状態を確認
$ cd /home/ユーザー名/
$ ls -la

lrwxrwxrwx  1 ユーザー名 ユーザー名  18  x月 xx  xxxx public_html -> deploy/pro/current
```

現状は`public_html`に対する`deploy/pro/current`というシンボリックリンクがある。
(deployerで自動デプロイを設定している)

ここで、`projdir`というディレクトリを新規で作成する。

```
$ mkdir projdir
```

## 7. ローカルで階層を変える(番外)
これは今回のGitHub Actionsとは関係ない工程だが、dockerをリポジトリに含めていなかったため、ローカル環境でも`projdir`を作成して配下にアプリケーションデータたちを置き、dockerディレクトリとdocker-compose.ymlファイルもgitに含めるように変更する。
ディレクトリの構成を変えるときにgitの追跡が失われないように留意すること。
(必要に応じて再度`$ git clone`した方が良いかも..)

### Xdebugの設定変更(番外)
なおこれも今回のdeployとは関係ないが、このときDockerfileのXdebugの設定に以下を追加した。(もともとはコメントアウトで別の値が入っていた)

```:Dockerfileに追記
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so
```

## 8. GitHub Actions Workflowの設定 & deployテスト
Workflowファイルを作成してプロジェクトと同じディレクトリに入れる。(今回は管理者から共有)
内容としてはmain(stag)ブランチが更新されると自動的にjobsが走るというもの。

```yml:deploy_develop.ymlの雰囲気(の一部)
name: StagingDeploy
on:
  push:
    branches: [stag]

env:
  ENV_FILE: .env.dev
  RSYNC_SOURCE: /projdir/
  RSYNC_TARGET: /home/ユーザー名/〇〇/develop/public_html

jobs:
  deploy_staging:
    if: github.ref == 'refs/heads/stag'
    runs-on: ubuntu-latest
    steps:
..
~後略~
```

### 実行されてdeployが完了した時の図
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/dd8ab5df-fe22-6279-fd1f-a01f860b232e.png" width=70%>
完了すると、先ほどサーバーで作成した`proj`ディレクトリにドキュメントが入っているのが確認できる。

## 9. virtualhostのconfファイルの編集
### バックアップ作成
まずはディレクトリに移動して既存のdefault_vhost.confファイルのバックアップを作成する。

```
$ cd /etc/httpd/conf.d/extra
$ sudo cp default_vhost.conf default_vhost.conf.bk0401
```

### ドキュメントルート変更
viエディタで編集する。

```
$ sudo vi default_vhost.conf
DocumentRoot /home/ユーザー名/public_html/public ←これを
DocumentRoot /home/ユーザー名/public_html ←これに修正する
```
`<Directory>`の部分も同様に修正する。

### 構文テスト
`default_vhost.conf`を修正したら、Apacheの構文のテストを行う。

```
$ httpd -t
AH00526: Syntax error on line 32 of /etc/httpd/conf.d/extra/default_vhost.conf:
SSLCertificateFile: file '/etc/letsencrypt/live/ドメイン名/fullchain.pem' does not exist or is empty
```
構文エラーとなるので確認。

```
$ cd /etc/letsencrypt/live/
-bash: cd: /etc/letsencrypt/live/: Permission denied
```
`live`ディレクトリのパーミッションが原因で構文エラーとなっているので、
パーミッションの設定を変更する。

```
$ cd /etc/letsencrypt
$ sudo chmod 755 archive/
$ sudo chmod 755 live/
```
この状態で再度テストして、OKなら再起動。

```
$ httpd -t
Syntax OK
$ sudo systemctl reload httpd
```

なおテスト環境では次のシンボリック設定を先に行っても良いが、本番環境では今回のように
`構文テスト→NG→パーミッション変更→confファイル変更→シンボリック設定`
の順序としないとダウンタイム(アクセスできない状態)が発生することになるので留意。

## 10. ドキュメントルートにシンボリックリンク貼る
`/home/ユーザー名`に移動し、ドキュメントディレクトリ名を`public_html`から`public_html_bk`にリネーム。

```
$ cd /home/ユーザー名
$ sudo mv public_html public_html_bk
```
再度`public_html`という名前で`/home/ユーザー名/projdir/public`に対するシンボリックリンクを新たに作成する。

```
$ ln -s /home/ユーザー名/projdir/public /home/ユーザー名/public_html
```

`$ ln -s`コマンドは、第一引数が本体(リンクの参照先)で、第二引数が作成するリンクである。
結果として以下のように変更される。

```:変更前
drwxrwsr-x  3 ユーザー名 ユーザー名    17 xxx deploy
drwxrwxr-x  2 ユーザー名 ユーザー名  4096 xxx logs
drwxrwsr-x 13 ユーザー名 ユーザー名  4096 xxx projdir
lrwxrwxrwx  1 ユーザー名 ユーザー名    18 xxx public_html -> deploy/pro/current
```
```:変更後
drwxrwsr-x  3 ユーザー名 ユーザー名    17 xxx deploy
drwxrwxr-x  2 ユーザー名 ユーザー名  4096 xxx logs
drwxrwsr-x 13 ユーザー名 ユーザー名  4096 xxx projdir
lrwxrwxrwx  1 ユーザー名 ユーザー名    29 xxx public_html -> /home/ユーザー名/projdir/public
lrwxrwxrwx  1 ユーザー名 ユーザー名    18 xxx public_html_bk -> deploy/pro/current
```

## 11. SSL/TLS自動更新の確認
今回はコマンドラインからの手動更新ではなくCertbotとCronを使った自動更新を設定しており、
ドキュメントルートを変更したため設定の確認を行っていく。
CertbotはLet’s EncryptからSSL/TSL証明書を発行や更新の申請をするための無料ツールである。

### ①Certbotコマンドのパスを確認
```
$ which certbot (なければcertbot-auto)
/usr/bin/certbot
```

### ②dry-run(テスト)を実行する
コマンドの場所が分かったので`renew`コマンド(証明書更新)をテストする。

```
$ sudo /usr/bin/certbot renew --dry-run --post-hook "systemctl restart httpd"
```
`--dry-run`オプションはファイルに影響を及ぼさずにテスト実行をするオプション。
また`--post-hook`は更新実行後に行われる処理を記述できるオプションである。（`--deploy-hook`でも良いかも）

コマンドを実行すると、

```
..
Challenge failed for domain ドメイン名
..
To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address.
```
となる。ドメイン名やDNSのAレコード、IPアドレスが正しいか確認してくださいとのこと。

### ③ドメインのconfファイルを編集する
これは今回ドキュメントルートを変更したことによるためで、
/letsencript/renewalディレクトリにあるドメインのconfファイルを編集していく。

```
$ sudo vi  /etc/letsencrypt/renewal/ドメイン名.conf
webroot_path = /home/ユーザー名/public_html, ←ここと
[[webroot_map]]
ドメイン名 = /home/ユーザー名/public_html ←ここを、正しいドキュメントルートに修正
```

### ④再度dry-runを実行する
```
$ sudo /usr/bin/certbot renew --dry-run --post-hook "systemctl restart httpd"
```
今度は無事に成功。

```
Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/ドメイン名/fullchain.pem (success)
```

### ⑤Cronの状態を確認
```
$ sudo systemctl status crond
```
これで、

```
Active: active (running)
```
となっていればOK。

以上で作業完了です。お疲れさまでした〜