Alexa スキルで外部APIを使う

Alexaスキルから実行するLambda 内で外部APIを利用したい。
APIの呼び出しを非同期で行ったところ、うまくいかなかった、、、

var options = {
    uri: "https://xxxxxxx",
    headers: {
        "Content-type": "application/json",
    },
    json: {
        "data1": "xxxx",
        "data2": "yyyy"
    }
};
request.post(options, function(error, response, body){
    // this.emit(':tell', "外部API呼び出し"); // ← ここでemitを実行するとAPIコールするが、Alexaへの応答が空になる
});
// this.emit(':tell', "外部API呼び出し");  // ← ここでemitを実行するとAPIコールされず、Alexaへの応答は正常にされる

本来あるべき姿ではないと思うが、APIコールを同期的に行うことで一旦解決。

npm install sync-request

呼び出しはこんな感じ

var response = request(
    'POST',
    "https://xxxxxx",
    {
        json: {
            "data1": "xxxxx",
            "data2": "yyyy"
        }
    }
);
this.emit(':tell', "外部API呼び出し");

一応GETの場合はこんな感じ

var response = request(
    'GET',
    "https://xxxxxxx"
);
var data = JSON.parse(response.getBody('utf8'));
this.emit(':tell', "データ1は" + data.data1 + "です");

Lambda のコードをアップロードする

Lambda で開発していてコードをアップロードするのに毎回Webコンソールからアップロードするのは大変なのでシェルを組みます。

zipに圧縮して、S3にアップ、それをLambdaに紐付ける流れです。
実際のシェルはこんな感じです。

#!/bin/sh
zip -r app.zip index.js node_modules
aws s3 cp ./app.zip s3://{バケット名}/app.zip --profile={プロファイル}
aws lambda update-function-code --function-name {Lambdaファンクション名} --s3-bucket {バケット名} --s3-key app.zip --publish --profile={プロファイル}

AWS の ElasticBeanstalk でログファイルを CloudWatch に転送する

SpringBootで作成したWebアプリケーションをElasticBeanstalkで運用していてAutoScalingなどでEC2が削除されると、EC2上に保存されているログファイルも当然削除されてしまう。
削除される前にログファイルをどこかに保存したいと思うのですが、その一つの方法としてCloudWatchに転送することにする。

ElasticBeanstalkにはCloudWatchにログファイルを連携する機能がありますが、今回はアプリケーション独自のログを連携したいのでこの機能(CloudWatch Logs へのインスタンスログのストリーミング)は使いません。

アプリケーションから出力した独自ログとapacheのログを転送することにします。
CloudWatchへの転送にはawslogsを使用します。
awslogsのインストールなどEC2への操作などはデプロイするWebアプリケーション内に設定ファイルとして追加します。
具体的には、アプリケーションの src/main/webapp/.ebextensions 以下に配置します。

.ebextensions 以下にconfigファイルを配置しますが、awslogsを使う場合は以下のような内容です。

packages:
  yum:
    awslogs: []
container_commands:
  1-cp-awscli_conf:
    command: cp -rf .ebextensions/awscli.conf /etc/awslogs/awscli.conf
  2-cp-awslogs_conf:
    command: cp -rf .ebextensions/awslogs.conf /etc/awslogs/awslogs.conf
  3-chkconfig-awslogs:
    command: /sbin/chkconfig awslogs on
  4-restert-awslogs:
    command: /sbin/service awslogs status; if [ $? -eq 0 ]; then /sbin/service awslogs restart; else /sbin/service awslogs start ; fi;

awslogsの設定ファイルも予め用意しておいて、そのファイルコピーします。
最終的なファイル構成はこんな感じです。

awscli.conf、awslogs.conf の内容も参考までに。

awscli.conf

[plugins]
cwlogs = cwlogs
[default]
region = ap-northeast-1

awslogs.conf

[general]
state_file = /var/lib/awslogs/agent-state

[/var/log/httpd/access_log]
log_group_name = sample_app
log_stream_name = {instance_id}_httpd
file = /var/log/httpd/access_log
datetime_format = %d/%b/%Y:%H:%M:%S
initial_position = start_of_file
buffer_duration = 5000

[/var/log/tomcat8/sample_app.log]
log_group_name = sample_app
log_stream_name = {instance_id}_app
file = /var/log/tomcat8/sample_app.log
datetime_format = %Y-%m-%d %H:%M:%S.
initial_position = start_of_file
buffer_duration = 5000

もちろんログのファイル名や、日付けフォーマットなどを合わせる必要があります。

WordPress(Lightsail)でhttpsを強制

apache設定にリダイレクト設定を追加するので設定ファイルを開き、

sudo vi /opt/bitnami/apache2/conf/bitnami/bitnami.conf

以下のリダイレクト設定を追加。

<VirtualHost _default_:80>
  DocumentRoot "/opt/bitnami/apache2/htdocs"
  # Redirect
  RewriteEngine On
  RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [R=301,L]

追加後にapacheの再起動。

sudo /opt/bitnami/ctlscript.sh restart apache

以下のように条件をつけても良かったが、VirtualHostで80ポートにきたら無条件でリダイレクトにした。

RewriteCond %{HTTPS} off

EC2でEBSのスナップショットを定期的にとる

EBSのスナップショットを作成するバッチを作り、cronで動かす。

