Serverless FrameworkでEC2をスケジュール起動/停止するテンプレート(Lambda[java])

Serverless Framework

はじめに

  • コンテナ付いてる昨今は、久しくServerless Framework触って無かったのですが、EC2を8時に起動して、20時半に停止する要件が浮上したので、サクッとslsで作りました。
  • ソースはGithubで公開してます。
  • 至極簡単な内容なので、速攻実現出来ると思ってます。

環境のセットアップ

Serverless FrameworkでDeploy

ソースの取得

  • 以下のGithubからソースを取得します。
$ git clone https://github.com/ukitiyan/operation-ec2-instance.git

STS(Eclipse)にインポート

  • STSを起動して、Project Explorer -> 右クリック -> Maven -> Existing Maven Projectsで先程Githubから取得した「operation-ec2-instance」フォルダを選択します。

serverless.yml の修正 + Build

  • serverless.ymlのL37 周辺の設定を適宜修正します。

    • rate: AWS Lambda – Scheduled EventのCron書式
      を参考に UTC で記載
    • instanceId: 対象インスタンスのinstanceIdを記載
    • scheduleは、縦に増やせるので複数インスタンスに対応できます。(それを踏まえて環境変数でinstanceIdを指定してません)
serverless.yml
- schedule:
    rate: cron(30 11 * * ? *)
    input:
      goal: stop
      instanceId: i-XXXXXXXXXXXXXXXXX
- schedule:
    rate: cron(0 23 * * ? *)
    input:
      goal: start
      instanceId: i-XXXXXXXXXXXXXXXXX
  • プロジェクトを右クリック -> Run As -> Maven Install でビルドします。
  • target配下にoperation-ec2-instance.1.0.0.jarが出来上がります。

Deploy

  • 例のごとく、以下のコマンド一発です。
$ cd operation-ec2-instance
$ serverless deploy -v
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
・
・
Serverless: Stack update finished...
Service Information
service: operation-ec2-instance
stage: prod
region: ap-northeast-1
api keys:
  None
endpoints:
  None
functions:
  aws-java-maven-prod-hello: arn:XXXXXXXX
  • 以下のJsonでコンソールから test するか、設定時間になるまで待って、問題ないか気にしておきましょう。
{
  "goal": "stop",
  "instanceId": "i-XXXXXXXXXXXXXXXXX"
}

まとめ

  • まぁ、やっつけですが。速攻実現出来ました。
  • STSで完結するのが、アプリ屋にとっては本当うれしいです。

続きを読む

AWSのIAMロール・ポリシーをソースコード管理するのに役立つツールライブラリ

AWS の IAM ロールとポリシーをどうやって管理していますか?

JSON ファイルでロールとポリシー定義を管理できるのに便利なツール(npmライブラリ)を作りました。
github.com/tilfin/aws-iam-policy-tool

できること

  • ローカルの JSON ファイルと IAM のロールとポリシー定義の相互インポート・エクスポート
  • ローカルの定義ファイルに沿って IAM ロールとポリシーが構成されているかの検証
  • 正規表現マッチするカスタムポリシーの複数削除(AWS Management Console では一個ずつしか消せない)

できないこと

  • ロールのインラインポリシーはサポートしていない

どのように使えるのか

  • IAM ロールとポリシー定義を JSON ファイルでソースコード同様にGit等のリポジトリで管理できる。
  • npm ライブラリなので Lambda から定期的にデプロイ環境のロールとポリシーが正しいかの検証ができる。

定義ファイルの仕組み

  • IAM のブラウザ上で編集する JSON とほぼ同じです。
  • デプロイ環境別にリソース名に prefix/suffix を付けている場合は ENV としておくことで、コマンド実行時に引数で指定します。
  • AWS アカウント ID も ACCOUNT_ID でコマンド実行時に引数指定することで変えることができます。

ロール

