【Webアプリ環境構築】GCPインスタンス作成～Webアプリ起動

# はじめに
ポートフォリオの環境構築をGCPで行うことが2度目なので、
今後も行うことを想定して備忘録として全体の流れをまとめておきます。

駄文ですが、部分的でもどなたかのお役に立てれば幸いです。


[ポートフォリオ](https://qiita.com/Takthustra/items/b08a65584b4bbd632800)
[ポートフォリオ第二弾](https://qiita.com/Takthustra/items/c651636b874e2602841b)

# 全体の流れ

Springをwarファイルにビルドしたものを、
GCPで生成したCentOS上にTomcatとmariadbを構築して動かします。

* GCPのインスタンス生成
* Tomcatインストール
* ポートフォワーディングの設定
* mariadbインストール
* タイムゾーンの設定
* セッションタイムアウトの設定

# GCPのインスタンス生成
インストールする際の各種設定（無料枠について,os,firewall,ネットワーク等）
userのパスワード変更してsshできるように設定

参考:
[(2023年版) Google Cloud 無料枠　ホントに課金されない？の検証](https://se-abeaver.com/gcp-free-tier/)

`インスタンスを作成`をクリック
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/923980d0-4c42-8746-9e68-9880af11a104.png)

`リージョン`を無料枠のオレゴン・アイオワ・サウスカロライナのいずれか選択
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/7c594300-22a0-bb0e-6aac-8cd49891f3d0.png)

`マシンタイプ`を無料枠の**e2-micro**に選択
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/e6c6f4f4-7a34-8787-a8d8-25c12468b0c7.png)

`ブートディスク`のタイプを無料枠の**標準永続ディスク**に変更
サイズを無料枠の**30GB以下**に変更
イメージを無料枠の好きなOSに変更（私はCentOS7にしました）
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/d113540b-1db3-c3cc-8a6c-f3ba8ff552ab.png)

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/67ac46fc-a202-83b2-b243-3378739aafeb.png)

`ファイアウォール`のHTTP,HTTPSトラフィックの許可(80ポートの許可)
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/51853407-ae59-e346-99c3-2975fa38879d.png)

## 詳細オプション

### 静的外部IPの設定をする場合
`詳細オプション→ネットワーキング`を開く
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/f5147459-e731-4906-86db-06253a4c60c9.png)

`ネットワークインターフェース`を開く
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/7c62bf7f-d38a-0182-213c-4a889cb2ff08.png)

`外部IPv4アドレス`を開く
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/0f653985-e9ce-1a06-6724-0cfae8c159cb.png)

`静的IPv4アドレス`を開く
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/51d30a9a-4aca-7a95-ce0c-198fa55bbdb2.png)

任意の名前を入力
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/feb62b1d-a395-e71a-2e51-7f28b834c75d.png)

静的IPv4アドレスの登録完了
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/2ccfe293-7c73-149a-b4ff-86b7aa653405.png)



### TeraTermなどでSSH通信する場合
`詳細オプション→セキュリティ`を開く
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/633a5ed3-fd46-bbea-a24b-126e21e9412a.png)

`アクセスを管理する`を開く
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/70fd54c8-1eb6-dcdc-8a86-40c068f5b8f8.png)

`手動で生成したSSH認証鍵の追加`から`項目の追加`を押して、
自分が生成した公開鍵を追加。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/83a45c37-19c4-6c93-387d-1038939ab749.png)

`作成`を押すとインスタンスが作られる
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/07a88c51-68ef-7965-61c3-51b3bfaf9aac.png)

`VMインスタンス`から`SSH`をクリックしてサーバーに接続する
![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3528591/3521a4d4-cd26-2611-6123-556f9338da5b.png)


