AWSにGoアプリケーションをデプロイしてみた！Apacheをリバースプロキシとして使用

## はじめに

こんにちは、Shioです。  
インフラ学習の一環として、自作のGoアプリケーションをAWSにデプロイしました。  
サーバーにはApacheを利用しましたが、個人的に設定が大変でした💦  

PHPやPythonのようなインタプリタ言語は、Apacheのモジュール（例: `mod_php` や `mod_wsgi`）で簡単にサポートされます。しかし、Goはスタンドアロンで動作するバイナリのため、Apacheのモジュールでは直接対応できません。そのため、今回はApacheをリバースプロキシとして設定し、公開しました。

本来、リバースプロキシには軽量かつシンプルなNginxがよく選ばれますが、今回は認証の学習も目的として、Digest認証を標準でサポートするApacheを使いました。  

本記事が少しでも皆さんのお役に立てれば嬉しいです🙇！  



## 環境構成

- **サーバー**: AWS EC2（HTTPアクセスがセキュリティグループで許可されていることが前提）
- **OS**: Amazon Linux 2023 AMI
- **Webサーバー**: Apache
- **アプリケーション**: Go言語APIサーバー（フロントエンドは用意していません）
  - Dockerfileは使用せず、アプリケーションを転送後、直接ビルドしました。
- **データベース**: SQLite（簡単に動作確認を行うため採用）
 ※ 本記事では、アプリケーションで使用するデータベースの設定や動作には触れません。


リモートサーバーにログインした状態から手順を開始します👀  

## 1. 基本パッケージのインストール

### Apache HTTPサーバーのインストール

`httpd` はApache HTTP Serverのパッケージ名です。`-y` オプションは全ての問い合わせに「yes」として応答します。

```bash
sudo yum install httpd -y
```

### 必要なモジュールの確認（proxy関連）

以下のコマンドで現在ロードされているApacheモジュールを確認し、`mod_proxy` と `mod_proxy_http` があることを確認します。

```bash
httpd -M | grep proxy
```

出力例:
```
proxy_module (shared)
…
proxy_http_module (shared)
…
```

- **`mod_proxy`**: Apacheをリバースプロキシとして動作させるために必要なモジュール  
- **`mod_proxy_http`**: HTTPリクエストをリバースプロキシとして処理するために必要なモジュール  

### Go言語のインストール

```bash
sudo yum install golang -y
```



## 2. Apacheの設定

### リバースプロキシの設定

Apache設定ファイル（`/etc/httpd/conf/httpd.conf`）に以下の内容を追加します。  
⚠️ 管理者権限でファイルを開き編集します。

```bash
sudo vim /etc/httpd/conf/httpd.conf
```

以下の設定を追加:

```apache
<VirtualHost *:80>
    ServerName example.com  # EC2のパブリックIPやドメイン名に変更
    
    ProxyPreserveHost On
    
    # /v1/ へのリクエストをGoアプリケーションへ転送
    ProxyPass /v1/ http://127.0.0.1:8080/v1/
    ProxyPassReverse /v1/ http://127.0.0.1:8080/v1/
</VirtualHost>
```

#### 設定の詳細

1. **`<VirtualHost *:80>`**  
   ポート80で受けた全リクエストに対して、この仮想ホストの設定を適用します。`*` は全IPアドレスを意味します。

2. **`ServerName example.com`**  
   仮想ホストの対象となるIPアドレスまたはドメインを指定します。

3. **`ProxyPreserveHost On`**  
   クライアントからのリクエストヘッダーの `Host` フィールドをそのままGoアプリケーションに渡します。

4. **`ProxyPass /v1/ http://127.0.0.1:8080/v1/`**  
   Apacheが受け取った `/v1/` へのリクエストをローカルの8080番ポートにあるGoアプリケーションへ転送します。

5. **`ProxyPassReverse /v1/ http://127.0.0.1:8080/v1/`**  
   Goアプリケーションからのレスポンスで `Location` ヘッダー等が適切に修正されるようにします。



### Apacheの起動と有効化

設定ファイルの構文チェックを行います。

```bash
sudo httpd -t
```


`Syntax OK`と表示されたら、Apacheを起動し、サーバー起動時に自動的にApacheが起動するよう設定します。

```bash
sudo systemctl start httpd  # Apacheの起動
sudo systemctl enable httpd  # 自動起動を有効化
```



## 3. Goアプリケーションのデプロイ

### アプリケーションの転送

ローカルPCからEC2インスタンスにアプリケーションを転送します。`scp`コマンドを使用します。

```bash
scp -i <秘密鍵> -r ./myapp ec2-user@<EC2のIP>:/home/ec2-user/
```

#### コマンドの意味

