EC2にYellowfinのベストなテスト環境を構築するまでのメモ

テストや検証目的でAWSにYellowfinを構築することがよくあります。いろいろ試した結果、現時点で最適だと思える構築手順を公開します。

# AWSコンソールでの作業

まずはAWSコンソールでEC2のインスタンスを立ち上げます。

## EC2インスタンスの作成

今回の例ではAmazon Linux 2を選択します。
![cap001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/21e5154d-831e-9c48-a3d9-b1ad749a3e69.png)

インスタンスタイプはr5a.largeを選択。
![cap002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/9b9cf398-49ac-aa10-5891-d2124536a26d.png)

ストレージは64GBまで追加しておきます。
![cap003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/60dc77f2-c083-688f-9d65-c9e084100595.png)

ここでは事前に定義しておいたセキュリティグループを適用して、必要なポートを開けます。
![cap004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/cc930efc-2765-7233-84c2-7376d5ce8811.png)

キーペアも事前に作成しておいたものを使用します。
![cap005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/722a3529-05f4-a2ba-6800-84b049d8bbbb.png)

## Elastic IPアドレスの割り当て

固定IPも取得することにします。
![cap006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/5deb4d77-b1b0-7eb9-80d1-416c554a563d.png)

取得したIPアドレスを先ほど作成したインスタンスに関連付けます。
![cap007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/9909e3c7-c5fc-4141-7223-08edd04df456.png)

どのインスタンスに紐付けたIPかわかりやすくなるように、タグを設定しておきます。
<img width="828" alt="cap008.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/03171f72-70e0-ba66-62d6-f46ec80cb587.png">

# DNSの設定

割り当てたElastic IPを使ってドメインも設定しますが、ここでは割愛。
環境に合わせて適宜設定してください。

# SSHでの作業

ここから先はSSHで作業を進めます。

## SSHで接続

以下のコマンドで接続します。
`$ ssh -i <キーペアのpemファイル> ec2-user@<IPアドレスやドメイン名>`

## SELinuxとfirewalldの状態確認

Amazon LinuxはSELinuxとfirewalldがデフォルトで無効になっています。
一応、確認だけしておきます。

```
[ec2-user@xxxxxx ~]$ getenforce
Disabled
[ec2-user@xxxxxx ~]$ systemctl status firewalld.service
Unit firewalld.service could not be found.
```

## rootの有効化

デフォルトではrootパスワードが設定されていないので、以下で有効化します。

`sudo passwd root`

## タイムゾーン設定

タイムゾーンをAsia/Tokyoに。

`sudo timedatectl set-timezone Asia/Tokyo`

## ロケール設定

ロケールを日本語に。

`sudo localectl set-locale LANG=ja_JP.UTF-8`

## 日本語フォントのインストール

日本語フォントをインストールします。
（Yellowfin 9以降はOSフォントを使用しないため、この手順は不要になります）

`sudo yum install -y ibus-kkc`
`sudo yum install google-noto-sans-japanese-fonts ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts vlgothic-fonts vlgothic-p-fonts`

## ホスト名設定

任意のホスト名を設定します。

`sudo hostnamectl set-hostname ＜ホスト名＞`

## dstatのインストール

サーバーリソース監視ツールのdstatをインストールします。

`sudo yum install dstat`

## PostgreSQLのインストール


### リポジトリからインストール

Amazon Linux 2の標準のPostgreSQLはバージョンが古いので、現時点で最新の12を使うように小細工します。
まず、リポジトリパッケージをダウンロード。

`wget https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm`

ダウンロードしたリポジトリパッケージをインストール。

`sudo rpm -Uvh --nodeps pgdg-redhat-repo-latest.noarch.rpm`

現状では依存ライブラリのチェックでエラーになります。
回避のためにリポジトリを直接修正してしまいます。

`sudo sed --in-place -e "s/\$releasever/7/g" /etc/yum.repos.d/pgdg-redhat-all.repo`

そしてPostgreSQL 12をインストールします。

`sudo yum install -y postgresql12-server`

### 初期設定

文字コードのオプションを指定して初期化コマンドを実行。