#!/bin/sh
SHELLDIR=`dirname ${0}`
SHELLDIR=`cd ${SHELLDIR}; pwd`
SHELLNAME=`basename $0`

LOG_DIR="/root/logs"
LOG_SAVE_PERIOD=14
LOG_FILE="${LOG_DIR}/${SHELLNAME}.log"

AZ=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone`
LN=`echo $((${#AZ} - 1))`
REGION=`echo ${AZ} | cut -c 1-${LN}`
SNAPSHOTS_PERIOD=2

AWS="/usr/bin/aws --region ${REGION}"

INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`

CREATED_BY="Created by CreateSnapshotShell"

rotate_log() {
    (( cnt=${LOG_SAVE_PERIOD} ))
    while (( cnt > 0 ))
    do
        logfile1=${LOG_FILE}.$cnt
        (( cnt=cnt-1 ))
        logfile2=${LOG_FILE}.$cnt
        if [ -f $logfile2 ]; then
            mv $logfile2 $logfile1
        fi
    done

    if [ -f $LOG_FILE ]; then
        mv ${LOG_FILE} ${LOG_FILE}.1
    fi
    touch $LOG_FILE
}

print_msg() {
    echo "`date '+%Y/%m/%d %H:%M:%S'` $1" | tee -a ${LOG_FILE}
}

set_vol_id() {
    VOL_ID=`${AWS} ec2 describe-instances --instance-ids ${INSTANCE_ID} --output text | grep EBS | awk '{print $5}'`
}

create_snapshot() {
    print_msg "Create snapshot Start"
    if [ -z ${VOL_ID} ] ; then
        echo ${VOL_ID}
        print_msg "ERR:ec2-describe-instances"
        logger -f ${LOG_FILE}
        exit 1
    fi
    print_msg "ec2-describe-instances Success : ${VOL_ID}"
    ${AWS} ec2 create-snapshot --volume-id ${VOL_ID} --description "${CREATED_BY}(${INSTANCE_ID}) from ${VOL_ID}" >> ${LOG_FILE} 2>&1
    if [ $? != 0 ] ; then
        print_msg "ERR:${SHELLDIR}/${SHELLNAME} ec2-create-snapshot"
        logger -f ${LOG_FILE}
        exit 1
    fi
    print_msg "Create snapshot End"
}

delete_old_snapshot() {
    print_msg "Delete old snapshot Start"
    SNAPSHOTS=`${AWS} ec2 describe-snapshots --output text | grep ${VOL_ID} | grep "${CREATED_BY}" | wc -l`
    while [ ${SNAPSHOTS} -gt ${SNAPSHOTS_PERIOD} ]
    do
        ${AWS} ec2 delete-snapshot --snapshot-id `${AWS} ec2 describe-snapshots --output text | grep ${VOL_ID} | grep "${CREATED_BY}" | sort -k11 | awk 'NR==1 {print $10}'` >> ${LOG_FILE} 2>&1
        if [ $? != 0 ] ; then
            print_msg "ERR:${SHELLDIR}/${SHELLNAME} ec2-delete-snapshot"
            logger -f ${LOG_FILE}
            exit 1
        fi
        SNAPSHOTS=`${AWS} ec2 describe-snapshots | grep ${VOL_ID} | grep "${CREATED_BY}" | wc -l`
    done
    print_msg "Delete old snapshot End"
}

rotate_log

print_msg "INF:$SHELLDIR/${SHELLNAME} START"
set_vol_id
create_snapshot
delete_old_snapshot
print_msg "INF:$SHELLDIR/${SHELLNAME} END"

exit 0

を、スナップショットを取得したいEC2に保存してcronを設定する。

# backup EBS
0 17 * * * /root/tools/createSnapshot.sh

WordPress(Lightsail) をインストールして最初に

バナー削除

こんなバナーが出ているので非表示にする。

sudo /opt/bitnami/apps/wordpress/bnconfig --disable_banner 1

WordPressの管理画面にログイン

ユーザーは”user”固定ですがパスワードが不明なので、SSHで接続して、

cat bitnami_application_password

https://{ドメイン名}/wp-login.php にアクセスしてログインする。

ログインURLを変更

セキュリティ上、デフォルトの”wp-login.php”を変更する。
今回は“SiteGuard WP Plugin” “WPS Hide Login”を使用しました。

Lightsail(WordPress)にSSLを設定

Lightsailのロードバランサーを使用すれば、AWSで生成するSSLを利用できるが予算の関係で無料のSSL、Let’s Encryptを使用する。

SSH接続

キーペアーを使用してローカルからSSHクライアントで接続も可能ですが、Webコンソールからも接続できます。

letsencrypt をインストール

$ sudo apt-get install letsencrypt python-letsencrypt-apache

SSL証明書を生成

$ sudo letsencrypt certonly --webroot -w /opt/bitnami/apps/wordpress/htdocs/ -d {ドメイン名}

こんな画面でメールアドレスの確認と規約への同意を求められます。

SSL証明書の配置

$ sudo cp /etc/letsencrypt/live/{ドメイン名}/fullchain.pem /opt/bitnami/apache2/conf/server.crt
$ sudo cp /etc/letsencrypt/live/{ドメイン名}/privkey.pem /opt/bitnami/apache2/conf/server.key
$ sudo /opt/bitnami/ctlscript.sh restart apache