- `scp`: セキュアコピーコマンド
- `-r`: ディレクトリを再帰的にコピーするオプション
- `-i <秘密鍵>`: 秘密鍵を指定
- `./myapp`: ローカルのディレクトリ
- `ec2-user@<EC2のIP>`: リモートのEC2インスタンスのユーザー名とIPアドレス
- `/home/ec2-user/`: 転送先ディレクトリ

転送が成功したら、再度EC2インスタンスにSSHでログインし、転送されたディレクトリが確認できるはずです👀

### Goアプリケーションのビルドと実行


#### 1. **アプリケーションのビルド**

アプリケーションのソースコードがあるディレクトリに移動して、ビルドします。

```bash
cd myapp
go build -o myapp main.go
```

#### 2. **アプリケーションの実行**

ビルドしたアプリケーションをバックグラウンドで実行します。



```bash
./myapp &
```
 
`&`を付けることでバックグラウンド実行されます。

ブラウザやcurlコマンドでアプリケーションが問題なく動作していることを確認します👀


## 4. Goアプリケーションのサービス化
バックグラウンド実行は一時的なものなので、サーバー再起動後も自動でアプリケーションを起動させるために`systemd`を使用してサービスとして設定します。その前に使用中の8080ポートを開放します👀


#### 1. **プロセスの確認**

   ```bash
   ps aux | grep myapp
   ```

#### 2. **プロセスの停止**

   プロセスIDを指定して停止します。

   ```bash
   kill -9 PID
   ```


### `systemd`サービスの設定

現在ディレクトリ構成は以下のようになっています。
```shell
/home/ec2-user/myapp/      # アプリケーションのディレクトリ
  ├── myapp                # 実行ファイル（Goアプリケーション）
  ├── go.mod               # Goのモジュール設定ファイル
  ├── go.sum               # Goのモジュールチェックサムファイル
  └── その他の設定ファイル
```


### サービスファイルを作成

systemdのサービスユニットファイルを作成し、以下の内容を貼り付けます。


   ```bash
   sudo vim /etc/systemd/system/myapp.service
   ```

   以下を記述:

   ```ini
   [Unit]
   Description=My Go Application
   After=network.target

   [Service]
   ExecStart=/home/ec2-user/myapp/myapp  # アプリケーションのパスを指定
   WorkingDirectory=/home/ec2-user/myapp  # アプリケーションがあるディレクトリ
   Restart=always
   User=ec2-user  # 実行ユーザー

   [Install]
   WantedBy=multi-user.target
   ```
### 詳細

[Unit]
- **`Description`**: サービスの説明です。  
- **`After=network.target`**: ネットワークが有効になった後に開始する設定です。

[Service]
- **`ExecStart=/home/ec2-user/myapp/myapp`**: 実行するバイナリのパスを指定します。
- **`WorkingDirectory=/home/ec2-user/myapp`**: アプリケーションがあるディレクトリを指定します。 
- **`Restart=always`**: サービスが停止した場合に自動で再起動します。  
- **`User=ec2-user`**: アプリケーションを実行するユーザーを指定します。  
- **`EnvironmentFile=/home/ec2-user/myapp/app/.env`**: （必要に応じて）環境変数を含むファイルを指定します


### サービスの起動と自動起動の有効化


#### 1. **`systemd`の設定をリロード**

   ```bash
   sudo systemctl daemon-reload
   ```

#### 2. **サービスの起動と自動起動の有効化**

   ```bash
   sudo systemctl start myapp
   sudo systemctl enable myapp
   ```

#### 3. **サービスのステータス確認**

アプリケーションが正しく起動しているかを確認します。

```bash
sudo systemctl status myapp
```

成功していれば以下のようなステータスが表示されます。

```bash

● myapp.service - My Go Application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; preset: disabled)
     Active: active (running) 
```

## 5. トラブルシューティング:hammer_pick:

### Apacheログの確認

Apacheのエラーログを確認

```bash
sudo tail -f /var/log/httpd/error_log
```

### Goアプリケーションのログ確認

Goアプリケーションの`systemd`ログを確認

```bash
sudo journalctl -u myapp -f
```

### 遭遇したエラーと対処方法:fire:

#### ポート8080がすでに使用中

:star:ポート8080を使用しているプロセスを特定して終了

```bash
sudo lsof -i :8080
sudo kill -9 <PID>  # 使用中のプロセスID(PID)を指定して終了
```

#### Apacheの設定ファイルミス

:star:Apacheの設定に誤りがないか構文チェック

```bash
sudo httpd -t
```



## 6. 注意事項

ここまで読んでくださりありがとうございます。本記事はあくまでGoアプリケーションをAWS上でデプロイするまでの手順になります。セキュリティ対策（SSL/TLSの設定やファイアウォール、アクセス制御）については触れていません🙇