ASP.NET API + Nginx + Docker ComposeをGithub Actionsを使ってCI/CD環境を構築する

# はじめに
[以前の記事](https://qiita.com/mkuwan/items/22a5d7fdb77008149fad)でAsp.netとNginxをdocker-composeで動くようにしましたが、今回はこれをGithub Actionsを使ってVPS上にテスト・ビルド・デプロイしようと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/ae8a4a7f-fc28-48e0-8bcf-edda634ad39f.png)
Github Actionsを使って、DockerHubにImageを出力して、LinuxからPullするというのもありなのですが、今回はこのDockerHubを使用しない形となっています。そのため、Linux側でdocker composeでの再ビルドをしています。

## 前提
VPS(ubuntu22.40): 自分はConoha Vpsを使用しています。awsやazureでも似たようなものですが、お作法は違うかもしれません。あとawsやaaureとかだとGitなども自前のものがあったりで、もっと簡単にいろいろな作業ができると思います。自分は勉強のために設定からできるようにとVPSを使用しています。
UbuntuにGit, docker composeをインストール済み
Github: アカウントを持っていてRepositoryを作成している
開発環境: Windows11, C#, ASP.net, Visual Studio 2022 (Javeでも基本作業は変わらないです)

## 記事の内容
- VPSでのユーザー作成とSSL鍵の作成
- Githubでの鍵の設置
- ローカル環境でのコード作成とGithub Actions用ymlファイル作成
- push build test deploy
以上の構成となっています。ここは知っているという場合は読み飛ばしてください。
# VPS環境での作業
ユーザー作成は、Github Actionsで使用するものですが、すでにある場合は次に進んでください。rootしか使っていないという場合でも操作はあまり変わりません。
## ユーザー作成
この辺りはwebを調べればいくらでも出てくるところですが、一応手順として書きます。
rootユーザーになるか、sudoからコマンドを始めます。
ここではdemouserとします。
```console
sudo useradd -m demouser
```
-m オプションはユーザーのホームディレクトリを作成するという意味です。
このあとパスワードを聞かれるので入力します。
続いて、このユーザーにsudo権限を与えておきます。
```console
sudo usermod -aG sudo demouser
```
できあがったら作成したユーザーに切り替えます。
```
su demouser
```
## SSL鍵作成
秘密鍵と公開鍵を作成します。
自分のイメージとしては秘密鍵＝鍵、公開鍵＝鍵穴です。鍵穴はいつもそこにありますが、鍵を持っている人にしか開けられないという感じです。
### sshディレクトリに移動
```console
cd ~/.ssh
```
### rsa 4096bitの鍵を作成
`mail@address.com`は自分のメールアドレスに入れ替えてください。
```console
sudo ssh-keygen -t rsa -b 4096 -C mail@adress.com
```
このあと Enter passphraseと聞かれますが、パスフレーズは入力せずにそのままEnterキーで次に進みます。それ以外もEnterキーで未入力のまま進んで問題ありません。
これで~/.sshの中をみるとid_rsaとid_rsa.pubができています。
注意事項ですが、pubは公開鍵なのであまり問題ありませんが、秘密鍵は他人に渡したり、公開することは絶対にしないでください。自分の家の鍵を人に渡してしまうのと同じです。
```console
ls
```
このうち.pubとなっているのが公開鍵です。
```console
id_rsa  id_rsa.pub
```
中を確認したい場合はcatコマンドで見ることができます。
```
sudo cat id_rsa
sudo cat id_rsa.pub
```
公開鍵はこんな感じ
```
ssh-rsa xxxxxxxxxxx～xxxxxxxxxx== mail@adress.com
```
秘密鍵はこんな感じです
```
-----BEGIN OPENSSH PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END OPENSSH PRIVATE KEY-----
```
このうち、秘密鍵をコピーして同じ名前で自分のPCにファイルを作成しておきましょう。
## 鍵を使ってログインできるようにする
公開鍵でのログインができるように設定ファイルを変更します。
nanoを使っていますがviでも構いません。
```console
sudo nano /etc/ssh/sshd_config
```
この中の以下のところの`#`を消して有効化します。
```console
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

AuthorizedKeysFile      .ssh/authorized_keys .ssh/authorized_keys2
```
また以下の設定値を`no`から`yes`に変更します。
```console
PubkeyAuthentication yes
```
これで保存して、変更を反映させます。
```console
systemctl reload sshd
```
## 確認
Windowsの場合はPowerShellで確認してみます。(macの方はコンソールでできます)
```console
"id_rsaを保存しているフォルダを指定してください"
cd C:\Users\demouser\documents\id_rsa
```
sshコマンドで接続
```console
"xxx.xxx.xxx.xxxはVPSのIPアドレス"
ssh -i id_rsa demouser@xxx.xxx.xxx.xxx
```
これで接続できれば、Github Actionsでも使用することができます。
もしパスワードを聞かれたり、Invalid Formatとなった場合は、どこかに問題がありますのでいろいろ調べてみましょう。

## リポジトリ用のフォルダ作成
GithubからリポジトリをPushするためのディレクトリを作っておきます
```console
sudo mkdir /repo/demo
```
これはご自分の好きな名前や場所で作成してください。Github Actionsで使用するymlファイルで使用します。

## Github接続情報
Githubの接続時に使用する秘密鍵をどれにするかの接続情報ファイルを作成します
```console
sudo nano ~/.ssh/config
```
configファイルがない場合は空のドキュメントができると思います。
この中に以下のような設定を書いて保存します。
```console
Host github.com
HostName github.com
IdentityFile ~/.ssh/id_rsa
User git
```

# Githubでの作業
作業に入る前に、今回Github Actionsを使用した構成を書いておきます。
公開鍵と秘密鍵の置き場所がポイントです。
現在はVPS上に公開鍵・秘密鍵がある状態です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/32cda963-0a1b-6293-67fb-bb913b956c3b.png)
Githubにコミット(push)すると、Github Actionsでビルドとテストが始まり、デプロイするためにGithubに保存された秘密鍵を使用してVPSに接続します。VPSで行うコマンドも送信します。
次に、送信されたコマンドでVPS側からGighubにpull requestを送り、Repositoryを受信します。
最後にVPS側でビルドとdocker-compose up を行い、アプリが起動するようにします。
### GithubのSettingsからの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/57420352-de48-0986-ff20-4e8651cca063.png)
SSH and GPG keysを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/cd4b0364-3605-9ce3-5293-3b4c26f3bdbc.png)
New SSH keyボタンを押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/d30ba38f-ea64-17cb-130b-86e86a2ce41f.png)
SSH Keys / Add new
Titleに適当な名前を入力
Key typeはAuthengication Key
Keyにid_rsa.pubの内容を貼り付けます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/abda7dc4-68aa-6ad6-39cb-0db730bf215b.png)
### RepositoryのSettingsを設定
Repositoryを選択してSettingsをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/feac6a02-736d-4d69-4ea7-2dba8f7e5152.png)
SecurityのSecrets#Actionsを選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/7a020ba9-193e-1ec2-b14d-9b90451f4687.png)
New repository secretをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/6fbe8497-7b75-1e88-5d9b-c889485da622.png)
Actions secretsの項目を入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/7a551221-08b6-aa07-6adb-82c7e6e9e516.png)
設定値は以下の感じで5つ作成します
1. Name: DEMOHOST
Secret: xxx.xxx.xx.xxx (VPSのIPアドレスもしくはドメイン)
2. Name: SSH_PORT
Secret: 22 (VPSでの設定に従ってください)
3. Name: DEMO_USER
Secret: demouser (ユーザー名)
4. Name: DEMO_PASS
Secret: ログインパスワード
5. Name: DEMO_ID_RSA
Secret: 秘密鍵を貼り付ける