`PGSETUP_INITDB_OPTIONS='--encoding=UTF-8 --locale=C' sudo /usr/pgsql-12/bin/postgresql-12-setup initdb`

サービスに登録。

`sudo systemctl enable postgresql-12`

サービスを起動。

`sudo systemctl start postgresql-12`

postgresユーザーのパスワードを設定。

`sudo passwd postgres`

postgresユーザーに切り替え。
以降の作業はpostgresユーザーで行います。

`su - postgres`

ロールのパスワードを変更します。

`psql -c "alter role postgres with password '＜postgresユーザーのパスワード＞'" ;`

パスワード（暗号化）認証で接続するようにpg_hba.confを編集します。

`vi /var/lib/pgsql/12/data/pg_hba.conf`

```conf:/var/lib/pgsql/12/data/pg_hba.conf（抜粋）
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
#local   all             all                                     peer
local   all             all                                     md5
# IPv4 local connections:
#host    all             all             127.0.0.1/32            ident
host    all             all             0.0.0.0/0            md5
# IPv6 local connections:
#host    all             all             ::1/128                 ident
host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege. 
#local   replication     postgres                                peer
#host    replication     postgres        127.0.0.1/32            ident
#host    replication     postgres        ::1/128                 ident
```

## PostgreSQLの簡易チューニング

PGTuneを使って適切な設定値を算出します。

https://pgtune.leopard.in.ua/

PostgreSQLのバージョンは12、OSはLinux、タイプはWebアプリケーション、メモリは1GB、ストレージはSSDを指定します。
<img width="936" alt="cap009.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/42a5bef9-ab1d-d21e-ac02-7f9748b39d63.png">


PostgreSQLはpostgresql.confを編集してメモリなどの設定を行っていましたが、バージョン9.4からALTER SYSTEMコマンドで設定できるようになりました。
コマンドの方が楽なのでALTER SYSTEMコマンドから設定します。
**ALTER SYSTEM**をコピーします。
<img width="926" alt="cap010.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/0cb2ce49-db0a-49dc-56dd-cf92e3e4f41d.png">


psqlを起動。

`psql`

先ほどの**ALTER SYSTEM**を実行します。

```sql:
ALTER SYSTEM SET
 max_connections = '200';
ALTER SYSTEM SET
 shared_buffers = '256MB';
ALTER SYSTEM SET
 effective_cache_size = '768MB';
ALTER SYSTEM SET
 maintenance_work_mem = '64MB';
ALTER SYSTEM SET
 checkpoint_completion_target = '0.7';
ALTER SYSTEM SET
 wal_buffers = '7864kB';
ALTER SYSTEM SET
 default_statistics_target = '100';
ALTER SYSTEM SET
 random_page_cost = '1.1';
ALTER SYSTEM SET
 effective_io_concurrency = '200';
ALTER SYSTEM SET
 work_mem = '655kB';
ALTER SYSTEM SET
 min_wal_size = '1GB';
ALTER SYSTEM SET
 max_wal_size = '4GB';
```

PostgreSQLはデフォルトではlocalhostからしか待ち受けをしていません。
外部からの接続も待ち受けるようにpsqlに続けて下記のコマンドを実行します。

```sql:
ALTER SYSTEM SET
 listen_addresses = '*';
```
psqlを終了し、postgresユーザーを抜けてec2-userに戻り、PostgreSQLを再起動します。

`sudo systemctl restart postgresql-12`

## httpdのインストール

今回の構成ではTomcatに直接アクセスするのではなく、Apacheを中継する形にします。
SSL証明書の設定・更新がApacheの方が楽にできるからです。

### リポジトリからインストール

`sudo yum install httpd mod_ssl`

### httpd.confの編集

`sudo vi /etc/httpd/conf/httpd.conf`

以下のようにコメントアウトしポート８０（httpのデフォルトポート）を無効にします。

```conf:/etc/httpd/conf/httpd.conf（抜粋）
#Listhen 80
```

### proxy-ajp.confの作成

httpdがYellowfinのTomcatと連携するための設定を行います。
proxy-ajp.confは存在しないので新規作成します。

`sudo vi /etc/httpd/conf.d/proxy-ajp.conf`

