lambda入門(Node)③ – API Gatewayを使ってslackからのリクエストをlambdaで受けられるようにする

第3回になりました。

過去のはこちら。

今回は、ようやくやりたいことに近づいて来まして
slackからのリクエストをlambdaで受けられるようにしたいと思います。

何か調べていくと、どうもAPI Gatewayを使うと良い感じぽい。
まずはAPI Gatewayについて予習を。

API Gateway

どんな役割をしてくれるのか

APIのエンドポイントとして待ち構える玄関として使える
現在はhoge/*のようなパスを/hoge/{proxy+}として設定できる
プロキシリソースなるものらしい
post, putの振り分けなどが簡単になった

課金体系

受信した API 呼び出しと、送出したデータ量に対して発生

serverless frameworkでの設定

serverless.yml
functions:
  bookStore:
    handler: books/store.store
    events:
      - http:
          path: books
          method: get
          cors: true

これだけ。
デプロイ実行すると

$ serverless deploy -v --stage dev
Serverless: Packaging service...
・
・
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
・
・
Service Information
service: testProject
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books
functions:
  bookStore: testProject-dev-bookStore

エンドポイントが作られた。。!

レスポンスを送れるか試してみる

$ curl https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books
{"message":"ok",...........}

うん、okそう、すごい。ymlに書くだけで何でも設定してくれちゃう。

Slackから応答させるようにする

ここで今回の本題。
作成したエンドポイントに対してスラックがリクエストを送って
slack上にメッセージが返ってくるようにする。

全体像

スクリーンショット 2017-04-29 15.17.57.png

やることとしては、リクエストパラメータを解析してデータを保存。
保存した結果を返す

book.js

'use strict';

const slackAuthorizer = require('../authorizer/slackAuthorizer');
const parser = require('../service/queryParser');

const bookSave = require('../useCase/book/save.js');

module.exports.book = (event, context, callback) => {
  const queryParser = new parser(event.body);
  const authorizer = new slackAuthorizer(queryParser.parseToken());

  /**
   * 認証
   */
  if (!authorizer.authorize()) {
    context.done('Unauthorized');
  }

  /**
   * @Todo ここで lambda function の振り分けを行いたい
   */
  bookSave(event, (error, result) => {});

  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'ok',
    }),
  };

  callback(null, response);
};

同期で処理を行いたいので asyncを使って処理をブロックごとに実行する

save.js

'use strict';

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const bookTable = process.env.bookTable;

const uuidV1 = require('uuid/v1');

const webhookUrl = process.env.slack_webhook_url;
const request = require('request');

const dateTime = require('node-datetime');
const dt = dateTime.create();
const insertDate = dt.format('Y-m-d H:M:S');

const async = require('async');

const parser = require('../../service/queryParser');

module.exports = (event, callback) => {

  const queryParser = new parser(event.body);

  const key = uuidV1();

  async.series([
    function(callback) {

      /**
       * データを保存
       */
      dynamoDB.put({
        'TableName': bookTable,
        'Item': {
          'id': key,
          'title': queryParser.parseText(),
          'insert_date': insertDate,
        },
      }, function(err, data) {
        callback(null, "saved");
      });
    },
    function(callback) {

      /**
       * 保存したものを取り出して
       * 結果を返す
       */
      dynamoDB.get({
        TableName: bookTable,
        Key: {
          id: key,
        },
      }, function(err, data) {
        if (!err) {
          const response = {
            text: ``${data.Item.title}` is Saved !!`,
          };
        
          /**
           * webhook でチャンネルにメッセージを返す
           */
          request.post(webhookUrl, {
            form: {
              payload: JSON.stringify(response),
            },
          }, (err, response, body) => {
            callback(null, 'getData');
          });
        }
      });
    },
  ], function(err, results) {
    if (err) {
      throw err;
    }
  });
};

slash commandから送られてきたパラメータをパースするために
query-stringモジュールを使い、ラップした

queryParser.js
'use strict';

const queryStringParser = require('query-string');

module.exports = class queryParser {

  constructor (queryString) {
    this.queryString = queryString;
  }

  parseToken () {
    return queryStringParser.parse(this.queryString).token;
  }

  parseText () {
    return queryStringParser.parse(this.queryString).text;
  }
};

slashコマンドの設定は省略します。
最初の api gatewayで設定されたエンドポイントのURLを
設定してあげればいいので

実際に動かすとこんな感じです。

4b4b9687608f58545ef2b4536a7166c2.gif

ソースは汚いかもしれませんが、saverlessフレームワークで
エンドポイント作成やdb、外部サービスの連携が簡単に出来ました

もっとキレイに書けるようにjsの筋力をつけていかねば。。笑

続きを読む

AWS x OpenVPNでP2P接続できる環境を作る

目的

遠隔地の端末XにNATを挟まずに接続したい!

端末Xの既存環境はいじりたくない!

(ついでに、クラウドと連携できると夢が広がるね!)


上記実現のために、

  1. VPN接続、かつルーティングのみで構成する
  2. 端末Xが接続するルーター(ex.ラズパイ等)に、VPNクライアント環境を構築する
  3. AWSパブリックサブネット上にVPNサーバーを構築する

想定するネットワークは以下の通り

image

赤、青、黒色のIPアドレスはそれぞれのサブネット内で割り振られたもので、紫色のIPアドレスはVPNネットワーク上で割り振られるアドレスとする

ここで、赤のVPNクライアント(10.8.0.6)から、青のVPNクライアントサブネット上の端末X(192.168.20.5)に、プライベートアドレス指定でアクセス可能な環境を構築する

具体的にやること

上記環境を構築するために、以下の手順で作業を行う

  1. AWSパブリックインスタンスにOpenVPNを導入する
  2. OpenVPN接続用の証明書を発行する
  3. サーバーの準備をする
  4. クライアントの準備をする
    1. mac編
    2. ubuntu編
  5. 各サブネットに接続確認を行う

なお、VPNクライアントすべてに対して、OpenVPN環境は未導入、VPNクライアント兼ルーターにはRaspberryPiを使用し、すでにルーターとして利用可能な状態であるとする

また、赤のVPNクライアントのOSはmacとする

1. AWSにOpenVPNを導入する

前提条件

AWS上のVPC、及びその内部のパブリックサブネットとプライベートサブネットは作成済みであるものとし、作成方法などの解説は行わない

以下に最低限必要な設定を示す

AWSパブリックサブネット上でVPNサーバーとして利用するインスタンスのOSには、Amazon Linux AMI 2016.09.1(HVM)を使用する

ボリュームサイズや処理性能は特に指定しないが、セキュリティグループ設定の際に、ssh及びVPN接続用に以下のポートを空けておく

  • ssh接続用ポート

    • タイプ:SSH
    • プロトコル:TCP
    • ポート範囲:22
    • 送信元:任意
  • VPN接続用ポート
    • タイプ:カスタムUDPルール
    • プロトコル:UDP
    • ポート範囲:1194
    • 送信元:任意

VPN接続用のポート設定は以降で利用する

他、必要に応じてソフトウェアの更新などを行っておく

合わせて、接続確認用にプライベートサブネット上にインスタンス(10.0.1.228)を立ち上げておく

パブリックサブネット上のインスタンスをOpenVPNサーバーとして動かすに当たり、導入が必要なものは以下の二つである

1. OpenVPN(v2.3.12)
2. easy-rsa(証明書方式の場合)(v3.0.0)

まずはsshでインスタンスに接続し、これらを導入する

OpenVPNのインストール

OpenVPNはyumでインストールできる

$ sudo yum install -y openvpn
...
完了しました!

基本的に、自動で自動起動に登録される

easy-rsaのインストール

拡張性、安全性、そして複数のクライアントを管理するVPNを構成するために、easy-rsaを利用する

easy-rsaはバイナリを取得、解凍して導入する

また、以降の作業は/usr/local/EasyRSA/ディレクトリ下で行うので移動しておく

$ wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.0-rc2/EasyRSA-3.0.0-rc2.tgz
...
2017-03-28 02:22:13 (107 KB/s) - `EasyRSA-3.0.0-rc2.tgz' へ保存完了 [34886/34886]
$ tar -xvzf EasyRSA-3.0.0-rc2.tgz
...
EasyRSA-3.0.0-rc2/easyrsa
$ sudo mv EasyRSA-3.0.0-rc2 /usr/local/EasyRSA
$ cd /usr/local/EasyRSA/

2. OpenVPN接続用の証明書を発行する

easy-rsaの初期設定

easy-rsaを導入した際には、以下のコマンドで認証情報を生成するための初期設定を行う必要がある

  1. init-pki
  2. build-ca
  3. gen-dh

init-pki

init-pkiを実行し、初期化を行う

$ ./easyrsa init-pki
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /usr/local/EasyRSA/pki

ここで生成された/usr/local/EasyRSA/pki/ディレクトリに、以降の手順で生成する認証用ファイルが配置される

build-ca

build-caで認証局を作成する

$ ./easyrsa build-ca
Generating a 2048 bit RSA private key
.+++
..+++
writing new private key to '/usr/local/EasyRSA/pki/private/ca.key'
Enter PEM pass phrase:<パスフレーズ>
Verifying - Enter PEM pass phrase:<パスフレーズ(確認)>
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:<ホスト名等>
CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/usr/local/EasyRSA/pki/ca.crt
  • <パスフレーズ>:任意のパスワード

    • 証明書の発行の際に必要、覚えておくこと
  • <ホスト名等>:認証局を識別するための名称
    • 本気で運用するならばつけるべき
    • 今回はVPN接続目的のみのため適当な名称 or デフォルトで可

gen-dh

gen-dhでDHパラメータを生成する

  • DH:Diffie-Hellman

    • 公開鍵暗号方式の具体的な方法の一つ
    • DHは公開鍵暗号方式の概念を初めて公開した人たちの名前
$  ./easyrsa gen-dh
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
...

DH parameters of size 2048 created at /usr/local/EasyRSA/pki/dh.pem

以上で、証明書の発行準備は完了である

次の手順から、実際に使用するサーバー/クライアント向けの認証ファイルの生成を行っていく

サーバー用秘密鍵・証明書の生成

build-server-fullでサーバー用秘密鍵と証明書の作成、及び署名を行う

$ ./easyrsa build-server-full server nopass
Generating a 2048 bit RSA private key
..........................+++
..................................+++
writing new private key to '/usr/local/EasyRSA/pki/private/server.key'
-----
Using configuration from /usr/local/EasyRSA/openssl-1.0.cnf
Enter pass phrase for /usr/local/EasyRSA/pki/private/ca.key:<登録したパスフレーズ>
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'server'
Certificate is to be certified until Mar 26 02:26:31 2027 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
  • 証明書読み込み時にパスフレーズ不要とする場合はnopassオプションを使用する

    • オプションを使用しない場合、VPN接続時に毎回、パスフレーズの入力が求められる
  • 途中でCA証明書生成時に設定したパスフレーズの入力を求められるので入力する

クライアント用秘密鍵・証明書の生成

build-client-fullでクライアント用秘密鍵と証明書の作成、及び署名を行う

以下に、ファイルを生成するコマンドの例を示す

$  ./easyrsa build-client-full client1 nopass

Generating a 2048 bit RSA private key
.....................................................................+++
.........+++
writing new private key to '/usr/local/EasyRSA/pki/private/client1.key'
-----
Using configuration from /usr/local/EasyRSA/openssl-1.0.cnf
Enter pass phrase for /usr/local/EasyRSA/pki/private/ca.key:<登録したパスフレーズ>
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'client1'
Certificate is to be certified until Mar  8 07:31:37 2025 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
  • client1には任意のクライアント名を指定する

    • ex, client1,client2,client3,and more…
    • ex, alpha,beta,and more…
  • パスフレーズ不要とする場合はnopassオプションを使用する

同様に、必要なだけクライアント用の秘密鍵、証明書の生成を行う

今回はclient1に加え、ルーター兼用クライアント向けにclient2も作成しておく

その際のコマンドは以下のようになる

$  ./easyrsa build-client-full client2 nopass

以上で、認証に必要なファイルの生成は終了である

次に、これらのファイルを配置し、VPN接続を行うための設定を行っていく

3. サーバーの準備をする

これまでに生成したファイルのうち、以下のファイルを/etc/openvpn/に移動する

  • ca証明書:ca.crt
  • サーバー用証明書:server.crt
  • サーバー用秘密鍵:server.key
  • DHパラメータ:dh2048.pem
$ sudo cp pki/ca.crt /etc/openvpn/
$ sudo cp pki/issued/server.crt /etc/openvpn/
$ sudo cp pki/private/server.key /etc/openvpn/
$ sudo cp pki/dh.pem /etc/openvpn/dh2048.pem

VPNサーバー設定ファイルのサンプルをコピーし、それをもとに実際にサーバーとして動かす際の設定を行う

サンプルの置かれているディレクトリは以下の通りである

/usr/share/doc/openvpn-2.3.12/sample/sample-config-files/server.conf

このサーバー設定ファイルも/etc/openvpn/に配置する

$ sudo cp /usr/share/doc/openvpn-2.3.12/sample/sample-config-files/server.conf /etc/openvpn/server.conf

サーバー設定ファイルを環境に合わせて修正する

好みのエディタで開き、編集する

$ sudo vi /etc/openvpn/server.conf

以下に編集例(抜粋)を示す

port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh2048.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 10.0.0.0 255.255.0.0"
push "route 192.168.20.0 255.255.255.0"
client-config-dir ccd
route 192.168.20.0 255.255.255.0
client-to-client
keepalive 10 120
comp-lzo
persist-key
persist-tun
status openvpn-status.log
log-append  openvpn.log
verb 3
  • portprototunはそれぞれ接続ポート番号、接続方式(udp/tcp)、インターフェイス形式(tap/tun)を指定する

    • 本環境では、UDP1194ポートをVPN接続用として開放しているので、これを指定する
    • この設定は、後述のクライアント設定ファイルでも使用する
  • cacertkeydhは、各々のサーバー用ファイルの場所を、/etc/openvpn/ディレクトリを基準とした相対パスで入力する
  • serverには、サーバーサブネットアドレス範囲を指定する
  • push "route ..."には、クライアントへ通知する、クライアントからアクセス可能なサブネットを指定する

    • 実際の動作としては、クライアントのルーティングテーブルに、指定したサブネットへアクセスする場合に、VPNサーバーへとルーティングするような設定が追加される
  • client-config-dir ccdオプションは、クライアントサブネットへアクセスする必要がある場合など、クライアントに関する設定が必要な際に有効化する

    • 有効化した際には/etc/openvpn/ccd/ディレクトリを作成しないと、openvpnそのものが正常に起動しなくなるので注意する(次の手順で作成)
    • ディレクトリ名称ccdは任意のディレクトリを指定可能
    • 合わせて、route ...により、クライアントサブネットとして192.168.20.0/24サブネットを、サーバーのルーティングテーブルに追加している
  • client-to-clientオプションは、クライアント間の通信、及びクライアントサブネット間の通信を許可する場合に有効化する
  • 設定項目については以下も参照

上記設定では、サーバー側からVPNクライアントに通知するルーティング設定として、以下の二つのサブネットにアクセスする際に、サーバーへとルーティングするような設定を加えている

  • 192.168.20.0/24
  • 10.0.0.0/16

前者のサブネットは、青のVPNクライアント兼ルーターの属するクライアントサブネットを示す

後者のサブネットは、AWSのVPC上のサブネット10.0.0.0/24(サーバーの立っているパブリックサブネット)と10.0.1.0/24(プライベートサブネット)を内包する

クライアントサブネットへの接続設定

VPNサーバーに、アクセス可能なVPNクライアントサブネットへのルートを登録する

サーバー設定ファイル上で追加したVPNクライアントサブネット192.168.20.0/24への通信をclient2へルーティングするよう設定を加える

まず、/etc/openvpn/ccd/ディレクトリを作成する

$ sudo mkdir /etc/openvpn/ccd

ディレクトリは、サーバー設定ファイル上で行った指定に従う

次に、ccdディレクトリ中にclient2という名称のファイルを生成し、iroute ...にサブネットを指定する

$sudo vi /etc/openvpn/ccd/client2

iroute 192.168.20.0 255.255.255.0

このiroute ...の設定は、OpenVPNサーバーに対し、192.168.20.0/24サブネット(上記設定の場合)への通信を、client2にルートさせるためのものである

サーバーをルーターとして使用可能にする

VPNサーバーにルーターとしての機能を持たせるためには、IPフォワーディングを許可する必要がある

以下のファイルを編集し、net.ipv4.ip_forwardを1に変更する

$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward = 1

以上で、インスタンス内で実施する必要のある設定は終了である

次に、AWS上で、ルーティング設定の修正を行う

AWS上の設定

AWS上のサブネットとデータの送受信を行うために、以下の設定を行う必要がある

  1. サブネットのルートテーブルへ、VPNネットワークのアドレスを登録
  2. VPNサーバーのインスタンスの送信元/送信先の変更チェックの無効化

ルートテーブルの修正

VPNクライアントからサーバー側サブネットにアクセスするため、サーバー側サブネットのルートテーブルにVPNサブネット(10.8.0.0/24)を登録する

VPNクライアントからサーバー側サブネットに接続するための設定はサーバー側設定ファイル上のpush "route ..."で実施済みであるが、サーバー側サブネットからVPNクライアントに接続するための情報は与えられていない

よって、AWSサブネットに紐づけられたルートテーブルの設定を修正する

  • AWS>[VPC]>[ルートテーブル]>サブネットに関連付けているルートテーブルを選択
    * [ルート]タブ>[編集]>[別ルートの追加]

    • 送信先:10.8.0.0/24(VPNサブネットを指定)
    • ターゲット:OpenVPNインスタンス(のネットワークインターフェイス)を指定

サーバー側サブネットからVPNクライアントサブネットにアクセスする場合には、同様の手順(送信先のみ変更)でルートテーブルに追加する

今回は、サーバー側サブネット-クライアントサブネット間のアクセスはないものと想定し、設定しない

送信元/送信先の変更チェックの無効化

AWSのEC2インスタンスは通常、自身のIPアドレス以外を指定した通信を無視する設定となっている

この状態では、クライアントサブネット間の通信のように、自インスタンス以外に向かう通信をルーティングすることができない

よって、この設定を無効化し、通信を受け取ることができるようにする必要がある

以下の設定を行う

  • AWS>[EC2]>VPNサーバーのインスタンスを選択

    • [アクション]>[ネットワーキング]>[送信元/送信先の変更チェック]
    • ダイアログにて、有効である場合には無効化を行う

02.png

ここまでの設定が終了したならば、念のためインスタンスを再起動させておく

4.1. クライアントの準備をする:mac編

以下の環境を想定し、クライアント側の準備を行う
(先述の想定環境中の赤のVPNクライアントを想定する)

  • OS:mac(macbook pro)
  • VPNクライアント:TunnelBlick(GUI)

クライアントへ必要なファイルを移動

SCPコマンド等を使用し、以下のファイルをクライアントへ移動する

  • ca証明書:ca.crt
  • クライアント用秘密鍵:client1.key等
  • クライアント証明書:client1.crt等

SCPコマンドを使用して、ファイルをダウンロードする際の例を以下に示す

  • 以下はssh接続用のキーが存在する場合の例

    • pemファイル名:xxx.pem
    • ダウンロード元アカウント名:ec2-user(AWSデフォルトの場合)
    • ダウンロード元グローバルIP:xxx.xxx.xxx.xxx(VPNサーバーグローバルIP)
    • ダウンロード元ファイル:/usr/local/EasyRSA/pki/private/ca.key等
    • ダウンロード先ディレクトリ:/etc/openvpn/
  • 接続先のポート番号を指定する場合は、scp -P xxxx -i ...
  • 接続先のパスは絶対パスで指定する
$ sudo scp -i ~/.ssh/xxx.pem ec2-user@xxx.xxx.xxx.xxx:/usr/local/EasyRSA/pki/private/ca.key /etc/openvpn
$ sudo scp -i ~/.ssh/xxx.pem ec2-user@xxx.xxx.xxx.xxx:/usr/local/EasyRSA/pki/issued/client1.crt /etc/openvpn
$ sudo scp -i ~/.ssh/xxx.pem ec2-user@xxx.xxx.xxx.xxx:/usr/local/EasyRSA/pki/private/client1.key /etc/openvpn

取得したファイルは/etc/openvpn/に配置する

クライアント設定ファイルの修正

必要があれば、クライアント設定ファイルのサンプルも、クライアントにコピーし使用する

あるいは、OpenVPNのサイトより、コピーして使用することもできる

ファイル名は任意であるが、ここではclient.confとする

以下にクライアント設定ファイルの設定例(抜粋)を示す

client
dev tun
proto udp
remote <サーバーグローバルIP> 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client1.crt
key client1.key
comp-lzo
verb 3
  • clientは、この設定ファイルを読んだ端末がクライアント側であることを示す
  • devprotoの指定はサーバー設定ファイル(前述)に従う
  • remoteには、<サーバーグローバルIP>とサーバーポート番号をスペース区切りで記述する

    • <サーバーグローバルIP>にはグローバルIPでなくとも、クライアントからサーバーにアクセスできるIPアドレス、あるいはホスト名を指定する
    • サーバーポート番号の指定はサーバー設定ファイルに従う
  • cacertkeyには、max x tunnelblick環境では、/etc/openvpn/ディレクトリを基準としたパスを指定する

    • 今回は/etc/openvpn/直下のためディレクトリ指定は行う必要はない

Tunnelblickへの登録

作成したclient.confを、Tunnelblickで開く

  • エラーが出た場合は修正する

    • どこが間違っているかは教えてくれる
    • tls設定がサンプル時点で有効になっている場合があるので確認が必要
  • 特に、ca証明書のディレクトリ指定などに注意する

問題なく登録が終わったならば、”接続”できるはずである

4.2. クライアントの準備をする:ubuntu編

インストール手法などは異なるものの、linux系統でOpenVPNをクライアントとして導入する手順は同一である
(今回は、具体的にRaspberryPiにOpenVPNを導入する場合を想定する)

ここで、クライアントが所属するサブネットワークへのアクセスを許可する場合、VPNクライアント側で直接設定をする必要は基本的には存在しない

サブネットへのルーティング設定は、VPNサーバー側の設定のみで良い

ただし、ルーター上でサブネットの設定を行う必要はあるが、すでにルーターとして使用可能な状態であり、設定済みであるとして、今回は説明しない

ここでは以下の環境を想定し、クライアント側の準備を行う

  • OS:ubuntu(macbook pro上でrefind使用)
  • VPNクライアント:OpenVPN(CUI)

OpenVPNのインストール

以下のコマンドでOpenVPNをインストールする

$ sudo apt-get install openvpn

クライアントへ必要なファイルを移動

SCPコマンド等を使用し、以下のファイルをクライアントへ移動する

  • ca証明書:ca.crt
  • クライアント用秘密鍵:client1.key等
  • クライアント証明書:client1.crt等

取得したファイルは/etc/openvpn/に配置する

クライアント設定ファイルの修正

VPNクライアント設定ファイルのサンプルをコピーし、それをもとに実際の設定を行う

サンプルの置かれているディレクトリは以下の通りである

/usr/share/doc/openvpn-2.3.12/sample/sample-config-files/client.conf

サーバー設定ファイルが置かれていたディレクトリと同一である

このクライアント設定ファイルも/etc/openvpn/に配置する

$ sudo cp /usr/share/doc/openvpn-2.3.12/sample/sample-config-files/client.conf /etc/openvpn/client.conf

サーバー設定ファイルを環境に合わせて修正する

好みのエディタで開き、編集する

$ sudo vi /etc/openvpn/client.conf

以下に編集例(抜粋)を示す

client
dev tun
proto udp
remote <サーバーグローバルIP> 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca /etc/openvpn/ca.crt
cert /etc/openvpn/client1.crt
key /etc/openvpn/client1.key
comp-lzo
verb 3
  • 一番上のclientは、このファイルを読んだ端末がクライアント側であることを示す
  • cacertkeyには絶対パスを指定する

なお、上記編集例はサーバー設定ファイルの内容に対応する

VPNサーバーへの接続

VPNのクライアントとして起動させるには、起動の際にクライアント設定ファイルを指定する必要がある

以下のコマンドで、クライアントようコンフィグファイルを指定し、クライアントとしてVPNサーバーへ接続する

$ sudo /usr/sbin/openvpn /etc/openvpn/client.conf
...
... Initialization Sequence Completed

※コンソールを一つ占有することに注意する

5. 各サブネットに接続確認を行う

ここでは、以下のような環境で、クライアントからVPNサーバーを経由し、サーバー側サブネットへtracerouteを行う

ここまでの手順通りに行っていれば、問題なく接続できるはずである

netstatコマンドなどで、対応するルーティングが行われているか確認する

VPNクライアントからping、あるいは以下のようなtracerouteコマンドが通れば、正常に設定できている

$ traceroute 10.0.1.228
traceroute to 10.0.1.228 (10.0.1.228), 64 hops max, 52 byte packets
1 10.8.0.1 (10.8.0.1) xxx ms xxx ms xxx ms
2 10.0.1.228 (10.0.1.228) xxx ms xxx ms xxx ms
$ traceroute 10.8.0.10
traceroute to 10.8.0.10 (10.8.0.10), 64 hops max, 52 byte packets
1 10.8.0.10 (10.8.0.10) xxx ms xxx ms xxx ms
$ traceroute 192.168.20.5
traceroute to 192.168.20.5 (192.168.20.5), 64 hops max, 52 byte packets
1 10.8.0.10 (10.8.0.10) xxx ms xxx ms xxx ms
2 192.168.20.5 (192.168.20.5) xxx ms xxx ms xxx ms

以上

続きを読む

メモ: Route 53 で Privacy Protection がなかなか有効にならないときには、ドメインのロックを有効にして Don’t hide にしてから hide にするといいよ

Amazon Route 53で汎用JPドメインを申請してみた

の方法を参考にしつつ、自分も Privacy Protection を有効化しました。

とても些細なハマりどころなのですが、
1 日ほど待っても WHOIS 情報が自分の住所のまま、ということがありました。

ワークアラウンドで解決したのでやり方を書いておきます。

1. Privacy Protection を有効化するにはドメインのロックが必要

連絡先情報を非表示にできるのは、移管できないようにドメインがロックされている場合のみです。ドメインを Amazon Route 53 に移管する場合または から移管する場合は、WHOIS クエリでお客様の連絡先情報が表示されるように、プライバシー保護を無効にする必要があります。移管が完了したらプライバシー保護を再度有効にすることができます。

http://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/domain-privacy-protection.html

わすれがちです。ドメインのロックを有効にしましょう。

Registered Domains > ドメインを選択 > Transfer lock で (enable) をクリックすると有効になります。

2. ドメインのロックと Privacy Protection を有効にする順番

ここが私の躓きのポイントでした。

ドメインのロックを有効化しても、その前に既に Privacy Protection が有効化されている状態だと、自動的に WHOIS 情報は更新されることはないようです。

一度 Privacy Protection を無効にしてから有効にすることで強制的に更新する、というワークアラウンドが必要になります。

つまり

  1. ドメインのロックを有効化
  2. Privacy Protection を無効化 (Edit contacts からラジオボタンを Don’t hide contact information と選んで Save)
  3. (無効化処理が完了するまで数十秒ほど待つ)
  4. Privacy Protection を有効化 (Edit concats からラジオボタンを Hide contact information if … と選んで Save)

という手順になります。

ここに気が付かないと「おかしいな、ちゃんと有効化にしてるのにな〜、すぐには反映されないのかな〜」と何度も Edit contacts したり、何時間か反映を待ってみたり、無駄な時間を過ごす羽目になります……。

続きを読む

Terraformでstate lockを触ってみた。

はじめに

初めてTerraformを触る折に
「v0.9以上だとstate lockっていう機能があるから使ったほうが良いよ」
というアドバイスをもらいました。
(そもそも、stateってなんですか?から始まるわけですが・・・)

初めてTerraformを触った時に、公式ドキュメントだけでは???な状態だったので
痕跡として残しておきます。

結果として、まだ使いこなせてません。というか僕の使い方は果たしてあっているのだろうか
なので情報としては不足しているかもしれませんが、とりあえず備忘録的に残します。
またわかったらUpdateします。

そもそもState Lockとは

完全に理解しないで書いてますが
Terraformは「自分が知っているリソース(EC2やS3)しか関与しないよ」というポリシーで
どのリソースを自分が扱ったか、というのをtfstateというJSONファイルで管理しているようです。

このtfstateファイルは、一人でTerraformを動かす場合は問題無いのですが
複数人でTerraformをいじる必要が出てきた時に問題で、それは「backend」モジュールを使うことで回避してきたようですが
同じタイミングでterraformを実施した場合、その部分までは制御しきれてなかったようです。

で、v0.9以上?から、「Plan/Applyをしているタイミングではロックする」機能が実装されたようで。
せっかくなので導入してみました。

公式サイト:https://www.terraform.io/docs/state/locking.html

準備

手動で準備が必要なものは
・terraformを実行するユーザのCredential情報
→ めんどくさかったので test-terraformとかいうユーザを別途作成しました。
・S3 Bucketの作成
→ terraform-s3-state とかいう名前で作りましょう
・DynamoDBの作成
→ 名前はなんでも良いです。terraform-state-lock とかで作りましょう。
  プライマリキーを「LockID」としてください。それ以外では動きません。
作り終えたら、読み込み・書き込み容量ユニットは最低の1にしておいたほうが良いと思います。

※ S3とDynamoDBをterraformで管理はしないほうが良さげです。
どのみち、初回実行時に「そんなリソース無いんだけど!」ってTerraformが怒ります。

動くまで

今回はState Lockの話がメインなので作るリソースはなんでも良いです。
リージョンが関係無いRoute53を題材にします。

route53.tf
# 適当に1ゾーン作ります。

resource "aws_route53_zone" "test_zone" {
    name    =   "test.lockstate.com"
}
settings.tf
# ネーミングよくないですが、providerとbackendの設定します

provider "aws" {
    access_key = "ACCESS_KEY"
    private_key = "PRIVATE_KEY"
}

terraform {
    backend "s3" {
        bucket     = "terraform-s3-state"
        key        = "terraform.tfstate"
        region     = "s3とdynamoがいるregion"
        lock_table = "terraform-state-lock"
    }
}

これで、「該当のディレクトリに移動して」$ terraform plan してください。(怒られます。)

Backend reinitialization required. Please run "terraform init".
Reason: Initial configuration of the requested backend "s3"

The "backend" is the interface that Terraform uses to store state,
perform operations, etc. If this message is showing up, it means that the
Terraform configuration you're using is using a custom configuration for
the Terraform backend.

とりあえず terraform init しろよと言われるので言われるがままに。

$ terraform init

ただし、以下の通り、認証情報がねーんだけど!って怒られる。なんやねん。

Initializing the backend...

Error configuring the backend "s3": No valid credential sources found for AWS Provider.
  Please see https://terraform.io/docs/providers/aws/index.html for more information on
  providing credentials for the AWS Provider

Please update the configuration in your Terraform files to fix this error
then run this command again.

ここらへんちゃんとよくわかってないですが、この問題の対処として2つありました。
A. settings.tfのbackendにaccess_key/secret_keyの2つを渡してCredential情報を平文で書く。
-> 変数でいいじゃん!と思ったんですが、変数で渡すと、Terraformがよしなにやる「前に」
  Backendが立ち上がるために違う方法で渡してくれ、と怒られました。
以下のようなメッセージ

% terraform init 
Initializing the backend...
Error loading backend config: 1 error(s) occurred:

* terraform.backend: configuration cannot contain interpolations

The backend configuration is loaded by Terraform extremely early, before
the core of Terraform can be initialized. This is necessary because the backend
dictates the behavior of that core. The core is what handles interpolation
processing. Because of this, interpolations cannot be used in backend
configuration.

If you'd like to parameterize backend configuration, we recommend using
partial configuration with the "-backend-config" flag to "terraform init".

B. 環境変数にaccess_key/secret_keyの2つを食わせる
  → 今はこちらを採用中。

init or plan実行時に、状況に応じて「ローカルのStateをS3にコピーするか / S3からStateファイルをローカルにコピーするか」聞かれるかもしれません。
初回(もしくはテスト)であればYes、すでに何回か実行しているような状況ではnoでいいんじゃないでしょうか。

これで、terraform plan (or apply)を実行すると
DynamoDBのLockDBに対して誰が使用しているか、のロック情報が書き込まれ
終わったタイミングでリリースされます。

ターミナルやシェルを複数立ち上げ、同じぐらいのタイミングで
terraform plan( or apply )を実行すると、ロックが成功できた奴以外はエラーで落とされます。
ただし、ロックがリリースされる前の実行中などにCtrl-Cなどで強制終了させるとロックが残り続けるので注意。
$ terraform force-unlock ID情報
で強制ロック解除できます。

今僕が解決できてない問題点

公式ドキュメントを見る限り、「terraform remote コマンドは terraform initコマンドに置き換えたよ!」と言っています。
また、backendを使用する、backendを書き換えた、などのタイミングに起因して terraform init を求められます。

が、terraform initは、「実行されたディレクトリに対して .terraform/terraform.tfstate」を吐き出します。
そしてそれが無いと怒ります。
まだ試せてはいませんが、以下のようなtfの配置をしていたりした場合
root配下でinitを実行しても、tfファイルがありませんと怒られる状況で
tfファイルがあるディレクトリまで移動しないとinitができない状態です。

$ terraform init ./route53/tf
とするとroot直下にtfファイルがコピーされる状況です。
なので、リソースごとに区切る、とか環境ごとに区切る、とかはうまくできていません。
別の見方をすれば、環境ごとに一つのディレクトリでリソース類は全部その中で管理しろよ!
というHashiCorpからのお達しなのか・・・?
これは、新しく実装されたEnvironmentを試せ、ということなんでしょうか。。。

と思ったらBackendConfigなるものがあるらしいのでそれを試してみよう。

root
├── settings
│   └── tf
│   └── settings.tf
├── route53
│   └── tf
│   └── route53.tf
└── ec2
└── tf
└── ec2.tf

Terraformとの付き合い方がよくわからない

続きを読む

[JAWS-UG CLI] CodeBuild: #4 ビルドの実行

前提条件

CodeBuildへの権限

CodeBuildに対してフル権限があること。

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.57
コマンド
aws --version

結果(例):

  aws-cli/1.11.70 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.5.33

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        CODEB_PROJECT_NAME:  (0.3) ${CODEB_PROJECT_NAME}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CodeBuildにフル権限のあるプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  CODEB_PROJECT_NAME:  (0.3) codebuild-demo-java-20170417

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials 
       | grep '[' 
       | sed 's/[//g' | sed 's/]//g'

結果(例):

  iamFull-prjz-mbpr13
  <CodeBuildにフル権限のあるプロファイル>
変数の設定
export AWS_DEFAULT_PROFILE='<CodeBuildにフル権限のあるプロファイル>'

0.2. リージョンの指定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. プロジェクト名の指定

変数の設定
CODEB_PROJECT_NAME='codebuild-demo-java-20170417'

最終確認

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        CODEB_PROJECT_NAME:  (0.3) ${CODEB_PROJECT_NAME}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <CodeBuildにフル権限のあるプロファイル>
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  CODEB_PROJECT_NAME:  (0.3) codebuild-demo-java-20170417

1. 事前作業

2. Buildの実行

変数の確認
cat << ETX

        CODEB_PROJECT_NAME: ${CODEB_PROJECT_NAME}

ETX
コマンド
aws codebuild start-build 
        --project-name ${CODEB_PROJECT_NAME}

結果(例):

  {
    "build": {
      "buildComplete": false,
      "initiator": "hoge/hoge-session",
      "artifacts": {
          "location": "arn:aws:s3:::artifact-20170417-XXXXXXXXXXXX/codebuild-demo-java-20170417"
      },
      "projectName": "codebuild-demo-java-20170417",
      "timeoutInMinutes": 60,
      "buildStatus": "IN_PROGRESS",
      "environment": {
          "computeType": "BUILD_GENERAL1_SMALL",
          "image": "aws/codebuild/java:openjdk-8",
          "type": "LINUX_CONTAINER",
          "environmentVariables": []
      },
      "source": {
          "type": "S3",
          "location": "src-20170417-XXXXXXXXXXXX/MessageUtil.zip"
      },
      "currentPhase": "SUBMITTED",
      "startTime": 14xxxxxxxx.000,
      "id": "codebuild-demo-java-20170417:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "arn": "arn:aws:codebuild:ap-northeast-1:XXXXXXXXXXXX:build/codebuild-demo-java-20170417:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }
  }

3. 事後作業

3.1. ビルドIDの取得

プロジェクトに存在するビルドのIDを全て取得します。

コマンド
ARRAY_CODEB_BUILD_IDS=$( 
        aws codebuild list-builds-for-project 
          --project-name ${CODEB_PROJECT_NAME} 
          --query 'ids' 
          --output text 
) 
        && echo ${ARRAY_CODEB_BUILD_IDS}

結果(例):

  codebuild-demo-java-20170417:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

プロジェクト内のビルドで完了していない(つまり実行中の)もののIDを取得し
ます。

コマンド
CODEB_BUILD_ID=$( 
        aws codebuild batch-get-builds 
          --ids ${ARRAY_CODEB_BUILD_IDS} 
          --query 'builds[?currentPhase != `COMPLETED`].id' 
          --output text 
) 
        && echo ${CODEB_BUILD_ID}

結果(例):

  codebuild-demo-java-20170417:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

3.2. ビルドフェイズの確認

コマンド
CODEB_BUILD_PHASE=$( 
        aws codebuild batch-get-builds 
          --ids ${CODEB_BUILD_ID} 
          --query "builds[].currentPhase" 
          --output text 
) 
        && echo ${CODEB_BUILD_PHASE}

結果(例):

  PROVISIONING

3.3. ログの確認

コマンド
LOGS_GROUP_NAME=$( 
        aws codebuild batch-get-builds 
          --ids ${CODEB_BUILD_ID} 
          --query "builds[].logs.groupName" 
          --output text 
) 
        && echo ${LOGS_GROUP_NAME}

結果(例):

  /aws/codebuild/ codebuild-demo-java-20170417
コマンド
LOGS_STREAM_NAME=$( 
        aws codebuild batch-get-builds 
          --ids ${CODEB_BUILD_ID} 
          --query "builds[].logs.streamName" 
          --output text 
) 
        && echo ${LOGS_STREAM_NAME}

結果(例):

  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
コマンド
aws logs get-log-events 
        --log-group-name ${LOGS_GROUP_NAME} 
        --log-stream-name ${LOGS_STREAM_NAME}

3.4. ビルドフェイズの確認

コマンド
CODEB_BUILD_PHASE=$( 
        aws codebuild batch-get-builds 
          --ids ${CODEB_BUILD_ID} 
          --query "builds[].currentPhase" 
          --output text 
) 
        && echo ${CODEB_BUILD_PHASE}

結果(例):

  COMPLETED

3.5. ビルドステータスの確認

コマンド
CODEB_BUILD_STATUS=$( 
        aws codebuild batch-get-builds 
          --ids ${CODEB_BUILD_ID} 
          --query "builds[].buildStatus" 
          --output text 
) 
        && echo ${CODEB_BUILD_STATUS}

結果(例):

  SUCCEEDED

3.6. View Summarized Build Information

コマンド
aws codebuild batch-get-builds 
        --ids ${CODEB_BUILD_ID}

結果(例):

  {
    "buildsNotFound": [],
    "builds": [
      {
          "buildComplete": true,
          "phases": [
              {
                  "phaseStatus": "SUCCEEDED",
                  "endTime": 14xxxxxxxx.543,
                  "phaseType": "SUBMITTED",
                  "durationInSeconds": 0,
                  "startTime": 14xxxxxxxx.977
              },
              {
                  "contexts": [],
                  "phaseType": "PROVISIONING",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 15,
                  "startTime": 14xxxxxxxx.543,
                  "endTime": 14xxxxxxxx.654
              },
              {
                  "contexts": [],
                  "phaseType": "DOWNLOAD_SOURCE",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 4,
                  "startTime": 14xxxxxxxx.654,
                  "endTime": 14xxxxxxxx.34
              },
              {
                  "contexts": [],
                  "phaseType": "INSTALL",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 0,
                  "startTime": 14xxxxxxxx.34,
                  "endTime": 14xxxxxxxx.454
              },
              {
                  "contexts": [],
                  "phaseType": "PRE_BUILD",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 0,
                  "startTime": 14xxxxxxxx.454,
                  "endTime": 14xxxxxxxx.559
              },
              {
                  "contexts": [],
                  "phaseType": "BUILD",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 59,
                  "startTime": 14xxxxxxxx.559,
                  "endTime": 14xxxxxxxx.448
              },
              {
                  "contexts": [],
                  "phaseType": "POST_BUILD",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 0,
                  "startTime": 14xxxxxxxx.448,
                  "endTime": 14xxxxxxxx.541
              },
              {
                  "contexts": [],
                  "phaseType": "UPLOAD_ARTIFACTS",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 0,
                  "startTime": 14xxxxxxxx.541,
                  "endTime": 14xxxxxxxx.329
              },
              {
                  "contexts": [],
                  "phaseType": "FINALIZING",
                  "phaseStatus": "SUCCEEDED",
                  "durationInSeconds": 5,
                  "startTime": 14xxxxxxxx.329,
                  "endTime": 14xxxxxxxx.808
              },
              {
                  "phaseType": "COMPLETED",
                  "startTime": 14xxxxxxxx.808
              }
          ],
          "logs": {
              "groupName": "/aws/codebuild/codebuild-demo-java-20170417",
              "deepLink": "https://console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#logEvent:group=/aws/codebuild/codebuild-demo-java-20170417;stream=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
              "streamName": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
          },
          "artifacts": {
              "location": "arn:aws:s3:::artifact-20170417-XXXXXXXXXXXX/codebuild-demo-java-20170417"
          },
          "projectName": "codebuild-demo-java-20170417",
          "timeoutInMinutes": 60,
          "initiator": "hoge/hoge-session",
          "buildStatus": "SUCCEEDED",
          "environment": {
              "computeType": "BUILD_GENERAL1_SMALL",
              "image": "aws/codebuild/java:openjdk-8",
              "type": "LINUX_CONTAINER",
              "environmentVariables": []
          },
          "source": {
              "type": "S3",
              "location": " src-20170417-XXXXXXXXXXXX /MessageUtil.zip"
          },
          "currentPhase": "COMPLETED",
          "startTime": 14xxxxxxxx.977,
          "endTime": 14xxxxxxxx.808,
          "id": "codebuild-demo-java-20170417:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
          "arn": "arn:aws:codebuild:ap-northeast-1:XXXXXXXXXXXX:build/codebuild-demo-java-20170417:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      }
    ]
  }

3.7. Get the Build Output Artifact

コマンド
aws s3 ls s3://${S3_BUCKET_ARTIFACTS} --recursive

結果(例):

  2017-04-1700:01:14       2065 codebuild-demo-java-20170417/target/target/messageUtil-1.0.jar

完了

続きを読む

Amazon RDS のエラーログをCLIで取得する。

はじめに

Amazon RDS for MySQL においてエラーログを取得したいと思います。
一般ログとスローログに関してはテーブルへ出力させることができるため、mysqlコマンドでも取得できるようになります。ですがエラーログはできないみたいです。
MySQL エラーログにアクセスする
なのでAWS CLIを利用することで取得したいと思います。

ログファイルの確認

まずはログを確認します。RDSのログを確認するには describe-db-log-files を使います。

ログファイル一覧
$ aws rds describe-db-log-files --db-instance-identifier rds001
{
    "DescribeDBLogFiles": [
        {
            "LastWritten": 1491455703000, 
            "LogFileName": "error/mysql-error-running.log", 
            "Size": 19425
        }, 
        {
            "LastWritten": 1491436546000, 
            "LogFileName": "error/mysql-error-running.log.0", 
            "Size": 63825
        }, 
        {
            "LastWritten": 1491440100000, 
            "LogFileName": "error/mysql-error-running.log.1", 
            "Size": 62255
...

ですが、JSON形式だと見づらいのですね。 –filename-contains error とすることで error ログのみを抽出することができ、さらに –output text で一覧表示させることができます。

エラーログ一覧
$ aws rds describe-db-log-files --db-instance-identifier rds001 --filename-contains error --output text
DESCRIBEDBLOGFILES  1491469200000   error/mysql-error-running.log   0
DESCRIBEDBLOGFILES  1491436546000   error/mysql-error-running.log.0 63825
DESCRIBEDBLOGFILES  1491440100000   error/mysql-error-running.log.1 62255
DESCRIBEDBLOGFILES  1491404111000   error/mysql-error-running.log.15    72150
DESCRIBEDBLOGFILES  1491407728000   error/mysql-error-running.log.16    72150
DESCRIBEDBLOGFILES  1491411306000   error/mysql-error-running.log.17    70300
DESCRIBEDBLOGFILES  1491414901000   error/mysql-error-running.log.18    71225
DESCRIBEDBLOGFILES  1491418516000   error/mysql-error-running.log.19    68450
DESCRIBEDBLOGFILES  1491443708000   error/mysql-error-running.log.2 62620
DESCRIBEDBLOGFILES  1491422125000   error/mysql-error-running.log.20    67525
DESCRIBEDBLOGFILES  1491425715000   error/mysql-error-running.log.21    65675
DESCRIBEDBLOGFILES  1491429300000   error/mysql-error-running.log.22    65675
DESCRIBEDBLOGFILES  1491432938000   error/mysql-error-running.log.23    65675
DESCRIBEDBLOGFILES  1491447334000   error/mysql-error-running.log.3 61050
DESCRIBEDBLOGFILES  1491450949000   error/mysql-error-running.log.4 61050
DESCRIBEDBLOGFILES  1491454500000   error/mysql-error-running.log.5 59200
DESCRIBEDBLOGFILES  1491458107000   error/mysql-error-running.log.6 59200
DESCRIBEDBLOGFILES  1491461705000   error/mysql-error-running.log.7 58275
DESCRIBEDBLOGFILES  1491465304000   error/mysql-error-running.log.8 56425
DESCRIBEDBLOGFILES  1491468300000   error/mysql-error-running.log.9 45012
DESCRIBEDBLOGFILES  1491491100000   error/mysql-error.log   0

エラーログは mysql-error-running.log という名前で1時間ごとに出力されます。

エラーログの取得

では実際にエラーログの中身を見て見ます。
エラーログは download-db-log-file-portion コマンドを使います。

エラーログ
$ aws rds download-db-log-file-portion --db-instance-identifier rds001 --log-file-name error/mysql-error-running.log.6

ここでも –output text として通常のログと同じように出力させたいと思います。

エラーログ
$ aws rds download-db-log-file-portion --db-instance-identifier rds001 --log-file-name error/mysql-error-running.log.6 --output text
2017-04-06 04:55:08 3372 [Note] Error reading relay log event: slave SQL thread was killed
2017-04-06 04:55:08 3372 [Note] Slave I/O thread killed while connecting to master
2017-04-06 04:55:08 3372 [Note] Slave I/O thread exiting, read up to log 'db001-bin.000002', position 353
2017-04-06 04:55:09 3372 [Warning] Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
2017-04-06 04:55:09 3372 [ERROR] Slave I/O: error connecting to master 'repl@10.0.0.205:3306' - retry-time: 60  retries: 1, Error_code: 2003
2017-04-06 04:55:09 3372 [Note] Slave SQL thread initialized, starting replication in log 'db001-bin.000002' at position 353, relay log '/rdsdbdata/log/relaylog/relaylog.001308' position: 236
...

これでエラーログの確認ができました。

ログについて

  • エラーログは mysql-error.log ファイルへ書き込まれる。
  • mysql-error.logは5分ごとにフラッシュされる。
  • フラッシュされた内容は mysql-error-running.log へ追加される。
  • mysql-error-running.log は1時間ごとにローテーションされる。
  • ローテーションされたログは24時間保持される。
  • 各ログファイルはUTC時間がファイル名に付加される。(ローテーションされたUTC時刻の%H)
  • エラーログへの書き込みは 「起動時」、「シャットダウン時」、「エラー検出時」にのみ行われる。

ポリシー

CLIで取得する際のポリシーです。

policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1491493558000",
            "Effect": "Allow",
            "Action": [
                "rds:DescribeDBLogFiles",
                "rds:DownloadCompleteDBLogFile",
                "rds:DownloadDBLogFilePortion"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
Actions Description
DescribeDBLogFiles DBインスタンスのログをリストする。
DownloadCompleteDBLogFile 指定したデータベースログファイルの内容をダウンロードする。
DownloadDBLogFilePortion 指定されたログファイルの全部または一部をダウンロードする(最大1 MBのサイズ)。

おわりに

CLIを利用するとエラーログも取得できるようになります。ですが、やっぱりスローログや一般ログと同じようにtableへ出力できるようになって欲しいですね。。。

続きを読む

[stripe][AWS] Stripe の Webhook を API Gateway で受け取って Lambda で処理する

Serverless Framework 使って Stripe の Webhook からのメッセージを処理するサンプル書いたので、使い方とか解説。
コード自体は Serverless Examples にプルリクしてマージしてもらってあるので、そちらを取ってきてください。
https://github.com/serverless/examples/tree/master/aws-node-stripe-integration

これは Stripe の Webhook から投げられるメッセージを AWS API Gateway 経由で AWS Lambda で処理したい時に使います。

セットアップ

Serverless Framework のインストール方法は割愛。
Installing The Serverless Framework を参考にしてね。

サンプルコードの取得

https://github.com/serverless/examples/ を git clone するなりしてから、aws-node-stripe-integration ディレクトリを持ってきてください。

追記: GitHub 上のプロジェクトは serverless install で一発コピーできるそうです。thx @horike37

$ serverless install --url https://github.com/serverless/examples/tree/master/aws-node-stripe-integration
Serverless: Downloading and installing "aws-node-stripe-integration"...
Serverless: Successfully installed "aws-node-stripe-integration"

依存 npm パッケージのインストール

この example では、npm パッケージ config, js-yaml, stripe を使ってるので、まずは npm install で依存パッケージをインストール。

npmパッケージのインストール
$ npm install

Stripe API Key の書き換え

Stripe の API Key を config/local.yaml ってファイルに書いてください。
ソースを GitHub 等で公開しないのであれば、config/default.yaml の中身を書き換えてもいいよ。

config/local.yaml
stripe:
    test_sk: 'Stripe_Test_Secret_Key_here'
    live_sk: 'Stripe_Live_Secret_Key_here'

サンプルコードの解説

event object の取得

実際に Webhook から送られたデータを処理しているのは handler.js です。
Webhook 経由で event object が投げられると event.body に JSON 形式でセットされてくるので、最初にそれを JSON.parse() してます。
https://github.com/serverless/examples/blob/master/aws-node-stripe-integration/handler.js#L18

event object の構造は、Stripe API リファレンスを参考にしてください。
https://stripe.com/docs/api#event_object

event object の正当性チェック

events.retrieve() で、受け取った event object が正当なものかをチェックしてます。
https://github.com/serverless/examples/blob/master/aws-node-stripe-integration/handler.js#L22

events.retrieve() から呼び出される callback 関数内では err だった場合の処理を入れてないので、これは実装してください。

event type ごとの処理

受け取った event の種類は stripeEvent.type を見ればわかります。
それに合わせて Slack 通知するなりなんなりの処理を実装してくださいね。
https://github.com/serverless/examples/blob/master/aws-node-stripe-integration/handler.js#L33-L40

event type のリストは、Stripe API リファレンスを参考にしてください。
https://stripe.com/docs/api#event_types

デプロイ!

AWS API Gateway エンドポイントの作成

まずは、そのまま sls deploy すると Test 環境用の一式がデプロイされます。

Test環境用
$ serverless deploy
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (2.15 MB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.....
Serverless: Stack update finished...
Serverless: Removing old service versions...
Service Information
service: aws-node-stripe-integration
stage: development
region: us-east-1
api keys:
  None
endpoints:
  POST - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/test/stripe/incoming
functions:
  incoming: aws-node-stripe-integration-test-incoming

Stack Outputs
ServiceEndpoint: https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/test
ServerlessDeploymentBucketName: aws-node-stripe-integration-serverlessdeploymentbuck-xxxxxxxxxxxx
IncomingLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:000000000000:function:aws-node-stripe-integration-test-incoming:20

endpoints ってのが、API Gateway の endpoint になります。

本番環境( Live )用の API Gateway endpoint を作成するには --stage live をつけてデプロイ

Live環境用
$ serverless deploy --stage live

Stripe Webhook の設定

Stripe の管理画面から Webhook URL を設定します。
https://dashboard.stripe.com/account/webhooks の「Add endpoint」をクリック。
Screen Shot 2017-04-07 at 12.58.07.png

Test 環境用の Webhook を設定したい場合は「Mode」Test を、Live 環境用の Webhook を設定したい場合は「Mode」Live を選択して「URL」欄に先ほど serverless deploy した時に作成された API Gateway の endpoint を入力して「Create endpoint」をクリックするだけです。

Serverless Framework のおかげで、簡単にできちゃいますねー。
現場からは以上です。

続きを読む

メールの送信ができない!550-5.7.1 this message does not meet IPv6 sending guidelines regarding PTR(postfix)

はじめに

postfixでメールサーバーを構築し、送信テストを行ったら送信できたりできなかったり、送信できても迷惑メールBoxに入ったり、入らなかったり。。。。。
送信できなかった時は、自分自身にメールが返ってきていました。。。
送信できる時とできない時がある。
迷惑メールに行く時といかない時がある。
原因がわかったので、解決方法をまとめます!

送信できなかった時に返ってきたメールは下記のような感じです。
5.7.1.エラーっぽいですが。。。。

# less /home/test/Mailbox ※該当箇所のみ記載
Status: 5.7.1
Remote-MTA: dns; gmail-smtp-in.l.google.com
Diagnostic-Code: smtp; 550-5.7.1 [2406:da14:f9e:2700:ac0a:aa1d:af29:fb6d] Our
    system has detected that 550-5.7.1 this message does not meet IPv6 sending
    guidelines regarding PTR 550-5.7.1 records and authentication. Please
    review 550-5.7.1  https://support.google.com/mail/?p=IPv6AuthError for more
    information 550 5.7.1 . 36si6344033pgx.243 - gsmtp

原因

postfixがIPv6で送信しようとしたが、IPv6の設定をしてなかった。。。。

postfixは、デフォルトでIPv6を優先して使用しようとします!!

該当箇所は下記の通りです!
現状の設定を確認しましょう!!

# vi /etc/postfix/main.cf ※該当箇所のみ記載

119行目 inet_protocols = all ←★使用するプロトコルが「全て」になっていました。このままだと、IPv6を優先して送信しようとします。

このままの設定で、IPv4しか持っていないサーバーで送信しようとすると、「はじめに」で述べたような事象が発生します!!

解決策

解決策①(サーバーにIPv4しかない場合)

使用するプロトコルをIPv4に変更する!

●設定変更
# vi /etc/postfix/main.cf ※該当箇所のみ記載

119行目 inet_protocols = ipv4 ←★使用するプロトコルを「IPv4」に変更。


●設定反映
# systemctl restart postfix

ちなみに、「ipv4」を大文字で「IPv4」と書くと、postfixの構文エラーになります。

解決策②(サーバーがIPv4, IPv6のどちらにも対応している場合)

使用している送信アドレスにAAAAレコードを設定する!(DNSサーバー内)
一応設定内容も下記に記載します。

# vi /etc/postfix/main.cf ※該当箇所のみ記載

119行目 inet_protocols = all 

AAAAレコード設定方法(Route53)

ドメイン取得済み、IPv6を設定済みであることが前提です。

1.AWSコンソールにログイン

2−1.レコードの登録

[サービス]→[Route53]→[Hosted Zone]→[取得済みのドメインを押下]
スクリーンショット 2017-03-26 12.22.31.png

2−2.レコードの登録

[Create Record Set]→[Name: 送信アドレス(未記入でも可)]→[Type: AAAA – IPv6 address]→[Value: IPv6アドレス ]→[Create]
スクリーンショット 2017-04-01 22.44.05.png
登録後は下記のように、表示されます。
スクリーンショット 2017-04-01 22.49.41.png

3.浸透の確認(登録されたか確認)

下記コマンドを実行します。

# dig smtp.perfectakahashi.com aaaa

; <<>> DiG 9.9.4-RedHat-9.9.4-38.el7_3.2 <<>> smtp.perfectakahashi.com aaaa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25230
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;smtp.perfectakahashi.com.  IN  AAAA

;; ANSWER SECTION:
smtp.perfectakahashi.com. 60    IN  AAAA    2406:da14:f9e:2700:ac0a:aa1d:af29:fb6d ←★ここを確認!設定したIPv6が表示されることを確認。

;; Query time: 128 msec
;; SERVER: 172.31.0.2#53(172.31.0.2)
;; WHEN: 土  4月 01 22:53:45 JST 2017
;; MSG SIZE  rcvd: 81

これで設定は完了です!
メールを送信してみてください!

AWSでIPv6を設定する方法は、下記URLを参考にしてください。

【手順】iOSのAPIサーバーに必要なIPv6をEC2に設定してみた(VPC)

続きを読む

AWS g2インスタンスにCUDAを入れる際の落とし穴(2017年3月版)

概要

chainer/Tensorflowで使うことを目的に、g2インスタンスにCUDAをインストールしました。

もちろん既にCUDAインストール済みのAmazonLinuxを使用するという選択肢もありますが、今回はUbuntuが使いたかったためまっさらの状態からCUDAを入れました。その際にハマった箇所をメモします。

※ 本手順ではCUDA7.5を指定しましたが、CUDA8.0でも同様と思います。

EC2インスタンスの構成

CUDAのインストールを始める前に、この作業を行ったEC2のインスタンスの設定は以下の通り。

  • AWS EC2のインスタンス(g2.2xlarge)
  • AMIはUbuntu Server 14.04 LTS (HVM)

【参考】Ubuntu14.04にcuda 7.5をインストール

注意点としては、EBSの領域(OSが入る領域)がデフォルトで8GBです。作業領域はS3をマウントするにしても、後々窮屈になるので16GBへの変更がオススメです。

【参考】http://qiita.com/pyr_revs/items/e1545e6f464b712517ed

基本的なインストールの流れ

基本的に流れは、以下いずれかの手順の通りです。

【参考】Ubuntu14.04にcuda 7.5をインストール
もしくは
【参考】 http://www.cs.stevens.edu/~kbatsos/awscudasetup.html

なお、Nvidiaグラフィックドライバは最新のものではなく、Version367系の最後のものを選びます。理由は後述。

落とし穴

上記いずれの手順でも、CUDAインストール後 deviceQuery でCUDAの動作確認をする段階で、

$ cd /usr/local/cuda/samples/1_Utilities/deviceQuery
$ sudo make
$ ./deviceQ uery
./deviceQuery Starting...

CUDA Device Query (Runtime API) version (CUDART static linking)

cudaGetDeviceCount returned 30
-> unknown error
Result = FAIL

といったエラーになります。その他、以下のようなエラーが確認できます。

$ dmesg
(中略)
[ 568.286327] NVRM: The NVIDIA GRID K520 GPU installed in this system is
[ 568.286327] NVRM: supported through the NVIDIA 367.xx Legacy drivers. Please
[ 568.286327] NVRM: visit http://www.nvidia.com/object/unix.html for more
[ 568.286327] NVRM: information. The 375.26 NVIDIA driver will ignore
[ 568.286327] NVRM: this GPU. Continuing probe...
$ nvidia-smi

NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
$ sudo modprobe nvidia

modprobe: ERROR: could not insert 'nvidia_375': No such device

原因

  1. g2インスタンスのGPUであるK520は、NVIDIAグラフィックドライバーVersion367.xxまでしか対応していない
  2. CUDAインストーラは、CUDAを入れるついでにNVIDIAグラフィックドライバーを最新(367.xxより新しいもの)に更新してしまう。GPUの対応状況を確認せずに! ← コイツがお馬鹿

というコンボにより、上記の現象が発生します。

対処法

対処はシンプルで、上記の落とし穴にはまった後、以下のコマンドでドライバをVersion367へ戻すことで解消します。

$ sudo apt-get install -y nvidia-367

【参考】 https://github.com/NVIDIA/nvidia-docker/issues/319

対処した後の確認結果

成功した場合、以下のような表示となります。

cd /usr/local/cuda/samples/1_Utilities/deviceQuery
sudo make
./deviceQuery
(中略)
deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 8.0, CUDA Runtime Version = 7.5, NumDevs = 1, Device0 = GRID K520
Result = PASS
$ sudo modprobe nvidia
(何もメッセージが出ない)
$ sudo dpkg -l | grep nvidia

ii nvidia-367 367.57-0ubuntu0.14.04.1 amd64 NVIDIA binary driver - version 367.57
rc nvidia-375 375.26-0ubuntu1 amd64 NVIDIA binary driver - version 375.26
ii nvidia-modprobe 375.26-0ubuntu1 amd64 Load the NVIDIA kernel driver and create device files
ii nvidia-opencl-icd-367 367.57-0ubuntu0.14.04.1 amd64 NVIDIA OpenCL ICD
rc nvidia-opencl-icd-375 375.26-0ubuntu1 amd64 NVIDIA OpenCL ICD
ii nvidia-prime 0.6.2.1 amd64 Tools to enable NVIDIA's Prime
ii nvidia-settings 375.26-0ubuntu1 amd64 Tool for configuring the NVIDIA graphics driver

※ iiはインストール済み、rcは削除済みだがconfigは残ってる の意味

【おまけ】 CUDAインストールの後の手順のハマりどころ

cuDNNを入れた後、tensorflow/chainerのGPUインストールが上手くいかないのは大抵、下記のいずれかが原因

  • 環境変数の設定が足りない。 → 公式のインストール手順を再確認
  • 一般ユーザの.bashrcに環境変数の設定をしておいて、sudo pip install している。pip install –user chainer でユーザ単位でインストールするか、virtualenvを使うのが簡単

感想など

対策をサラッと書いてますが、これに気付くまでが結構長かった。何か手順を間違えたかとやり直したり、ドライバのバージョンが原因と気付いてもどのタイミングで最新にされてしまうのかが分からずで半日以上潰しましたよね orz

結局 ググってヒットした上記のgithubのissueで、「オレも、オレも同じ感じでダメ!」って質問したらNVIDIAの中の人が即レスしてくれて一発解決&感謝だったわけですが… うーん 複雑な気持ち^^;

【追記】既に同じような記事が…

Twitterのツイート検索をしてみたところ、AWS E2 G2インスタンス上にKeras環境を構築する 2017年2月版 同様の手順を実施されている記事がありました。タイトルの最後に~年~月版と入れているところがソックリで笑ってしまいました^^;

この方の場合は、CUDAのインストール → Nvidiaグラフィックドライバの順にインストールを行っているので、この記事で起きているような現象に出遭わずに済んでいるようです。このやり方がスマートかも。

続きを読む

serverlessでLambdaのローカル開発環境を整える

serverlessフレームワークを使ってローカル環境で作成したNode.jsをLambda関数としてデプロイ

環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1212

$ brew -v
Homebrew 1.1.11
Homebrew/homebrew-core (git revision 0e1e; last commit 2017-03-11)

$ node -v && npm -v
v6.9.2
3.10.9

やったこと

何はともあれServerlessのインストール

$ npm install serverless -g

AWS CLIのインストール

$ brew install awscli
$ aws
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help
aws: error: too few arguments

AWS CLIの設定
Access Keyで設定する場合

  • 事前にIAMでグループ権限とユーザーに対するグループ追加を行う必要あり
$ aws configure
AWS Access Key ID [None]: ABCABCABCABCABCABC12
AWS Secret Access Key [None]: BCDBCDBCDBCDBCDBCDBCD+123+EFGEFGEFG45678
Default region name [None]: us-east-1
Default output format [None]: json

※AWS リージョンコード一覧
http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-regions-availability-zones.html

確認

AWC CLIが使えるかの確認をします。
ここでは、EC2のインスタンス一覧表示コマンドを投げました。
(結果は何もないのでからのjsonが返ってくる。

aws ec2 describe-instances
{
    "Reservations": []
}

使ってみる

プロジェクト作成

まずはプロジェクトを作成
公式ドキュメントにある、node.jsテンプレートを使用し、プロジェクトを作成

$ serverless create --template aws-nodejs --path serverless-test
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/d_ojima/Documents/serverless-test"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  ___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.8.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"

それっぽいロゴが表示されます。
テンション上がりますね:watermelon:

内容を確認

$ cd serverless-test/ && tree .
.
├── handler.js
└── serverless.yml

0 directories, 2 files

デプロイ

handler.jsにサンプルが書かれている状態なので、このままデプロイしてみます。

$ serverless deploy -v
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-test-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - serverless-test-dev
Serverless: Stack create finished...
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (583 B)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - serverless-test-dev
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-test-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersionmQpsI2bHdSCO39nFrbUuIDXStzt10M9R68vwOUGdtc
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersionmQpsI2bHdSCO39nFrbUuIDXStzt10M9R68vwOUGdtc
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - HelloLambdaVersionmQpsI2bHdSCO39nFrbUuIDXStzt10M9R68vwOUGdtc
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-test-dev
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - serverless-test-dev
Serverless: Stack update finished...
Service Information
service: serverless-test
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  None
functions:
  hello: serverless-test-dev-hello

Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:932608639237:function:serverless-test-dev-hello:1
ServerlessDeploymentBucketName: serverless-test-dev-serverlessdeploymentbucket-1gve57f768zkr

ブラウザからAWSコンソールを確認すると

serverlessでデプロイしたLambda関数が確認できました:airplane:
スクリーンショット 2017-03-11 18.53.10.png

また、serverlessではCloudFormationの機能を利用しているため、同時にIAMロールS3バケットなども作成されていました。

雑感

とりあえず環境構築ということで、serverlessの触り中の触りを試してみた。
今後は、外部パッケージを使ったラムダ関数の作成、API Gatewayとの連携などを試してみたい。

続きを読む