これでVPSとGithubでの作業は完了です。
最後にローカル環境での作業となります。ここまでくれば、基本的にローカル環境で開発を行い、Githubにコミットすれば、自動的にVPSで使用できるようにまでなります。

# ローカル環境(Visual Studio)での作業
基本的なローカル開発は[以前の記事](https://qiita.com/mkuwan/items/22a5d7fdb77008149fad)を参考にしてください。
ここではdocker-composeまでは完了しているという前提で、Github Actionsのための設定ymlファイルを紹介します。
### build.yml作成
Visual Studioのトップにworkflowsフォルダを作成し、その中にbuild.ymlファイルを作成します。
ここではbuild.ymlとしていますが、この名前は自由です。
重要なのは、workflowsフォルダをトップに作成するということです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/9c71e6bc-2bb6-a9c8-5626-19adcc9fdfae.png)
### build.ymlの中身
```yml
# GitHub リポジトリの [アクション] タブに表示されるワークフローの名前(省略可能)
name: '.net build test deploy'

# ワークフローのトリガー
# 変更がリポジトリにプッシュされるたびに、ワークフロー実行がトリガーされます
on:
  push:
  pull_request:
    branches: [ main ]
    paths:
    - '**.cs'
    - '**.csproj'
env:
    DOTNET_VERSION: '6.0.x'
jobs:
  build-and-test:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v3
      - name: Setup .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}
      - name: Install dependencies
        run: dotnet restore [ソリューション名].sln 
      # Build
      - name: Build
        run: dotnet build [ソリューション名].sln --configuration Release --no-restore
      # Test
      - name: Test with dotnet
        run: dotnet test [ソリューション名].sln --no-restore --verbosity normal
      # docker-compose
      - name: docker-compose build
        run: docker compose build
  deploy:
    runs-on: ubuntu-22.04
    needs: build-and-test
    steps:
      - name: Deploy to Conoha VPS
        uses: appleboy/ssh-action@master
        with:
          key: ${{ secrets.DEMO_ID_RSA }}
          host: ${{ secrets.DEMOHOST }}
          username: ${{ secrets.DEMO_USER }}
          port: ${{ secrets.DEMO_PASS }}
          script: |
            cd /repo/demo
            git pull git@github.com:[user]/[repository].git
            docker compose -f docker-compose.yml build
            docker compose up -d
```


# 最後に
これで基本的な設定は完了です。
ローカル環境で開発を行い、Githubにpushすると、こんな感じの画面がGithub Actionsに表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/1130b101-343b-c9db-911b-da94e0f5fed9.png)
こまかくbuild.ymlについて説明していませんでしたが(後日記事にするかもです)、buildとtestが終わってからdeployが行われるようにymlファイルを記述しています。
すべてうまくいけば緑色に表示されます。
VPS側ではbuildとdocker compose up -dが走っているので、自動的にビルドと起動が行われています。

ちなみにテストに失敗すると、deployは行われないようになっています。

これで、ローカル環境で開発を行い、Githubにpushすることで、自動テスト・自動ビルド・自動デプロイが行われようになりました。

# 大切なこと
CI/CDがきちんと行われるためには、間違ったコードがデプロイされないということが大切です。
つまり、テストがきちんと成功／失敗することが重要になります。
テストコードを書くことは、開発現場では軽視されがちですが、実はテストコードこそが開発の効率性に寄与するということになります。テストコードを書く習慣が業界にもっと広がることを願うばかりです。