{
    "Role": {
        "RoleName": "yourapp-ec2-api-ENV",
        "Path": "/",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "ec2.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    },
    "AttachedPolicies": [
        {
            "PolicyName": "yourapp-s3-storage-ENV",
            "PolicyArn": "arn:aws:iam::ACCOUNT_ID:policy/yourapp-s3-storage-ENV"
        }
    ]
}

ポリシー

ポリシー名は内容に含まれないため JSON ファイル名そのものが適用されます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::yourapp-storage-ENV/*"
        }
    ]
}

導入と使い方

npm からインストールします。

$ npm install -g aws-iam-policy-tool

現状のロールを ./roles とポリシーを ./policies にそれぞれ書き出します。

$ awsiamtool export-role ./roles
$ awsiamtool export-policy ./policies

各ファイルの ARN 内の AWS アカウント ID の部分を ACCOUNT_ID に置換します。動作環境を示すもの (例. dev, staging など) が名前に含まれていれば、ENV に置換します。ポリシーの場合はファイル名もリネームします。

検証してみます。

$ awsiamtool validate-role -i 111122223333 -e dev roles
$ awsiamtool validate-policy -i 111122223333  -e dev policies

本番環境に定義を作成します。

$ AWS_PROFILE=prod awsiamtool import-policy -i 111122224444 -e prd policies
$ AWS_PROFILE=prod awsiamtool import-role -i 111122224444 -e prd roles

※ポリシー修正時 import-policy に上書きオプションを --overwrite を指定します。

ライブラリとして使う(別の変数を適用するには)

現状コマンド引数ではサポートしていませんがライブラリとして使えば可能です。

const awsIamPolicyLib = require('aws-iam-policy-tool');

const opts = {
  json: true,
  overwrite: true
};

const varSets = {
  ACCOUNT_ID: '000011112222',
  ENV: 'stg',
  COMMON_ACCOUNT_ID: '333344445555'
};

awsIamPolicyLib.importPolicy('./policies', varSets, opts);
.then(() => {
  return awsIamPolicyLib.importRole('./roles', varSets, opts);  
})
.then(() => { console.info('Importing done') })
.catch(err => { console.error(err) });

その他、コマンドの実行結果例などは README を見てください。

続きを読む

Fessでドキュメント検索環境を構築した話

やったこと

  • プロジェクトの新規ドキュメントはGitBookで作成しGitで管理
  • 既存のドキュメント(Word、Excel、PDF、テキスト程度のメモ書きなど)はGitに登録
  • GitbookのBuildはcommitが行われる度にJenkinsで自動的に実行
  • Gitに登録されたドキュメントはFessサーバ内に日に1回pullする
  • ドキュメント群をFessで毎日クローリングしインデックス作成
  • 上記はすべてAWSで構築

全体構成

ざっくりとしたものですがこんな感じです。

キャプチャ.PNG

GitBook構築

GitBookインストール

https://www.gitbook.com/

ざっくりと言うと、マークダウン形式でドキュメントを作成し、ビルドすることによって
htmlやPDFを作成し、公開したりプロジェクト内に展開できるツールです。
今回はAWSのEC2上に環境を作成しましたが、GitBook.com上でも行えますので用途に応じて変えたらよいと思います。

インストールに関しては、検索して出てきた先達の方々のを参考にして行いましたので、こちらでは割愛します。

プロジェクトメンバーに公開する

公開に用いたWebサーバはapacheの2.2系を用いました。
GitBookはプロジェクト内のチーム毎に作成しており、都度増減します。
その度にapacheのconfを書き換えるのは面倒なので、VertualDocumentRootを利用しました。
AWSのRoute53で社内用プライベートドメインを割り当て、そのサブドメイン毎にGitBookとJenkinsのジョブを作成します。
workspace以下をドキュメントルートとすることで定義の書き換えの必要性をなくしています。

<VirtualHost *:80>
  ServerName [プライベートドメイン]
  ServerAlias *.[プライベートドメイン]
  VirtualDocumentRoot /var/lib/jenkins/workspace/%1/_book
  <Directory "/var/lib/jenkins/workspace/%1/_book">
    Options Indexes FollowSymLinks
    AllowOverride All
  </Directory>
</VirtualHost>

Jenkinsとの連携

GitBookとJenkins

Jenkinsの Gitlab Plugin を用いて、GitlabのCommitとビルドを連動するようにしました。

キャプチャ.PNG

既存ドキュメントとJenkins

Jenkins内に最新のドキュメントを保持するのが目的です。
理由は後述するFessで利用するためです。

Fess構築

http://fess.codelibs.org/ja/

OSSの全文検索エンジンです。
公式にも書いてありますが。ElasticSearchを利用しており
WebAPIも対応しているため、WebベースでのGoogleライクな検索もできれば
APICallによるシステム内の検索部品としての役割も持つことができます。

現在はパッケージインストールに対応しているみたいなので、導入は比較的容易だと思います。
私の時(1年半ほど前)はなかった気がする…。

GitBookをクローリング

GitBookの公開URLに対してクローリングさせる設定を行います。
プロジェクト内で使っている名前などは[]書きで置き換えています。
キャプチャ.PNG

既存ドキュメントのファイルクロール

FessにGitで管理しているドキュメントのインデックスを作成させます。
検索対象は、Jenkinsでリポジトリからpullしたドキュメントが格納されているフォルダです。

クロール対象とするパス項目で、検索で見つかるドキュメントをある程度制御しています。
全ファイル対象だと、不要なゴミファイルまで引っかかってしまったためです。

キャプチャ.PNG

構築して思ったこと

  • GitBookを使う場合はマークダウンに慣れていれば楽
  • Fessでの検索はExcelなどの図形内テキストまで拾ってくれるので細かい検索ができる
  • ドキュメントのアップデートが知りやすくなった
  • OSSだけでもそれなりのものが構築できた
  • Jenkinsおじさんは働き者

続きを読む

EC2上にS3をマウントする方法

s3fs-fuseインストール

sudo apt-get update

sudo apt-get install build-essential git libfuse-dev libcurl4-openssl-dev libxml2-dev mime-support automake libtool
sudo apt-get install pkg-config libssl-dev

git clone https://github.com/s3fs-fuse/s3fs-fuse

cd s3fs-fuse/
./autogen.sh
./configure --prefix=/usr --with-openssl

make
sudo make install

参照先s3バケットの設定

sudo touch /etc/passwd-s3fs

sudo vi /etc/passwd-s3fs
# 以下を設定
---
s3パケット名:aws_access_key:aws_secret_key
---

sudo chmod 640 /etc/passwd-s3fs

マウント

sudo mkdir /mnt/s3fs

sudo s3fs s3バケット名:/参照ディレクトリ名 /mnt/s3fs -o allow_other -o use_cache=/tmp

【参考】
https://github.com/s3fs-fuse/s3fs-fuse/wiki/Installation%20Notes#tested-on-ubuntu-1404-lts
http://www.mori-soft.com/2008-08-15-01-36-37/os/215-s3-s3

続きを読む

[JAWS-UG CLI] Alexa Skills Kit Command Line Interface (ASK CLI)のセットアップ

Alexa Skills Kit Command Line Interface (ASK CLI)をセットアップします。

オリジナル手順:https://developer.amazon.com/ja/docs/smapi/quick-start-alexa-skills-kit-command-line-interface.html#step-2-install-and-initialize-ask-cli

前提条件

1. nodeのインストール (nodebrewを使う場合)

コマンド
curl -L git.io/nodebrew | perl - setup
結果
Fetching nodebrew...
Installed nodebrew in $HOME/.nodebrew

========================================
Export a path to nodebrew:

export PATH=$HOME/.nodebrew/current/bin:$PATH
========================================

~/.bashrcにPATHを追記します。

.bashrcに追記
echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bashrc
コマンド
. ~/.bashrc 
コマンド
which nodebrew
結果(例)
/Users/taro/.nodebrew/current/bin/nodebrew

nodeをバイナリでインストールします。(v6.10.0の例)

コマンド
nodebrew install-binary v6.10.0
コマンド
nodebrew use v6.10.0
node -v
結果(例)
v6.10.0

自動的にnpmもインストールされているはずです。

コマンド
which npm
結果(例)
/Users/taro/.nodebrew/current/bin/npm
コマンド
npm -v
結果(例)
3.10.10

2. ASK CLIのインストール

コマンド
npm install -g ask-cli
コマンド
which ask
結果(例)
<HOMEディレクトリ>/.nodebrew/current/bin/ask

3. ASKの初期化

コマンド
ask init --no-browser
出力
There is no AWS credentials setup yet, do you want to continue the initialization? (Default: False)
入力
Y
出力
Paste the following url to your browser:
<URL>

? Please enter your Authorization Code:
  • をコピーしてブラウザのURL欄に貼り付けます。
  • Authorization Codeが表示されるので、黒い枠の中のテキストをコピーします。
  • 上記でターミナルに表示されている”Please enter your Authorization Code:”の後ろにペーストし、エンターキーを押します。
出力
Tokens fetched and recorded in ask-cli config.
Vendor ID set as XXXXXXXXXXXX

Profile [default] initialized successfully.

4. 事後確認

コマンド
ask init -l
結果(例)
Profile              Associated AWS Profile
  [default]                 ** NULL **

~/.ask/cli_configに認証情報が保存されています。

完了

続きを読む

いまからはじめるJavaでAWS Lambda(ラムダ) 前編

いまからはじめるJavaでAWS Lambda(ラムダ)

AWS Lambda関数をJava8で記述し、AWS上 Lambda Functionとしてアップロードし、それをJavaクライアントから呼び出す例をステップ・バイ・ステップで紹介します。

想定読者

  • Javaが書けて、はじめてAWS Lambdaをつかう人
  • いままではnode.jsでLambda関数を作っていたが、わけあってJavaでつくってみようとおもう人(=私のような)

記事構成

TL;DR 前編・後編で書きます

  • 【前編】 JavaでLambda関数(クラウド側)と、クライアント(ローカル側)をお手軽に作る←本稿
  • 【後編】 Lambda関数(クラウド側)の同期型、非同期型の呼び出しタイプ(Invocation Type)と、Javaクライアント側の同期、非同期呼び出し、API Gatewayでの公開

AWS Lambda(ラムダ)とは

  • 自前でサーバーを作ったり、管理したりする必要が無く、コードをAWS Lambdaにあげるだけで各種リクエスト(イベント)の処理が可能な素敵なサービスです。

 http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html
 https://aws.amazon.com/jp/lambda/faqs/

  • AlexaのスキルもAWS Lambdaで作成することができます。

JavaとAWS Lambda

  • JavaでAWS Lambdaの処理(Lambda関数)を記述することが可能
  • Javaクライアントから、直接Lambda関数を呼び出すことも可能
  • Javaに限らないが、API Gatewayというサービスと連携させると、Lambda関数にendpointをつくって公開することができ、Web APIのようにGETやPOSTといったHTTP(S)経由でも呼び出すことが可能

目次

以下のステップを1つずつ説明しつつ実施していきます

  1. AWS Lambda関数をJavaでコーディングする
  2. アップロード用のjarファイルを作る
  3. jarファイルをAWS Lambdaに登録して実行可能(呼び出し可能)にする
  4. AWSコンソール上でテストする
  5. ローカルJavaから、AWS Lambda関数を呼び出しする

1.AWS Lambda関数をコーディングする

Java用ライブラリ読み込み

aws-lambda-java-coreライブラリを追加します。mavenを使う場合は、以下の記述を追加します

maven
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>1.1.0</version>
</dependency>

Lambda関数の中身をコーディングする

ここでは、関数を呼び出すと結果が返るリクエストレスポンス型(RequestResponse)のLambda関数をつくります。

今回はaws-lambda-java-coreライブラリが用意しているRequestHandlerインタフェースを実装して、POJOを入出力できるLambda関数を実装します。

以下は、姓(lastName)と名(firstName)を入力すると、フルネームを出力してくれるLambda関数のサンプルです。

MyLambda.java
package lambda.cloud;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import lambda.cloud.MyLambda.Input;
import lambda.cloud.MyLambda.Output;

public class MyLambda implements RequestHandler<Input, Output> {

  @Override
  public Output handleRequest(Input in, Context context) {

    final Output out = new Output();
    out.in = in;
    out.fullName = in.firstName + "_" + in.lastName;

    return out;
  }

  public static class Input {
    public String firstName;
    public String lastName;
  }

  public static class Output {
    public Input in;
    public String fullName;

  }

}

以下のように、handleRequestメソッドを実装するだけです。引数 Input はリクエスト、戻り値 Output がレスポンスを示します。

 public Output handleRequest(Input in, Context context) {

2. アップロード用のjarファイルを作る

次にLambda関数としてAWS Lambdaで使えるようにするためにはコードをjarファイル(またはzip)にワンパッケージ化してAWS Lambdaにアップロードする必要があります。

このjarファイルは、いまつくったコードの他、依存しているライブラリなどをひとまとめに統合しておく必要があります。

ひとまとめに統合したjarファイル(つまりfat jar)をつくるためにmaven pom.xmlのplugins以下にmaven-shade-pluginを追加しておきます。

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
            <createDependencyReducedPom>false</createDependencyReducedPom>
        </configuration>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

pom.xml

pom.xml全体は、以下のようになります

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>lambda.cloud</groupId>
    <artifactId>mylambda</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <name>AWS lambda example</name>
    <description>example of AWS lambda
    </description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

</project>

ソースコードダウンロード

上記サンプルのフルソースコードは以下にあります
https://github.com/riversun/aws_lambda_example_basic_client.git

Eclipseでcloneする場合
File>Import>Git>Projects from Git>Clone URI
https://github.com/riversun/aws_lambda_example_basic_client.git を指定
import as general projectを選択してインポート
– インポート後に、プロジェクト上を右クリックしたメニューでConfigure>Convert to Maven projectを実行

これでEclipse上でMavenプロジェクトとなるので、以後取り扱いが楽になります。

mavenをつかってアップロード用のjarを作成する

  • (1)コマンドラインでjarを作る場合

pom.xmlのあるディレクトリで以下のコマンドを実行します

mvn packgage
  • (2)Eclipse上でjarを作る場合

 Step 1. 右クリックメニューでRun As>Maven buildを選択する
 

 Step 2. Edit configurationsダイアログで、Goalspackage shade:shade と入力しRun
 

(1)(2)いずれの場合でも、/target以下にすべての依存コード・ライブラリを含んだ mylambda-1.0.0.jarが生成されます。

これがAWS Lambdaアップロード用のjarファイルとなります。

3. jarファイルをアップロードしてAWS Lambdaに登録する

AWSコンソールからLambda関数を登録する手順をみていきます

(1)AWS Lambdaを開く
Lambdaサービスを開きます。
img01.png

(2)新しいLambda関数を作る
まだLambda関数をつくっていない場合は以下のような画面になります。

関数の作成(Create function)をクリックします。

img02.png

(3)Lambda関数を[一から作成]する

以下のように設計図(blue print)一覧から選択する画面が出ますが、一から作成(Author from scratch)を選択します。

img03.png

(4)基本的情報画面で名前、ロールを設定する

ここでは、myFunctionという名前のLambda関数をつくっていきます

  • 名前(Name)は「myFunction
  • ロール(Role)は「テンプレートから新しいロールを作成(Create new role from template)
  • ロール名(Role name)は「myRole

入力できたら、関数の作成(Create function)をクリックします

img04.png

(5) jarファイルをアップロードする

img06.png

STEP 1
まず、画面上部にある、ARNを確認しておきます
画面に表示されている、arn:aws:lambda:ap-northeast-1:000000000000:function:myFunction部分をメモしておきます

ご存知ARN(Amazon Resource Name)はAWS リソースを一意に識別するためのもので、Lambda関数実行時にその特定のために使います。

STEP 2
ランタイム(runtime)からJava8を選択する

STEP 3
ハンドラ(Handler)にlambda.cloud.MyLambdaと入力する
ハンドラ名は、パッケージ名.クラス名::メソッド名 のフォーマットで記述します。
さきほど作ったコードにあわせて、パッケージ名が、lambda.cloud、クラス名がMyLambdalambda.cloud.MyLambdaを入力しています。この例では、メソッド名は省略しても動作します。

STEP 4
アップロード(upload)をクリックしてさきほど作ったmylambda-1.0.0.jarをアップロードします。

はい、ここまでで、さきほど自作したLambda関数がAWS Lambdaに登録されました。

4. AWSコンソール上でテストする

アップロードが終わったら、コンソール上からテストしてみます。

(1)テスト用イベントの準備

Lambda関数は 何らかのイベントをトリガーにして起動する という考え方があり、たとえば、S3バケットにオブジェクトが作成されたというイベントをトリガーとして、Lambda関数を発火させる、という使い方があります。

そこで、Lambda関数を実行するための入力のことをイベントと呼びます。

ここでは、イベントをコンソール上から発火させ、Lambda関数の動作を確認します。

画面上部にある、テストイベントの設定(Configure test events)を選択します。

img07.png

すると以下のように、テストイベントの設定(Configure test events)画面が開きますので、ここでテスト用のイベントを作成します。

image08b.jpg

イベント名(Event name)MyEventとして、その下にあるエディットボックスはLambda関数をリクエストするときの本文です。

さきほどつくったPOJOで入出力するLambda関数は、実際の呼び出しではPOJOが自動的にJSONにマップされ、JSONで入出力されます。

そこで以下のようにJSON形式でイベントの本文を入力します

{
  "firstName": "john",
  "lastName": "doe"
}

入力したら、画面下にある作成を押します。

(2)テスト用イベントの実行

いま作成したMyEventを早速実行します。画面右上のテストを押します。

img09.png

ちなみに、「ハンドラーで指定されたファイル名がデプロイパッケージのファイル名と一致しないため、Lambda 関数 「myFunction」はインラインで編集できません。」というメッセージが表示されても気にしなくてOkです。
Lambdaコンソールでは、Java、C# などのコンパイル済み言語のインラインエディタは提供されていません。

(3)実行結果を確認する

テストを押してしばらくすると、結果画面が表示されます。

成功のときは、実行結果: 成功(Execution result:succeeded)と表示されます。その下の▼詳細(details)を押して展開すると結果の詳細を確認できます。

img10.png

Lambda関数の出力も、さきほどのPOJO Outputクラスが以下のようなJSONに変換されます。

入出力用のPOJO
public static class Input {
    public String firstName;
    public String lastName;
  }

  public static class Output {
    public Input in;
    public String fullName;

  }
実行結果
{
  "in": {
    "firstName": "john",
    "lastName": "doe"
  },
  "fullName": "john_doe"
}

5. ローカルJavaから、AWS Lambda関数を呼び出す

さきほど作ったLambda関数 myFunction をJavaプログラムから呼び出します。

ライブラリを読み込む

JavaからAWS Lambda関数をたたく場合には、aws-java-sdk-lambdaライブラリを追加します。mavenを使う場合は、以下を追加します

maven
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-lambda</artifactId>
    <version>1.11.210</version>
</dependency>

Javaクライアント

コードは以下のようになります。(各種設定値はダミーです)

Javaクライアント
public class ExampleLambdaClient {

  public static void main(String[] args) {
    ExampleLambdaClient client = new ExampleLambdaClient();
    client.invokeLambdaFunction();

  }

  private void invokeLambdaFunction() {

    final String AWS_ACCESS_KEY_ID = "ANMNRR35KPTR7PLB3C7D";
    final String AWS_SECRET_ACCESS_KEY = "UKA6EsKY25LJQBEpUvXyYkj8aWKEDnynEZigVPhz";

    AWSCredentials credentials = new BasicAWSCredentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY);

    // ARN
    String functionName = "arn:aws:lambda:ap-northeast-1:000000000000:function:myFunction";

    String inputJSON = "{"firstName":"john","lastName": "doe"}";

    InvokeRequest lmbRequest = new InvokeRequest()
        .withFunctionName(functionName)
        .withPayload(inputJSON);

    lmbRequest.setInvocationType(InvocationType.RequestResponse);

    AWSLambda lambda = AWSLambdaClientBuilder.standard()
        .withRegion(Regions.AP_NORTHEAST_1)
        .withCredentials(new AWSStaticCredentialsProvider(credentials)).build();

    InvokeResult lmbResult = lambda.invoke(lmbRequest);

    String resultJSON = new String(lmbResult.getPayload().array(), Charset.forName("UTF-8"));

    System.out.println(resultJSON);

  }
}

AWSCredentials credentials = new BasicAWSCredentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY);
  • アクセスキーID(AWS_ACCESS_KEY_ID)とシークレットアクセスキー(AWS_SECRET_ACCESS_KEY)をつかってcredentialsをつくります。

InvokeRequest lmbRequest = new InvokeRequest()
        .withFunctionName(functionName)
        .withPayload(inputJSON);
  • リクエストを生成します。#withFunctionNameでは関数名を指定します。ARN(arn:aws:lambda:ap-northeast-1:000000000000:function:myFunction)を指定するか、関数名(myFunction)を指定します。ここではARNを指定しました

  • #withPayloadでリクエストの本文(JSON)を指定します。
    ここではJSON文字列 “{“firstName”:”john”,”lastName”: “doe”}”;を指定してます。


lmbRequest.setInvocationType(InvocationType.RequestResponse);
  • 呼び出しタイプ(invocation type)を指定します
    ここでは InvocationType.RequestResponse を指定しています。これでRequestResponse型の呼び出しタイプになります。RequestResponseにしておくと、処理結果を受け取ることができます。

InvokeResult lmbResult = lambda.invoke(lmbRequest);
String resultJSON = new String(lmbResult.getPayload().array(), Charset.forName("UTF-8"));
  • Lambda関数を呼び出して、結果(JSON)を受け取ります。

Javaクライアントのソースコード

クライアント側のフルソースコードは以下においてあります
https://github.com/riversun/aws_lambda_example_basic_client.git

まとめ

  • Javaでも比較的簡単にAWS lambdaの関数を記述できました
  • Javaのクライアントから直接 Lambda関数を呼び出せました

サンプルはEclipse(Oxygen)で作成しましたが、特段プラグインなど必要ありません
(AWS SDK Plugin for Eclipseも不要。あれば、もっと手軽になりますが)

  • 後編では、Lambda関数およびJavaクライアント側の同期/非同期についてや、API Gatewayについて書きます。

おまけ

  • 2009年頃ですがコードだけ書いたら後はおまかせ、課金は使った分だけというサービスの元祖「Google App Engine (GAE)」が登場したときは衝撃をうけました。独特の制約が多かったものの、とてもお世話になりました。

  • 今度はその元祖!?が、Google Cloud Functionsを投入しています。今現在はβで実績はこれからだとおもいますが、今後はそちらも選択肢に入ってきそうです。

続きを読む

Djangoの既存プロジェクトをec2にデプロイ

概要

詰まりまくったのでメモ。Dockerで開発していたのでそのままデプロイしたろ!と思っていたが意味わからなすぎて断念。(おそらく?)一般的な方法でデプロイした。もっと良い方法があれば教えて欲しい限りです。

環境

OS: Amazon Linux AMI release 2017.09
ローカル: Docker
Python: 3.6.2
Django: 1.11.5
Gunicorn: 19.7.1
Nginx: 1.12.1

AWSの設定

インスタンスの作成

AWSの設定はAmazon Web Services 基礎からのネットワーク&サーバー構築を参考にした。

AWSに登録してコンソール > EC2からインスタンスを作成する。全部デフォルト。t2microなら無料。
VPC、サブネット、ルートテーブル、ゲートウェイ、セキュリティグループやらが作成される(はず)。なかったら作ってVPCに紐付ける。sshキーをダウンロードまたは登録しておく。

ポートの開放

EC2 > セキュリティグループからポートが開放できる。セキュリティグループを選択 -> インバウンド -> 編集 -> ルールの追加で80番ポート、8000番ポート(確認用、あとで閉じる)を開く。タイプはカスタムTCP、ソースは0.0.0.0/0で良い。
ここで EC2 > インスタンス から作ったインスタンスの詳細が確認できる。右側のパブリックDNSでドメイン、IPv4パブリックIPでIPが確認できる。

nginxのインストール

AWSにsshでログイン。キーペアをダウンロードした場合は~/.sshに置いて別のキー名を指定する。

# ssh -i ~/.ssh/id_rsa ec2-user@(インスタンスのIP)

以下ではrootで作業する。

$ sudo su -

以下、EC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイするを参考にnginxをインストール。

nginx入れる。

$ yum install nginx

nginxを起動する。

$ nginx

nginxの自動起動設定

$ chkconfig --add nginx
$ chkconfig nginx on

自動起動設定できているか確認する。

$ chkconfig | grep nginx
nginx           0:off   1:off   2:on    3:on    4:on    5:on    6:off

また、http://(パブリックDNS)を確認してnginxが起動しているか確認する。

Djangoプロジェクトの起動

AWSにプロジェクトを送る。

scpでzipで固めたDjangoプロジェクトを送る。また送る前にrequirements.txtは用意しておく。

# pip freeze > requirements.txt
# scp -i ~/.ssh/id_rsa ~/path/to/project.zip ec2-user@yourIP:home/ec2-user/

送ったものがhome/ec2-userに落ちているはず。解凍する

$ unzip project.zip

ほんとはgitで落とせばいいんだろうけどプライベートリポジトリにしてるので新しいuser登録して新しくssh登録してってしないといけないのかな。誰か教えてください。

pythonとかを入れる

色々考えるのがめんどくさかったのでEC2サーバにPython3環境構築を参考にした。

gitとpyenv入れる。-yが全部yesって答えるオプションらしい。初めて知った。

$ yum install git -y
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv

path通す。

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

コマンド通るか確認。

$ pyenv -V

依存関係を入れる。

$ sudo yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel -y

本体を入れる。

$ pyenv install 3.6.2

pythonを切り替える。これはrootのpythonなので、sudo su -後でないとデフォルトのpythonに戻ってしまうので注意。

$ pyenv global 3.6.2
$ pyenv rehash
$ python --version
Python 3.6.2

Django、その他諸々のプロジェクトに必要なライブラリをインストールする。

$ pip install --upgrade -r project/requirements.txt

requirements.txtにGunicornが入ってなければGunicornを入れる。
Gunicornとはwsgiサーバーのことで、nginxとDjangoを繋ぐものみたいなイメージ。

$ pip install gunicorn

manage.pyの上でDjangoを起動させる。

$ gunicorn your_project.wsgi --bind=0.0.0.0:8000

http://(パブリックDNS):8000を確認すると、ALLOWED_HOSTSに追加してね!と出るので追加する。

/your_project/settings.py
# 中略
ALLOWED_HOSTS = ['(パブリックDNS)']
# 以下略

もう一回確認してプロジェクトが見えれば成功。

Nginxの設定の変更

再びEC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイするを丸パクリ。本当にありがとうございました
/etc/nginx.confとあるが、Amazon Linuxでは/etc/nginx/nginx.confにあった。
以下引用

/etc/nginx.confを以下の通り編集する

/etc/nginx.conf
〜中略〜

http {
   〜中略〜

   upstream app_server {
       server 127.0.0.1:8000 fail_timeout=0;
    }

   server {
        #以下4行はコメントアウト
        #listen       80 default_server;
        #listen       [::]:80 default_server;
        #server_name  localhost;
        #root         /usr/share/nginx/html;

       # 以下3行を追加
        listen    80;
        server_name     IPアドレス or ドメイン;
        client_max_body_size    4G;

       # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

       location / {
            # 以下4行を追加
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass   http://app_server;
        }

   〜以下略〜

nginxの再起動

$ service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]

Djangoを再びGunicornで立ち上げる

$ gunicorn your_project.wsgi --bind=0.0.0.0:8000

http://(パブリックDNS)で確認できたら成功。

daemon化

今はコマンドからGunicornを起動しているだけなので、ログアウトしたら終了してしまう。そこでGunicornをデーモン化して常駐するようにしたい。
ただ色々な所を見ると、Surpervisorというツールでそれが可能なようだが、SurpervisorはPython3系に対応していないので2系の環境をもう一つ作れとのこと。んなアホな。
もうちょっと調べてみると、普通に-Dというオプションをつけるだけでデーモンとして起動できるらしい。

起動

$ gunicorn your_project.wsgi --bind=0.0.0.0:8000 -D

終了するときは

$ ps -ef | grep gunicorn
root     17419     1  0 Oct07 ?        00:00:08 /root/.pyenv/versions/3.6.2/bin/python3.6 /root/.pyenv/versions/3.6.2/bin/gunicorn your_project.wsgi --bind=0.0.0.0:8000 -D
root     17422 17419  0 Oct07 ?        00:00:01 /root/.pyenv/versions/3.6.2/bin/python3.6 /root/.pyenv/versions/3.6.2/bin/gunicorn your_project.wsgi --bind=0.0.0.0:8000 -D
root     21686 21594  0 08:05 pts/0    00:00:00 grep --color=auto gunicorn
$ kill 17419

スクリプトで起動

ただこのままだと毎回めんどくさいので、シェルスクリプトでサーバーを起動、停止するにあるスクリプトを使わせてもらう。

Flaskとかで作ったちょっとしたサーバーの起動/停止用のシェルスクリプト。
gunicornでデーモン状態にしている。
startで起動、stopで終了、restartでstop+start。

your_project.sh
#!/bin/sh
PROGNAME=`basename $0`
BASEDIR=`dirname $0`
PIDFILE=$BASEDIR/$PROGNAME.pid

start() {
  echo "Starting server..."
  cd $BASEDIR
  gunicorn flaskhello:app -p $PIDFILE -D
}

stop() {
  echo "Stopping server..."
  kill -TERM `cat $PIDFILE`
  rm -f $PIDFILE
}

usage() {
  echo "usage: $PROGNAME start|stop|restart"
}

if [ $# -lt 1 ];  then
  usage
  exit 255
fi

case $1 in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    stop
    start
    ;;
esac

以下のgunicorn flaskhello:app -p $PIDFILE -D
gunicorn your_project.wsgi --bind=0.0.0.0:8000 -Dに書き換える。

$ sh your_project.sh start   #起動
$ sh your_project.sh stop    #終了
$ sh your_project.sh restart #再起動

sshを切ってもhttp://(パブリックDNS)が見えたら成功。ひとまず見えるようになった。

staticファイルが見つからない

ただここまで追ってもcss,jsなどstaticファイルは見つかってないはず。以下で見えるようにする。
setting.pyのSTATIC_URL、STATIC_PATHは設定済みでローカルでは見える状態とする。
詳しい解説 -> Django での static files の扱い方まとめ

staticファイルのコピー

manage.pyの上でstaticファイルを指定したディレクトリにコピー

$ python manage.py collectstatic

nginx.confをいじる

nginx.confにstaticファイルのディレクトリを登録する。

/etc/nginx/nginx.conf
server{
    # 〜中略〜
    location /static {
         alias /home/ec2-user/your_project/static;
         #settings.pyで設定したのと同じ場所を記述
    }
    # 〜以下略〜
}

パーミッション変更

ここでnginxとgunicornを再起動する。

$ sh your_project.sh stop
Stopping server...
$ service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]
$ sh your_project.sh start
Starting server...

するととpermission deniedが出た。これはec2-userにotherからの権限がないせいだった。
参考によると、このディレクトリに移動する権限が必要らしい。
/home/でec2-userに権限を与える。

$ ls -l
drwx------  5 ec2-user ec2-user 4096 Oct  6 04:19 ec2-user
$ chmod o+x ec2-user
$ ls -l
drwx-----x  5 ec2-user ec2-user 4096 Oct  6 04:19 ec2-user

gunicornを再起動すると無事にstaticファイルにアクセスできて終了。

参考

EC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイする
EC2サーバにPython3環境構築
シェルスクリプトでサーバーを起動、停止する
Django での static files の扱い方まとめ
Nginxでつくる、どシンプルな静的コンテンツサーバ

続きを読む

開発用サーバーを作る on AWS(Ruby on Rails 5)

はじめに

まぁそのまんま。
こちらのRubyonRails編。

Ruby 2.4.2
Ruby on Rails 5.1.4
なり。

Amazon Linux AMI+Nginx+Unicorn
やで。

インストールなど

sudo yum update
sudo yum -y install git
sudo yum install nodejs --enablerepo=epel
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
sudo yum install gcc openssl-devel readline-devel sqlite-devel
rbenv install 2.4.2
rbenv global 2.4.2
gem update --system
gem install --no-ri --no-rdoc rails
gem install bundler
gem install sqlite3
gem install unicorn
rbenv rehash
rails -v
rails new /var/www/html/sample
sudo mkdir /var/run/unicorn
sudo chmod 777 /var/run/unicorn
sudo chown -R nginx:ec2-user /var/www/html
sudo chmod 2777 /var/www -R

まぁ、だいぶ時間がかかりまっせ。

sample/config/unicorn.rbを作成

application = 'sample'

worker_processes 2
timeout 15

pid "/var/run/unicorn/unicorn_#{application}.pid"
listen "/var/run/unicorn/unicorn_#{application}.sock"

preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
  puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
  Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
  ActiveRecord::Base.connection.disconnect!end

after_fork do |server, worker|
  Signal.trap 'TERM' do
  puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
  ActiveRecord::Base.establish_connection
end

stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])

起動

bundle exec unicorn_rails -c config/unicorn.rb -E development -D

OSの再起動対応

~/.bashrcに追記

sudo mkdir -p /var/run/unicorn && sudo chmod 777 /var/run/unicorn

/etc/nginx/conf.d/sample.conf作成

upstream unicorn {
  server unix:/var/run/unicorn/unicorn_sample.sock;
}
server {
    listen 8080;
    server_name localhost;
    root /var/www/html/sample;

    access_log /var/log/nginx/myapp_access.log;
    error_log /var/log/nginx/myapp_error.log;

    try_files $uri/index.html $uri @unicorn;
    location @unicorn {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://unicorn;
    }
}

PHPを80で動かしてる都合上8080でごわす。

Nginx再起動

sudo service nginx restart

いじょー
Security Groupで8080はあけておきましょうね。

by 株式会社Arrvis

続きを読む

【AWS】CodeCommitを使ってみた

はじめに

AWSのリポジトリサービス「CodeCommit」の使ってみました。
「HTTPS」と「SSH」のツーパターンりますが、今回は「SSH」のやり方になります。

IAMの作成

SSH用CodeCommitグループの作成

  1. グループ名設定
    スクリーンショット 2017-10-06 1.12.03.png
  2. ポリシーのアタッチで以下の3つのポリシーを当てる
    • IAMUserSSHKeys
      スクリーンショット 2017-10-06 1.15.47.png
    • IAMReadOnlyAccess
      スクリーンショット 2017-10-06 1.16.11.png
    • AWSCodeCommitFullAccess
      スクリーンショット 2017-10-06 1.17.20.png
  3. 確認を押してグループを作成する
    スクリーンショット 2017-10-06 1.22.11.png

SSH用IAMユーザを作成する

  1. IAMユーザを「プログラムでのアクセス」で作成
    スクリーンショット 2017-10-06 2.22.49.png

  2. グループ指定では先程作成したグループを指定
    スクリーンショット 2017-10-06 2.24.12.png

  3. 確認画面で「ユーザの作成」を押して完了
    スクリーンショット 2017-10-06 2.24.55.png

  4. 今回アクセスキーは使用しないため、削除する

    • アクセスキーの☓ボタンを押す
      スクリーンショット 2017-10-06 2.26.37.png
    • 確認画面にて「削除」を選択
      スクリーンショット 2017-10-06 2.27.17.png
    • 削除されたことを確認する。
      スクリーンショット 2017-10-06 2.27.30.png

公開鍵とIAMの連携

ssh-keygenコマンドで公開鍵の作成

  • コマンドを実行し、Overwiteでyを入力する以外はすべてエンター。
  • デフォルトだとid_rsaの名前で公開鍵が書かれたファイルができます。
  • 今回はVisual Studio Codeを使ってます。
    スクリーンショット 2017-10-06 10.08.41.png

公開鍵をCodeCommitのIAMユーザに登録する

  1. ssh-keygenコマンドで作成した際に出来た、/Users/****/.ssh/id_rsa.pubの内容をコピーする。

    • id_rsa.pubはそのまま作成した場合の名称、作成時に入力していればその名前のファイル
  2. 作成したIAMユーザのSSH公開キーアップロードに、コピーした公開キーをアップする。
    スクリーンショット 2017-10-06 10.21.34.png
    スクリーンショット 2017-10-06 11.40.21.png

  3. 以下のように表示されればアップロード完了
    スクリーンショット 2017-10-06 11.41.33.png

SSH設定ファイルを編集

Configファイル(~/.ssh/config) 編集

Host git-codecommit.*.amazonaws.com
  User (IAMに公開鍵を登録した際に発行されたSSHキーID)
  IdentityFile ~/.ssh/(公開鍵ファイル名[今回はid_rsa])
  • IAMに公開鍵を登録した際に発行されたSSHキーID
    スクリーンショット 2017-10-06 15.05.38.png

  • viコマンドでconfigファイルに書き込み
    スクリーンショット 2017-10-06 15.08.23.png

  • 上記の内容を書き込んで保存
    スクリーンショット 2017-10-06 15.12.16.png

CodeCommitへの接続

リモートリポジトリの作成

  • CodeCommitでリモートリポジトリを作成する
    スクリーンショット 2017-10-06 16.03.53.png

git CloneコマンドでSSH接続

  • 作成すると以下の画面が表示されるので、Git cloneコマンドをコピーする
    スクリーンショット 2017-10-06 16.04.43.png
git clone ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample_repository
  • コピーしたgit cloneコマンドを入力、無事にリポジトリからコピーが出来たことを確認できました。
    スクリーンショット 2017-10-06 16.20.10.png

既存のローカルディレクトリをPUSHする場合

ローカル環境でGitを用意

$ git init
Initialized empty Git repository in /(ディレクトリパス)/.git/

リモートリポジトリの設定

$ git remote add origin ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/Sample_Repository
$ git remote -v
origin  ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/Sample_Repository (fetch)
origin  ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/Sample_Repository (push)

ローカルリポジトリにCommit

$ git add --all
$ git commit -m "first commit"

リモートリポジトリにPush

$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 272 bytes | 272.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/Sample_Repository
 * [new branch]      master -> master

CodeCommitにPushされていることを確認

スクリーンショット 2017-10-06 22.20.40.png

まとめ

GithubからCodeCommitに移行しようとおもったので作業しつつ記述
AWSのヘルプにも書いてある事だけど挿絵をさして自分がわかりやすいように残しました!
次はCodeCommitにPushしたらCodeDeployでデプロイ出来るようにしたいです。

参考ページ

サルでもできるCodeCommit コピペでgit cloneするまで

続きを読む