# JDK17インストール
参考:下記手順をsudoコマンドで実行します。
[CentOS 7にOpenJDK 17をインストール（Azul Zulu Builds of OpenJDK）](https://qiita.com/witchcraze/items/014da8ee2c638737e9bc)

# Tomcatインストール
参考:
[Apache Tomcat 9 を CentOS 7 にインストールする手順](https://weblabo.oscasierra.net/installing-tomcat9-centos7-1/)
[Apache Tomcatをインストールしたらすぐに設定したいセキュリティ対策](https://weblabo.oscasierra.net/java-tomcat-security/)

Tomcat を動かすための専用ユーザとして tomcat を追加します。
```:CentOS7
# useradd -s /sbin/nologin tomcat
```

tar.gz 形式の Apache Tomcat 10 本体をダウンロードします。
（現在の最新版は11だが、Webアプリの作成環境が10なのでこちらを使用）

```:CentOS7
# cd ~
# curl -O http://ftp.riken.jp/net/apache/tomcat/tomcat-10/v10.1.13/bin/apache-tomcat-10.1.13.tar.gz
```

ダウンロードした tar.gz ファイルを解凍して配置します。
tar コマンドで解凍して、今回は /opt に配置します。 
また、解凍した Apache Tomcat の所有者を作成したtomcatユーザの所有とします。

```sh:CentOS7
$ tar -xzvf ~/apache-tomcat-10.1.13.tar.gz
$ sudo mv ~/apache-tomcat-10.1.13 /opt
$ sudo chown -R tomcat:tomcat /opt/apache-tomcat-10.1.13
```

Apache Tomcat 10 をサービスとして登録します。
まずは /etc/systemd/system/tomcat.service を新たに作成して、
つぎのように記述して保存します。

```sh:CentOS7
$ sudo vi /etc/systemd/system/tomcat.service
```

```:CentOS7(tomcat.service)
[Unit]
Description=Apache Tomcat 10
After=network.target

[Service]
User=tomcat
Group=tomcat
Type=oneshot
PIDFile=/opt/apache-tomcat-10.1.13/tomcat.pid
RemainAfterExit=yes

ExecStart=/opt/apache-tomcat-10.1.13/bin/startup.sh
ExecStop=/opt/apache-tomcat-10.1.13/bin/shutdown.sh
ExecReStart=/opt/apache-tomcat-10.1.13/bin/shutdown.sh;/opt/apache-tomcat-10.1.13/bin/startup.sh

[Install]
WantedBy=multi-user.target
```

作成した定義ファイルの権限を 755 に変更。
```sh:CentOS7
$ sudo chmod 755 /etc/systemd/system/tomcat.service
```

定義ファイルの作成が完了したら、
systemctl enable コマンドでサービスを有効にします。
```sh:CentOS7
$ sudo systemctl enable tomcat
```

Apache Tomcat 10 を起動してみます。
`Active: active`と表示されたらOK。
```sh:CentOS7
$ sudo systemctl start tomcat
$ sudo systemctl status tomcat

● tomcat.service - Apache Tomcat 10
   Loaded: loaded (/etc/systemd/system/tomcat.service; enabled; vendor preset: disabled)
   Active: active (exited) 
```

firewalledの設定のため、一度stopさせます。
```sh:CentOS7
$ sudo systemctl stop tomcat
```

Firewalldで8080番を有効に設定します。
/etc/firewalld/services/ フォルダに tomcat.xml を作成します。
```sh:CentOS7
$ sudo vi /etc/firewalld/services/tomcat.xml

//下記内容をコピペ
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>Apache Tomcat 10</short>
  <description>Apache Tomcat 10</description>
  <port protocol="tcp" port="8080"/>
</service>
```

Firewalld が現時点で通信を許可しているサービスは、`firewall-cmd`コマンドで確認できます。
確認するとdhcpv6-client と ssh のサービスが許可されていることが分かります。
```sh:CentOS7
$ sudo firewall-cmd --list-services --zone=public --permanent
dhcpv6-client ssh
```

tomcat の通信を許可するように、次のように firewall-cmd を実行します。
```sh:CentOS7
$ sudo firewall-cmd --add-service=tomcat --zone=public --permanent
$ sudo firewall-cmd --reload
```

もう一度、現在通信を許可しているサービスを確認すると、
今度は tomcat が追加されたことが確認できます。
```sh:CentOS7
$ sudo firewall-cmd --list-services --zone=public --permanent
dhcpv6-client ssh tomcat
```

以下はやらなくても可。

GET と POST 以外のメソッドを拒否する設定をします。
Tomcatのホームディレクトリ配下の conf/web.xml (特定のアプリケーションだけ設定したい場合は、アプリケーション内のweb.xml)に`<http-method>`タグで拒否したいメソッドを列挙します。

```sh:conf/web.xml
##　以下をコピペ
<security-constraint>
    <web-resource-collection>
        <web-resource-name>deny method</web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method>HEAD</http-method>
        <http-method>PUT</http-method>
        <http-method>DELETE</http-method>
        <http-method>OPTIONS</http-method>
        <http-method>TRACE</http-method>
        <http-method>CONNECT</http-method>
        <http-method>PATCH</http-method>
        <http-method>PROPFIND</http-method>
        <http-method>PROPPATCH</http-method>
        <http-method>MKCOL</http-method>
        <http-method>COPY</http-method>
        <http-method>MOVE</http-method>
        <http-method>LOCK</http-method>
        <http-method>UNLOCK</http-method>
    </web-resource-collection>
    <auth-constraint/>
</security-constraint>
```

`apatch-tomcat/webapps`にwarファイルを配置します。
tomcatにリスタートをかけます。
```sh:CentOS7
$ sudo systemctl restart tomcat
```

アクセスできたら完了です。

# ポートフォワーディングの設定
私の環境だとTomcatを80番ポートに設定できないので、
ポートフォワーディングで疑似的に80番ポートのアクセスを受け付けます。

参考:
[firewalld で ポートフォワーディング](https://qiita.com/reefIrving/items/121eecb6e8374d02ba65)

80番ポートのアクセスを8080番に転送しています。
```sh:CentOS7
$ sudo firewall-cmd --add-forward-port=port=80:proto=tcp:toport=8080
#設定の永久化
$ sudo firewall-cmd --runtime-to-permanent
```

これでIPアドレスやドメインから直接サインインすることが可能になりました。

# mariadb
参考:
[初心者向け CentOS7.9 に MariaDB 10.6をインストール](https://shimaichi.blog/centos7_9-mariadb10_6/)
[Download MariaDB Server(公式)](https://mariadb.org/download/?t=repo-config&d=CentOS+7&v=11.2+%5BRC%5D&r_m=yamagata-university)

公式の手順に従って行います。

まずRepositoryを作ります。

```sh:CentOS7
$ sudo vi /etc/yum.repos.d/MariaDB.repo

#以下内容コピペ

# MariaDB 11.1 CentOS repository list - created 2023-08-29 12:25 UTC
# https://mariadb.org/download/
[mariadb]
name = MariaDB
# rpm.mariadb.org is a dynamic mirror if your preferred mirror goes offline. See https://mariadb.org/mirrorbits/ for details.
# baseurl = https://rpm.mariadb.org/11.1/centos/$releasever/$basearch
baseurl = https://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb/yum/11.1/centos/$releasever/$basearch
module_hotfixes = 1
# gpgkey = https://rpm.mariadb.org/RPM-GPG-KEY-MariaDB
gpgkey = https://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb/yum/RPM-GPG-KEY-MariaDB
gpgcheck = 1
```

mariadbのインストールを実行します。
```sh:CentOS7
$ sudo yum install MariaDB-server MariaDB-client

#ひたすらyを押して、以下が出たら完了
Replaced:
  mariadb-libs.x86_64 1:5.5.68-1.el7

Complete!
```

mariadbを実行します。
```sh:CentOS7
$ sudo systemctl enable mariadb
$ sudo systemctl start mariadb
$ sudo systemctl status mariadb

#以下表示ならOK
Active: active (running)
```

mariadbの最低限の設定を行います。
```sh:CentOS7
$ sudo mariadb -u root -p
Enter password: #そのままEnterキーを押す

#パスワードを設定
mariaDB[(none)]> ALTER USER 'root'@'localhost' IDENTIFIED BY "ここにパスワードを設定";
```

この後はWebアプリで使用するデータベースを構築するだけです。

# タイムゾーンの設定
参考:
[タイムゾーンを日本標準時(JST)に変更する CentOS 8, 7](https://centos.bungu-do.jp/archives/67)

私の作成したWebアプリでは日時を表示する機能があります。
そのため、Linuxのタイムゾーンを東京に変えます。

timedatectlコマンドでタイムゾーンを東京に設定します。
```sh:CentOS7
$ sudo timedatectl set-timezone Asia/Tokyo
$ sudo timedatectl status

#以下が出力される
      Local time: Wed 2023-08-30 16:30:52 JST
  Universal time: Wed 2023-08-30 07:30:52 UTC
        RTC time: Wed 2023-08-30 07:30:52
       Time zone: Asia/Tokyo (JST, +0900)
     NTP enabled: yes
NTP synchronized: yes
 RTC in local TZ: no
      DST active: n/a
```

mariadbを再起動してタイムゾーンを更新します。
```sh:CentOS7
$ sudo systemctl restart mariadb
$ mariadb -u root -p

MariaDB [(none)]> select @@time_zone; select @@system_time_zone;
+-------------+
| @@time_zone |
+-------------+
| SYSTEM      |
+-------------+
1 row in set (0.000 sec)

+--------------------+
| @@system_time_zone |
+--------------------+
| JST                |
+--------------------+
1 row in set (0.000 sec)
```

JSTに変更できました。


# 余談
## 500エラーの事例
Webアプリを動かすと500エラーが発生したので事例を載せておきます。


<h5>そもそもどこのログを見たら良いか</h5>
500エラーが発生した場合、エラーページでは詳細が分かりません。
Tomcatでは`apache-tomcat/logs/catalina.out`で分かります。

<h5>小文字、大文字の統一</h5>

```shell-session:catalina.out
org.springframework.jdbc.BadSqlGrammarException: 
PreparedStatementCallback; bad SQL grammar [SELECT NAME FROM USER WHERE NAME = ?]
...
Caused by: java.sql.SQLSyntaxErrorException:
(conn=31) Table 'learning_stopwatch.USER' doesn't exist
```
データベースの全ての定義を`小文字`で行っているにも関わらず、
JavaのSQL文に一部、`大文字`で指示している部分がありました。
`小文字`に修正すれば動きました。

<h5>文字コード</h5>

参考:
[文字コードに関する設定を記述する](https://www.javadrive.jp/mariadb/myini/index3.html#section1)
[MySQLのテーブル作成後に、文字コードをutf8mb4に変更する](https://www.karakaram.com/changing-the-character-set-to-utf8mb4-after-creating-mysql-table/#alter-database)

```shell-session:catalina.out
o.m.jdbc.message.server.ErrorPacket:
Error:1366-22007:Incorrect string value:'\xE5\xAE\x8C\xE6\x88\x90...' for column
```
insert intoした文字列が、
サポートする文字コードと合わない場合に表示されるエラーのようです。



mariadbの文字コードを確認してみます。
```sh:mariadb
MariaDB [(none)]> SHOW VARIABLES LIKE 'chara%';
+--------------------------+------------------------------+
| Variable_name            | Value                        |
+--------------------------+------------------------------+
| character_set_client     | utf8mb3                      |
| character_set_connection | utf8mb3                      |
| character_set_database   | latin1                       |
| character_set_filesystem | binary                       |
| character_set_results    | utf8mb3                      |
| character_set_server     | latin1                       |
| character_set_system     | utf8mb3                      |
| character_sets_dir       | /usr/share/mariadb/charsets/ |
+--------------------------+------------------------------+
```

クライアントに関係する部分は以下の3つのようです。
全てutf8なので問題なさそうです。

>character_set_client
クライアントからSQL文などが送信される時に使用される文字コード

>character_set_connection
クライアント側から受け取った文字を変換する文字コード

>character_set_results
クライアントへ結果を送信する時に使用される文字コード

続いて、データベースとテーブルの文字コードと照合順序を確認してみます。
```sh:mariadb
#データベース
MariaDB [(none)]> 
SELECT SCHEMA_NAME,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME 
FROM INFORMATION_SCHEMA.SCHEMATA 
WHERE SCHEMA_NAME = 'learning_stopwatch';

+--------------------+----------------------------+------------------------+
| SCHEMA_NAME        | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME |
+--------------------+----------------------------+------------------------+
| learning_stopwatch | latin1                     | latin1_swedish_ci      |
+--------------------+----------------------------+------------------------+

#テーブル
MariaDB [(none)]> 
SELECT TABLE_NAME,TABLE_COLLATION
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='learning_stopwatch';
+---------------------+-------------------+
| TABLE_NAME          | TABLE_COLLATION   |
+---------------------+-------------------+
| daily_learning_time | latin1_swedish_ci |
| user                | latin1_swedish_ci |
+---------------------+-------------------+
```

データベース、テーブルの文字コードは`latin1`になっていますね。

mariadbの文字コードをもう一度確認すると、
クライアントから受け取る文字コードは`utf8mb3`ですが、
データベースの文字コードは`latin1`になっています。
データベースの文字コードを`utf8mb3`に変更すれば動きそうです。

```sh:mariadb
MariaDB [(none)]> SHOW VARIABLES LIKE 'chara%';
+--------------------------+------------------------------+
| Variable_name            | Value                        |
+--------------------------+------------------------------+
| character_set_client     | utf8mb3                      |
| character_set_connection | utf8mb3                      |
| 'character_set_database' | 'latin1'                     |
| character_set_filesystem | binary                       |
| character_set_results    | utf8mb3                      |
| character_set_server     | latin1                       |
| character_set_system     | utf8mb3                      |
| character_sets_dir       | /usr/share/mariadb/charsets/ |
+--------------------------+------------------------------+
```

データベースとテーブルの文字コードを変更します。
```sh:mariadb
#まずはデータベースから変更
MariaDB [(none)]> 
ALTER DATABASE learning_stopwatch
CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;

Query OK, 1 row affected (0.035 sec)

MariaDB [(none)]> 
SELECT SCHEMA_NAME,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME     
FROM IMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME = 'learning_stopwatch';
+--------------------+----------------------------+------------------------+
| SCHEMA_NAME        | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME |
+--------------------+----------------------------+------------------------+
| learning_stopwatch | utf8mb4                    | utf8mb4_general_ci     |
+--------------------+----------------------------+------------------------+
#つづいてテーブルを変更
MariaDB [learning_stopwatch]>
ALTER TABLE user
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;

Query OK, 1 row affected (0.217 sec)
Records: 1  Duplicates: 0  Warnings: 0

MariaDB [learning_stopwatch]>
ALTER TABLE daily_learning_time
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;

Query OK, 0 rows affected (0.029 sec)
Records: 0  Duplicates: 0  Warnings: 0

MariaDB [learning_stopwatch]>
SELECT TABLE_NAME,TABLE_COLLATION
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='learning_stopwatch';
+---------------------+--------------------+
| TABLE_NAME          | TABLE_COLLATION    |
+---------------------+--------------------+
| user                | utf8mb4_general_ci |
| daily_learning_time | utf8mb4_general_ci |
+---------------------+--------------------+
```
これで正常に稼働しました。

## セッションタイムアウトの設定
参考:
[Spring Boot - セッションタイムアウト時間の設定方法
](https://qiita.com/penguinlab/items/f69b3b17609e1fd64a01)

#### warビルドの場合
web.xml を作成して設定する。
```web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

</web-app>
```
>TOMCAT_HOME/conf/web.xml でアプリケーションサーバに対するデフォルトのセッションタイムアウト設定することが可能。ただし、ほとんどの場合はアプリケーション個別にセッションタイムアウトを設定したいことのほうが多いと思う。その場合は、アプリケーションプロジェクトに src/main/webapp/WEB-INF/web.xml を作成する。

# おわりに
どなたかの参考になれば幸いです。