SECRET_KEYはAJP Connectorに接続するためのパスワードを指定します。
後述のserver.xmlの設定と合わせる必要があります。

```conf:/etc/httpd/conf.d/proxy-ajp.conf
ProxyRequests Off
ProxyPass / ajp://localhost:8009/ secret=SECRET_KEY timeout=1860
ProxyPassReverse / ajp://localhost:8009/ secret=SECRET_KEY
```

### SSLの設定

**/etc/httpd/crt**に証明書ファイルを配置し、ssl.confを編集します。
今回は証明書、認証キー、中間証明書を使用する例です。

`sudo vi /etc/httpd/conf.d/ssl.conf`

```conf:/etc/httpd/conf.d/ssl.conf（抜粋）
SSLCertificateFile /etc/httpd/crt/ssl_certificate.crt
SSLCertificateKeyFile /etc/httpd/crt/xxxxxxxxxx.key
SSLCACertificateFile  /etc/httpd/crt/IntermediateCA.crt
```

## Javaのインストール

今回はOpenJDKディストリビューションのAmazon Corretto 11をインストールします。

`sudo yum install java-11-amazon-corretto`

## Yellowfinのインストール

### インストーラの実行

事前に入手したインストーラを使用して、Yellowfinをインストールします。

`sudo java -jar yellowfin-x.x.x-xxxxxxxx-full.jar`

