Botpressで最近流行りのチャット・ボットを始めてみようと、ローカルへインストールしたりコンテナに立てて遊んだりしていたのですが、どうせなら作ったBotをサービスとしてパブリックに公開したいと思い、サーバ上でBotpressをセットアップしました。
セットアップ手順が若干難しい部分もありましたので、備忘録として、セットアップ手順を残そうと、記事の執筆に至った次第です。
概要
本記事の概要を以下に記載します。
本記事の概要
本番利用(個人利用の範囲)を想定したBotpressの基盤環境を構築します。
また、作成したBotをサービスとして公開する為、安定した運用が目指し、Botpressを導入するサーバには、Amazon Lightsailを採用します。
機能要件
- 基盤環境
- サーバはAmazon Lightsailを使用する。
- OS選定は右記の通り:プラットフォーム「Linux/Unix」、設計図「Nginx」
- 個人管理の為、コスト面から最低スペックでインスタンスを構築する。
- DNS管理はLightsailの標準機能であるDNSゾーンではなく、Route53にて行う。
- 基盤構築に関してはCloudFormationを使用し、スタックによる管理を行う。
- Botpress
- チャット・ボットの開発プラットフォームとしてBotpressを使用する。
- 言語サーバはBotpress社の提供しているサーバを使用する。
- OS構築はコンソール上にて、コマンド操作で行う。
※将来的には、Ansible等の自動化ツールによるOS構築の自動化を目指す
事前準備
本記事の手順を進めるにあたり、事前に以下の要件を満たして多く必要がある。
- 任意のプロパイダーでドメインを取得済であり、Route53にてパブリックホストゾーンが作成済であること
基盤構築
Lightsailインスタンスの構築
以下のCFnテンプレートを使用し、環境構築を行います。
- CloudFormationテンプレート
- lightsail.yml
Lightsailインスタンスを構築 - route53.yml
Route53のパブリックホストゾーンへサブドメインを登録する
- lightsail.yml
CloudFormationテンプレート – lightsail.yml
AWSTemplateFormatVersion: "2010-09-09"
Description:
"Launch a PJ-chibiBot-Botpress-Service-to-Lightsail"
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Parameters:
- InstanceName
- InstanceType
- ImageType
- AvailabilityZone
- SIPName
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
PJPrefix:
Type: "String"
InstanceName:
Type: "String"
Description: "Enter the name of your lightsail instance."
Default: "<インスタンス名を指定>"
InstanceType:
Type: "String"
Description: "Allowed Lightsail instance type."
Default: "nano_2_0"
ConstraintDescription: "Please choose a valid instance type."
ImageType:
Type: "String"
Description: "Allowed Lightsail Images."
Default: "nginx"
ConstraintDescription: "Please choose a valid OS image type."
AvailabilityZone:
Type: "String"
Description: "Select the AZ where you want to run the instance."
Default: "ap-northeast-1a"
SIPName:
Type: "String"
Description: "Enter the name of your lightsail StaticIp"
Default: "<StaticIPAddressの名前を指定>"
### Resources ###
Resources:
# ------------------------------------------------------------#
# Lightsail Instace
# ------------------------------------------------------------#
# Lightsailインスタンスの作成
LightsailInstance:
Type: "AWS::Lightsail::Instance"
Properties:
# インスタンスを実行するAZ
AvailabilityZone: !Ref "AvailabilityZone"
# 自動スナップショットの作成を有効にする
AddOns:
- AddOnType: "AutoSnapshot"
# デフォルトでは有効にしておく
Status: "Disabled"
# 立ち上げるOSイメージ
BlueprintId: !Ref "ImageType"
# インスタンスのサイズ
BundleId: !Ref "InstanceType"
# インスタンスの名前
InstanceName: !Ref "InstanceName"
## Lightsailのファイアウォール設定。 必要に応じて更新してください。
## ポートとIPアドレスから許可リストを作成
## https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-instance-port.html
#Port:
# AccessDirection: String
# AccessFrom: String
# AccessType: String
# CidrListAliases:
# - String
# Cidrs:
# - String
# CommonName: String
# FromPort: Integer
# Ipv6Cidrs:
# - String
# Protocol: String
# ToPort: Integer
# ------------------------------------------------------------#
# Lightsail StaticIp
# ------------------------------------------------------------#
StaticIP:
Type: AWS::Lightsail::StaticIp
Properties:
AttachedTo: !Ref LightsailInstance
StaticIpName: !Ref SIPName
# ------------------------------------------------------------#
# Output Parameter
# ------------------------------------------------------------#
Outputs:
### StaticIPAddress ###
StaticIP:
Value: !GetAtt StaticIP.IpAddress
Export:
Name: !Sub "${PJPrefix}-Lightsail-StaticIP"
loudFormationテンプレート – route53.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: "Launch a PJ-chibiBot-Botpress-Service-to-Route53"
# ------------------------------------------------------------
# Metadate
# ------------------------------------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Parameters:
- PJPrefix
- HostedZoneID
- SubDomain
# ------------------------------------------------------------
# Input Parameters
# ------------------------------------------------------------
Parameters:
### Project Prefix ###
PJPrefix:
Type: String
### Hosted Zone ###
HostedZoneID:
Type: String
Default: '<ホストゾーンのホストゾーンIDを指定>'
SubDomain:
Type: String
Default: '<サブドメイン>.<ドメイン>(例:www.example.com)'
### Resources ###
Resources:
# ------------------------------------------------------------
# Route53
# ------------------------------------------------------------
### Alias ALB ARN ###
DNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref HostedZoneID
Comment: "Add New RecordSet"
Name: !Ref SubDomain
Type: A
TTL: '300'
ResourceRecords:
- { "Fn::ImportValue": !Sub "${PJPrefix}-Lightsail-StaticIP" }
Botpress構築手順
LightsailインスタンスへSSH接続
作成したインスタンスへSSH接続します。
Bitnamiのアプローチタイプを判定
Bitnamiのアプローチタイプを判定します(アプローチA, アプローチB)
$ test ! -f "/opt/bitnami/common/bin/openssl" && echo "Approach A: Using system packages." || echo "Approach B: Self-contained installation."
Approach A: Using system packages.
詳細:https://docs.bitnami.com/aws/faq/get-started/understand-upcoming-changes/
今回はアプローチAでしたので、その前提で以下の手順を実行していきます。
不要なプロセスの停止
nginxにデフォルトで入っている不要なプロセスを停止します。
$ sudo /opt/bitnami/ctlscript.sh stop php-fpm
$ sudo /opt/bitnami/ctlscript.sh stop mariadb
$ sudo mv /etc/monit/conf.d/php-fpm.conf /etc/monit/conf.d/php-fpm.conf.disabled
$ sudo mv /etc/monit/conf.d/mariadb.conf /etc/monit/conf.d/mariadb.conf.disabled
$ sudo gonit reload
Let’s EncriptによるSSL設定
SSL設定を行います。
なぜWebサービスでもないのに、SSL化するのかというと、SSL設定作成したBotをSlackやTeams等と連携する際に、URLがHTTPSであることが求めらる為です。
また、SSL化にあたり、Let’s Encryptのクライアント lego を使用します。
SSL証明書のダウンロード
Let’s Encryptのクライアント legoをダウンロードします。
$ curl -Ls https://api.github.com/repos/xenolf/lego/releases/latest | grep browser_download_url | grep linux_amd64 | cut -d '"' -f 4 | wget -i -
SSL証明書のセットアップ
ダウンロードしたファイルを解凍します。
※本記事では「lego_v4.6.0_linux_amd64.tar.gz」
$ tar xvzf lego_v4.6.0_linux_amd64.tar.gz
$ sudo mkdir -p /opt/bitnami/letsencrypt
$ sudo mv lego /opt/bitnami/letsencrypt/lego
SSL証明書のインストール
解凍したlegoを使用してSSL証明書をサーバへインストールします。
まず、nginxを止めます。
$ sudo /opt/bitnami/ctlscript.sh stop nginx
Letsencript(lego)を実行します。
※ 実行中に「Do you accept the TOS? Y/n」と聞かれた際は、「y」と入力して「Enter」して下さい。
# Letsencriptを実行 ※以下の部分を環境に合わせて変更して下さい。
# 貴方のメールアドレス⇒【hoge@example.com】/ サイトのドメイン名⇒【www.example.com、もしくわexample.com】
$ sudo /opt/bitnami/letsencrypt/lego --tls --email="貴方のメールアドレス" --domains="サイトのドメイン名" --path="/opt/bitnami/letsencrypt" run
作成された証明書をセットアップします。
デフォルトの証明書をoldにして、発行した証明書にシンボリックリンクを貼ります。
# デフォルトの証明書をold化
$ sudo mv /opt/bitnami/nginx/conf/bitnami/certs/server.crt /opt/bitnami/nginx/conf/bitnami/certs/server.crt.old
$ sudo mv /opt/bitnami/nginx/conf/bitnami/certs/server.key /opt/bitnami/nginx/conf/bitnami/certs/server.key.old
$ sudo mv /opt/bitnami/nginx/conf/bitnami/certs/server.csr /opt/bitnami/nginx/conf/bitnami/certs/server.csr.old
# シンボリックリンクを作成
$ sudo ln -sf /opt/bitnami/letsencrypt/certificates/サイトのドメイン名.key /opt/bitnami/nginx/conf/bitnami/certs/server.key
$ sudo ln -sf /opt/bitnami/letsencrypt/certificates/サイトのドメイン名.crt /opt/bitnami/nginx/conf/bitnami/certs/server.crt
# 発行した証明書のパーミッションを変更する
$ sudo chown root:root /opt/bitnami/nginx/conf/bitnami/certs/server*
$ sudo chmod 600 /opt/bitnami/nginx/conf/bitnami/certs/server*
nginxを起動します。
$ sudo /opt/bitnami/ctlscript.sh start nginx
証明書の自動更新
Let’s Encriptの証明書有効期限は3ヶ月となっています。
毎月手動で更新するのは面倒なので、自動で証明書を更新してくれるように設定します。
$ sudo mkdir -p /opt/bitnami/letsencrypt/scripts
$ sudo vi /opt/bitnami/letsencrypt/scripts/renew-certificate.sh
shの中身は以下の通りとなっています。
#!/bin/bash
sudo /opt/bitnami/ctlscript.sh stop nginx
sudo /opt/bitnami/letsencrypt/lego --tls --email="自分のメールアドレス" --domains="適用したドメイン" --path="/opt/bitnami/letsencrypt" renew --days 90
sudo /opt/bitnami/ctlscript.sh start nginx
更新スクリプトが、cronで定期実行されるようにします。
$ sudo chmod +x /opt/bitnami/letsencrypt/scripts/renew-certificate.sh
$ sudo crontab -e
末尾に以下コードを追記します。
0 0 1 * * /opt/bitnami/letsencrypt/scripts/renew-certificate.sh 2> /dev/null
スワップ領域の作成
スワップ領域を作成します。(確かデフォルトだとスワップ領域無かったので。。)
スワップ領域の目安としては、メモリの2倍。(ここではメモリ512Mとして、スワップは1GB)
$ sudo dd if=/dev/zero of=/swapfile bs=1M count=1024
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ sudo vi /etc/fstab
「/etc/fstab」末尾に以下のコードを追記します。
/swapfile swap swap defaults 0 0
Botpressのインストール
Botpressをインストールします。
公式のbotpress.comからLinux版バイナリファイルをダウンロードします。
https://botpress.com/download
ダウンロードしたバイナリファイルはWinSCP,Tera TermのSSH SCP等を使用し、サーバ上にアップロードします。
アップロード先はホームディレクトリを指定して下さい。
※本記事では「botpress-v12_26_10-linux-x64.zip」
$ pwd
/home/bitnami
$ ls
botpress-v12_26_10-linux-x64.zip
Botpressの起動テスト
アップロードしたバイナリファイルを指定の場所に配置します。
$ mkdir botpress
$ cd botpress
$ unzip ../botpress-v12_26_10-linux-x64.zip
フォアグラウンドでテスト起動してみます。
※初回起動時に圧縮が展開されます
$ ./bp
サイトへアクセスし、nginxのテストページではなく、Botpressの管理画面が表示されることを確認します。
確認後、「ctrl」+「c」で中断します。
Node.jsのインストール
Node.jsのバイナリファイルをインストールします。
※本記事では12.x系を使用します。
$ curl -fsSL https://deb.nodesource.com/setup_12.x | sudo bash -
$ sudo apt-get install -y nodejs
pm2のインストール
RPMパッケージのグローバルインストールを有効化
rpmパッケージをグローバルインストールできるように設定します。
$ mkdir ~/.npm-global
$ npm config set prefix '~/.npm-global'
$ vi ~/.profile
「profile」末尾に以下のコードを追記します。
export PATH=~/.npm-global/bin:$PATH
pm2をインストール
pm2をグローバルへインストールして、OS起動時の自動起動を有効化します。
$ source ~/.profile
$ npm i pm2 -g
$ sudo /home/bitnami/.npm-global/bin/pm2 startup
Nginxの設定ファイルをBotpress用に修正する
Nginxの設定ファイルを、Botpress用の設定に差し替える。
$ cd ~/stack/nginx/conf/
$ mv nginx.conf nginx.conf.bitnami
$ sudo vi nginx.conf
「nginx.conf」に以下のコードを貼り付けます。
# Based on https://www.nginx.com/resources/wiki/start/topics/examples/full/#nginx-conf
user daemon daemon;
worker_processes auto;
error_log "/opt/bitnami/nginx/logs/error.log";
pid "/opt/bitnami/nginx/tmp/nginx.pid";
events {
worker_connections 1024;
}
http {
# Disable sending the server identification
server_tokens off;
# Prevent displaying Botpress in an iframe (clickjacking protection)
add_header X-Frame-Options SAMEORIGIN;
# Prevent browsers from detecting the mimetype if not sent by the server.
add_header X-Content-Type-Options nosniff;
# Force enable the XSS filter for the website, in case it was disabled manually
add_header X-XSS-Protection "1; mode=block";
# Configure the cache for static assets
proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
# Set the max file size for uploads (make sure it is larger than the configured media size in botpress.config.json)
client_max_body_size 10M;
# Configure access
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
error_log logs/error.log;
# Redirect unsecure requests to the HTTPS endpoint
server {
listen 80 default;
server_name localhost;
return 301 https://$server_name$request_uri;
}
server {
listen 443 http2 ssl;
server_name chibibotpress.chibinfra-itech.com;
ssl_certificate bitnami/certs/server.crt;
ssl_certificate_key bitnami/certs/server.key;
#include "/opt/bitnami/nginx/conf/bitnami/*.conf";
location /status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
# Force the use of secure protocols only
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# Enable session cache for added performances
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Added security with HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
# Enable caching of assets by NGINX to reduce load on the server
location ~ .*/assets/.* {
proxy_cache my_cache;
proxy_ignore_headers Cache-Control;
proxy_hide_header Cache-Control;
proxy_hide_header Pragma;
proxy_pass http://localhost:3000;
proxy_cache_valid any 30m;
proxy_set_header Cache-Control max-age=30;
add_header Cache-Control max-age=30;
}
# We need to add specific headers so the websockets can be set up through the reverse proxy
location /socket.io/ {
proxy_pass http://localhost:3000/socket.io/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
# All other requests should be directed to the server
location / {
proxy_pass http://localhost:3000;
}
}
}
設定を反映させる為、nginxを再起動します。
$ sudo /opt/bitnami/ctlscript.sh restart nginx
Botpressの起動
Botpressを起動する
Botpressサーバーを起動します。
※ 環境変数EXTERNAL_URLで、設定したドメインのURLを与える
$ pwd
/home/bitnami/botpress
$ EXTERNAL_URL=https://サイトのドメイン pm2 start './bp -p'
Botpressの自動起動設定
無事バックグラウンドでBotpressを起動できたかと思いますが、このままではサーバの再起動時など、サーバやpm2が落ちたタイミングでBotpressが停止してしまいます。
なので、pm2によるBotpressの自動起動設定を行います。
詳細①:https://www.yoheim.net/blog.php?q=20170705
詳細②:https://pm2.keymetrics.io/docs/usage/startup/
$ pm2 startup
[PM2] You have to run this command as root. Execute the following command:
sudo su -c "env PATH=$PATH:/home/unitech/.nvm/versions/node/v14.3/bin pm2 startup <distribution> -u <user> --hp <home-path>
次に、表示されたコマンドをコピーしてターミナルに貼り付けます。
$ sudo su -c "env PATH=$PATH:/home/unitech/.nvm/versions/node/v14.3/bin pm2 startup <distribution> -u <user> --hp <home-path>
最後に、必要なアプリをすべて起動したら、アプリリストを保存して、再起動後に再生成されるようにします。
$ pm2 save
以上で設定は全て完了です。
動作確認
「https://サイトのドメイン」 にアクセスして、Botpress管理ツールが開くか確認してください。
以下の管理画面が表示されれば、動作確認は完了です。
まとめ
本記事の内容は、@nakmas(りらいあデジタル株式会社)さんがQiitaで投稿して下さった手順をほぼほぼ丸パクリしたものです。
Lightsailでbotpressを構築する、といった課題に本格的に取り組みたい方は是非記事を参照してみて下さい。
環境は整ったので、今後はBotの作成に、着手していこうと思います。
コメント