インストール先ディレクトリは**/usr/local/yellowfin**にします。
その他、インストール手順の詳細は[サポートサイトの記事](https://support.yellowfin.co.jp/hc/ja/articles/360001025448)を参照してください。

インストーラが終了したら、ファイルを展開するため一旦Yellowfinを起動し、起動後に再び停止します。

`sudo /usr/local/yellowfin/appserver/bin/startup.sh`
`sudo /usr/local/yellowfin/appserver/bin/shutdown.sh`

## Yellowfinのサーバー設定変更

### TomcatのNative Libraryのインストール

Tomcat Native LibraryをインストールしなくてもYellowfinは動作しますので不要な方は"依存ライブラリのインストール"と”Tomcat Native Libraryのビルド”はスキップしてください。
詳細は、[Apache Tomcat Native Library](http://tomcat.apache.org/native-doc/) を参照して下さい。

Yellowfinを起動すると標準出力に下記のようなメッセージが表示されます。

```text:標準出力(Catalina.outなど)
ss-May-yyyy hh:mi:ss.000 情報 [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent 商用環境に最適な性能を発揮する APR ベースの Tomcat ネイティブライブラリが java.library.path [/Users/nishikenichirou/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.] に存在しません。
```
これはTomcat Native Libraryがインストールされていないために出力されます。
Native LibraryはTomcatのhttpやSSL機能を強化、拡張するライブラリです。
詳細は、[Apache Tomcat Native Library](http://tomcat.apache.org/native-doc/) を参照して下さい。
今回はTomcatのWebサーバー機能は使用しないので直接は関係ありませんが、毎回起動時のログに出力されて気持ちが悪いのでNative Libraryをインストールします。
インストールすることでHTTP/2などが使用できるようになります。
設定の詳細は、[Apache Tomcat 9 Configuration Reference](https://tomcat.apache.org/tomcat-9.0-doc/config/index.html) を参照して下さい。

#### 依存ライブラリのインストール

Tomcat Native Libraryは環境毎にソースをコンパイルしてインストールする必要があります。
コンパイルに必要なツールとライブラリをインストールします。

`sudo yum install gcc openssl apr-devel openssl-devel`

#### Tomcat Native Libraryのビルド

Tomcat Native　Libraryのソースは、"/usr/local/yellowfin/appserver/bin/tomcat-native.tar.gz" に格納されています。
上記のファイルを解凍し、ビルド・インストールを行います。

```
# cd /usr/local/yellowfin/appserver/bin
# tar zxvf tomcat-native.tar.gz
# cd ./tomcat-native-xx.xx.xx-src/native/
# ./configure --with-java-home=/usr/lib/jvm/java-11-amazon-corretto.x86_64 #JDKのインストールディレクトリ \
              --prefix=/usr/local/yellowfin/appserver                      #Tomcatのインストールディレクトリ
# make && make install
```

### catalina.shの編集

`sudo vi /usr/local/yellowfin/appserver/bin/catalina.sh`

JAVA_HOME変数には自動的にインストール時のシステムデフォルトのJavaのパスが指定されています。
Javaをバージョンアップする度にシステムデフォルトのJavaのパスは変更されるため、その度にJAVA_HOMEの指定も変更する必要があります。
非常に面倒臭いのでシステムデフォルトのJavaのパスを探して自動的にセットするように小細工をします。
以下を既存の"JAVA_HOME=xxxxxx"の行と置き換えます。

```sh:/usr/local/yellowfin/appserver/bin/catalina.sh（抜粋）
pathtojava=$(readlink -e $(which java))
JAVA_HOME="${pathtojava%/*/*}"
```

最近のほとんどのOSはIPv4とIPv６の両方が使えますがIPv6を優先で使用します。
ネットワークの一部でIPv6が使えない場合は途中で迷子になって通信が遅くなったり、不安定になることがあります。
今回は通信をIPv4に限定します。
以下を**適切な位置に**追記します。

```sh:/usr/local/yellowfin/appserver/bin/catalina.sh（抜粋）
JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true"
```

### setenv.shの作成

Tomcatにはデフォルトでは有効になっていない機能があります。
一部の機能を有効にします。
setenv.shは存在しないので新規作成します。

`sudo vi /usr/local/yellowfin/appserver/bin/setenv.sh`

Tomcatが停止しない場合に強制停止できる機能を有効にします。

```sh:/usr/local/yellowfin/appserver/bin/setenv.sh
#!/bin/sh

CATALINA_PID=/var/run/catalina.pid
```
下記のようにforceオプションをつけることで強制的にTomcatを停止できます。
`shutdown.sh -force`

Tomcat Native Libraryの参照PATHを有効にします。
※Tomcat Native Libraryをインストールしていない場合はこの設定は不要です。

```sh:/usr/local/yellowfin/appserver/bin/setenv.sh
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CATALINA_HOME/lib
export LD_LIBRARY_PATH
```

### server.xmlの編集

`sudo vi /usr/local/yellowfin/appserver/conf/server.xml`

httpd(Apache)と連携するためのAJPプロトコルを有効にします。
各種パラメータも指定します。
以下を**適切な位置に**追記します。

TOMCAT_IP_ADDRESSには、指定したポート（下記の例であれば8009）でリッスンするために使用するアドレスを記述します。
今回は接続するWebサーバーが同一マシンで稼働しているので、TOMCAT_IP_ADDRESSに"127.0.0.1"を指定します。
別サーバーから参照される場合は、Yellowfinサーバーに割り当てられたパブリックまたはプライベートのIPアドレスを指定します。

SECRET_KEYはAJP Connectorに接続するためのパスワードを指定します。
前述のproxy-ajp.confの設定と合わせる必要があります。

```xml:/usr/local/yellowfin/appserver/conf/server.xml（抜粋）
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"
               maxThreads="500" minSpareThreads="25"
               enableLookups="false" acceptCount="100"
               connectionTimeout="20000"
               URIEncoding="UTF-8" 
               maxPostSize="10485760" maxParameterCount="50000"
               address="TOMCAT_IP_ADDRESS"
               secretRequired="true" secret="SECRET_KEY"
    />
```

8080ポートのhttpを無効にします。
下記のようにコメントアウトします。


```xml:/usr/local/yellowfin/appserver/conf/server.xml（抜粋）
<!--
     <Connector port="8080" protocol="HTTP/1.1" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true"
               URIEncoding="UTF-8" compression="on" compressionMinSize="512"
               noCompressionUserAgents="gozilla, traviata"
               compressibleMimeType="text/html,application/x-javascript,text/css,application/javascript,text/javascript,text/plain,text/xml,application/json,application/vnd.ms-fontobject,application/x-font-opentype,application/x-font-truetype,application/x-font-ttf,application/xml,font/eot,font/opentype,font/otf,image/svg+xml,image/vnd.microsoft.icon,image/svg,text/comma-separated-values,application/csv,application/vnd.yellowfin.api-v1+json" 
               useSendfile="false" />
-->
```


### log4j.propertiesの編集

`sudo vi /usr/local/yellowfin/appserver/webapps/ROOT/WEB-INF/log4j.properties`

コンテンツのクエリSQLをログに残すようにします。
以下を追記します。

```properties:/usr/local/yellowfin/appserver/webapps/ROOT/WEB-INF/log4j.properties（抜粋）
# Report execution Log
log4j.appender.reportlog=org.apache.log4j.RollingFileAppender
log4j.appender.reportlog.File=${catalina.home}/logs/report_exec.log
log4j.appender.reportlog.MaxFileSize=1024KB
log4j.appender.reportlog.MaxBackupIndex=9
log4j.appender.reportlog.layout=org.apache.log4j.PatternLayout
log4j.appender.reportlog.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} (%M) - %m%n

log4j.category.com.hof.mi.process.MIReportGeneratorProcess=DEBUG,reportlog
log4j.category.com.hof.mi.util.background.QueueItem=DEBUG,reportlog
log4j.category.com.hof.adapter.JDBCEventCreation=DEBUG,reportlog
# set additivity to false so it doesn't also log to standard log files
log4j.additivity.com.hof.mi.process.MIReportGeneratorProcess=false
log4j.additivity.com.hof.mi.util.background.QueueItem=false
log4j.additivity.com.hof.adapter.JDBCEventCreation=false
```

### web.xmlの編集

`sudo vi /usr/local/yellowfin/appserver/webapps/ROOT/WEB-INF/web.xml`

リポジトリDBとの接続のタイムアウトを設定して、時間のかかるコンテンツも表示できるようにします。
以下を<servlet-name>InitConnectionPool</servlet-name>の<servlet>セクション内に追記します。

```xml:/usr/local/yellowfin/appserver/webapps/ROOT/WEB-INF/web.xml（抜粋）
    <init-param>
      <param-name>JDBCTimeout</param-name>
      <param-value>1800</param-value>
    </init-param>
```

## カーネルのチューニング

### yellowfin.confの作成

`sudo vi /etc/sysctl.d/yellowfin.conf`

TCPの受信及び送信のソケットの上限を増やします。
```conf:/etc/sysctl.d/yellowfin.conf
net.core.wmem_max=10485760
net.core.rmem_max=10485760
```

## Yellowfinのサービス登録

### yellowfin.serviceの作成

`sudo vi /etc/systemd/system/yellowfin.service`

```conf:/etc/systemd/system/yellowfin.service
[Unit]
Description=Yellowfin
After=syslog.target network.target postgresql-12.service
#After=network.target

[Service]
Type=forking
#User=yellowfin
#Group=yellowfin
ExecStart=/usr/local/yellowfin/appserver/bin/startup.sh
ExecStop=/usr/local/yellowfin/appserver/bin/shutdown.sh
WorkingDirectory=/usr/local/yellowfin
#RemainAfterExit=yes
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias=yellowfin.service
```

yellowfin.service内のファイルディスクリプタ数の上限を設定します。
`sudo mkdir /etc/systemd/system/yellowfin.service.d`
`sudo vi /etc/systemd/system/yellowfin.service.d/limits.conf`

```conf:/etc/systemd/system/yellowfin.service.d/limits.conf
[Service]
# Max open files
LimitNOFILE=65536
# Max processes
LimitNPROC=65536
```

### Yellowfinのサービス起動

`sudo systemctl start yellowfin`

## Yellowfinの接続確認

httpdをここで再起動しておきます。

`sudo systemctl restart httpd`

その後にインストールしたYellowfinのURLにブラウザでアクセスし、正しく表示されればOKです。

### httpdとYellowfinのサービスの自動起動

既に自動起動設定がされていることもありますが、念のために自動起動を有効にします。

`sudo systemctl enable httpd`
`sudo systemctl enable yellowfin`

## OSの再起動

カーネルパラメータの変更やサービスの自動起動の設定をしたので、OSを再起動し正しくOS及びYellowfinが起動できるか確認します。

## Yellowfinの初期設定

[サポートサイトの記事](https://support.yellowfin.co.jp/hc/ja/articles/360001025528)に従って初期設定を行ってください。