【サーバーレス Lambda】Apexを使ってデプロイ環境を整備する

Lambdaとは

公式によると 

AWS Lambda はサーバーをプロビジョニングしたり管理しなくてもコードを実行できるコンピューティングサービスです。AWS Lambda は必要に応じてコードを実行し、1 日あたり数個のリクエストから 1 秒あたり数千のリクエストまで自動的にスケーリングします。使用したコンピューティング時間に対してのみお支払いいただきます- コードが実行中でなければ料金はかかりません。 ...以下、略

と書いてあります。

難しすぎますね。。

個人的に重要な点

個人的にLambdaの重要なところは以下の3点だと思います。

  • 1.サーバー管理、言語環境設定などをせず、コードを実行出来る
  • 2.他のAWSサービスと連携がしやすい
  • 3.使用言語: Node C# JAVA Python

Lambdaの利点

前述したように、サーバー構築を一回でもやったことがある人なら認識あるかもしれないんですが、 新しいインスタンスVPSを立ち上げるのって結構だるいです。

Lambda を使えば環境構築も整備してくれて、すぐに実行出来ます。 そして、AWSのサービス群である CloudWatchAPIGatewayを使えば cron job や 面倒なAPIを作成することが出来ます!

Lambda 面倒な点?

まずはじめにLambda実行方法をあげてから面倒な点を述べます。

Lambdaの実行方法(GUI)形式で乗っ取ると以下のような手順を踏まえます。 注) 以下、python を想定

  • 1 python プログラム作成
  • 2 python プログラム、標準外ライブラリをフォルダに一式入れてZIP化、アップロード
  • 3 実行環境設定 (メモリ、実行時間など)

面倒なのは2と3の実行手順です。標準外のライブラリをダウンロードして ZIP化、さらには実行環境設定などをいちいち設定しなくてはいけません。

Apexとは

Apexは上述した、Lambdaの面倒な点をサポートするデプロイツールです。 Apex公式

利点

利点は以下の通りです。

  • プロジェクトごとに共通の設定を持たせることが出来る
  • プログラムファイルは自動でZIP化し、デプロイ
  • エイリアスを用いることが出来るので、ロールバックやアップデートも楽
  • Go言語が使用可 (デフォルト使用言語: Node C# JAVA Python)

利用方法

想定言語:python(ローカルでパッケージ管理ツール pip がインストール)

apex環境構築

# IAMで access key と secret access keyを取得しておくこと
# コマンドラインでAWSを操作出来るパッケージインストール

$ pip install awscli
$ aws help  # install 確認
$ aws configure
# 以下を入力
# AWS Access Key ID [**********]:
# AWS Secret Access Key [**********]:
# Default region name [ap-northeast-1]:
# Default output format [json]:

apex install

# apex install
curl https://raw.githubusercontent.com/apex/apex/master/install.sh | sh
apex version # install確認

apex 実行

$ mkdir apex-test 
$ cd apex-test/
$ apex init #project name descriptionを記入

apex initを実行すると以下のようなプロジェクトフォルダが作成されます。 初期設定はjsになるようです。。

.apex-test
│
├── functions
│   └── hello
│       └── index.js 
└── project.json # プロジェクトの固有設定

ので、python仕様と説明用に以下のようにしておきます。

.apex-test
│
├── functions
│   └── hello
│       │── function.json # function固有の設定(←デプロイされるプログラムの設定)
│       └── main.py # デプロイプログラム
└── project.json # プロジェクトの固有設定

project.json

{
  "name": "blog-test",
  "description": "test",
  "memory": 128, # 使用メモリ、CPUと連動
  "timeout": 5, # 秒
  "role": "[AWS-ROLE]",
  "environment": {} # 環境変数設定 pythonの場合、os.environ["#####"]で取得可
}

ここでは各プロジェクトの共通設定を記述しています。

function.json

用途:各ファンクション固有の環境設定を記載する。 なお、project.jsonの内容は継承されます。

{
  "description": "python functions",
  "runtime": "python3.6", # 実行環境
  "memory": 128,
  "timeout": 20,
  "hooks": {
  "build": "pip install -r requirements.txt -t ./", # デプロイ前の処理
  "clean": "rm -rf */" # デプロイ後の処理
  },
  "environment": {
    "????_API_KEY":"######",
    "URL":"https://hoge.com"
  }
}

このようにLambdaファンクション固有の設定を記述することが出来ます。

なお、hooksの内容は

  • build(deploy前):記述内容はlambdaデプロイ時に標準外パッケージをインストールし、デプロイ
  • clean(deploy後):buildでインストールした標準外パッケージを削除

となります。

上記のようなjsonファイルを作成することで、lambdaの面倒な点

  • 1.プログラム、標準外ライブラリをフォルダに一式入れる処理)
  • 2.実行環境設定 (メモリ、実行時間など)

を自動化することが出来ます。

apex コマンド

少し長くなってしまったので、個人的に有用だと思うコマンドをまとめておきます。

# 新規プロジェクトを作る
apex init
# 当該プロジェクト内のファンクションをデプロイ
apex deploy
# 当該プロジェクト内の特定のファンクションをデプロイ
apex deploy {FunctionName}
# invoke: deploy後の実行結果をローカルで試す。
apex invoke {FunctionName}
# event の受け渡し
apex invoke {FunctionName} < event.json
# エイリアス作成
apex alias {AliasName} {FunctionName}
# エイリアスのデプロイ
apex deploy -a {AliasName}

apex rollback -v n  # n:数字
apex rollback # 一個前に戻す
apex rollback -a {AliasName} # エイリアスロールバック

最後に

最近、Lambdaをはじめたのですが、面白いのでApexを用いて がしがし有用なプログラム作っていきたいです\(^o^)/

docker 使うコマンド

コマンド

気が向いた時に更新していきます。

$ docker search イメージ名
#コンテナのもととなるイメージを検索 
$ docker pause コンテナ識別子
# 起動中のコンテナを停止
$ docker pull イメージ名
$ dcoker attach  
# 稼働しているコンテナの接続
$ docker top
#稼働コンテナのプロセス確認
$ docker port
#稼働コンテナのポート確認
$ docker commit
#コンテナからイメージ作成

docker 頭の整理

docker for mac

Macでdockerを使う際はdocker for mac という素晴らしいアプリが ある。これは

  • docker
  • docker-machine
  • docker-compose

というありがたいツールがオール・インワンになっているアプリケーションです。 docker-machineは前回話した通り、仮想マシンを作成するものです。 docker-composeは後々まとめる予定。。

仮想マシンにログインするには

docker ssh とコマンドを打てばおkです。Vagrantみたいですね。

イメージとは

dockerの仮想マシン上にログインしたものの、 何もない状態ではつまらないので、docker上でruby環境やらpython環境 構築したいですね。その際には、docker imageをダウンロードして使います。

docker imageとは、アプリ実行に必要なプログラム、ミドルウェアを一つにまとめたものです。通常はプログラムとミドルウェアを別々に管理しておくようです。

docker image は自分で作成することもできますが、

docker hubを探して使うことも出来ます (便利!)

イメージを取得するするには

docker pull イメージ名 を行えばうまくいきます。

githubみたいです。

docker 概念整理

dockerとは

dockerとはホストマシン(macのローカル)に依存せず、開発環境を構築出来るツール linuxカーネルの仮想技術を用いているため、オーバーヘッドが少ない。

コンテナ技術を用いて、実行環境を他のプロセスから隔離してアプリを起動させます。 カーネルを直接使用しているから楽になります。 Vagrantだとメモリ割当やらCPUコア数やら設定しなくてはいけないんですが、 dockerは必要ありません。 もちろん、virtualboxでのメモリ割当は必要です。

カーネルとは

ぐぐってみると、アプリケーションとハードウェアの連携を司るシステムだそうです。

コンテナ技術とは

1つのOS上に隔離された区画を用意してアプリケーションを実行する技術です。 よく docker pull centos をしてcentosのイメージなどを構築するケースがありますが、これはdocker(linux環境)上にcentos環境を構築する形となります。

dockerホストマシン作成方法

通常はvirtualboxなどを用いて仮想マシンを作成します。 macwindowsではdocker-machineを用いて、docker virtualbox上にdockerホストマシンを構築します。

コマンドは以下の通りの感じで作成出来ます。

$ docker-machine create --driver virtualbox vbox

これで仮想マシンを作成します。

Google Maps Direction API を使ってみる。

概要

最近、mapsAPIを調べた所、Direction APIというのがあったのを知った のでまとめておきます。(昔からあったかも??)

Direction APIとは

2つの地点の距離、移動時間を計測するAPIです。

公式HPにそのまま書いてある通りです。

移動時間については、徒歩、自転車、電車、車など様々なモードが選んで計算してくれるところが 良いところではないでしょうあk。

使い方

APIを用いる際に必要なのは以下の2点となります。

  • APIキー取得、DirectionAPI有効化
  • リクエストパラメータ設定

リクエストパラメータについて

必須パラメータ

  • origin #出発地: 指定方法は 住所、緯度・経度
  • destination # 目的地を示す。 指定方法はoriginと同じ
  • APIKEY # 取得したGoogle maps api key

※ 場所指定(origin destination)は多少アバウトでも拾ってくれます。

オプションパラメータ(抜粋)

  • mode #移動時間を計算する際の交通手段を選択
  • units #移動距離の単位を選択。
  • arrival_time # 到着時刻を指定  UNIXTIMEからの経過時間で指定することに注意
  • departure_time # 出発時刻を指定 

実行してみる

request

$curl -s "https://maps.googleapis.com/maps/api/distancematrix/json?\
mode=walking&origins=渋谷www&destinations=恵比寿リキッドルーム\
&key=[API_KEY]" 

response

{
   "destination_addresses" : [ "Japan, 〒150-0011 Tokyo, Shibuya, Higashi, 3−16−6" ],
   "origin_addresses" : [
      "Japan, 〒150-0042 Tokyo, Shibuya, Udagawacho, 13−17 ライズビル 地下"
   ],
   "rows" : [
      {
         "elements" : [
            {
               "distance" : {
                  "text" : "2.0 km",
                  "value" : 2005
               },
               "duration" : {
                  "text" : "24 mins",
                  "value" : 1460
               },
               "status" : "OK"
            }
         ]
      }
   ],
   "status" : "OK"
}

次はjsonを抜き出したケース

request

$curl -s "https://maps.googleapis.com/maps/api/distancematrix/json?\
mode=walking&origins=渋谷www&destinations=恵比寿リキッドルーム\
&key=[API_KEY]"\
 | jq '({ destination: .destination_addresses[] },{ origin: .origin_addresses[] }) ,{distance: .rows[].elements[].distance.text } ,  {distance: .rows[].elements[].duration.text }'\
 | jq 'keys[],.[]' | xargs echo

response

destination Japan, 〒150-0011 Tokyo, Shibuya, Higashi, 3−16−6 origin Japan, 〒150-0042 Tokyo, Shibuya, Udagawacho, 13−17 ライズビル 地下 distance 2.0 km duration 24 mins

感想

有益な点

事前に登録してある場所の検索には非常に有益ではないでしょうか。

例えば、緯度・経度を事前に登録してある場合のケース(不動産サービスやホテルに対して駅からの距離) においては非常に強力だと思います。

やや残念な点

フリーキーワードで位置検索する場合の認識が甘いです。

例をあげます。

出発地=渋谷駅 到着地=代々木駅 でリクエストした場合は以下の通りとなります。

request

$curl -s "https://maps.googleapis.com/maps/api/distancematrix/json?\
mode=walking&origins=JR渋谷駅&destinations=代々木駅\
&language=ja&key=[API_KEY]"\
 | jq '({ destination: .destination_addresses[] },{ origin: .origin_addresses[] }) ,{distance: .rows[].elements[].distance.text } ,  {distance: .rows[].elements[].duration.text }'\
 | jq 'keys[],.[]' | xargs echo

response

destination 日本 origin 日本、東京都渋谷区道玄坂
 distance 3.5 km duration 11分

というように一般的に使われそうなキーワードでも引っかからないことがあります。

【Rubyメモ】Struct

Structとは

Structは構造体を表現するクラスです。 構造体とは1つ以上のフィールドを持つクラスです。 これを用いることで複数のフィールドをもたせることが出来ます。

少し特殊なのはStruct.newを行なうと、Sturctクラスの サブクラスを返すことです。

通常はクラスに対して newを行なうとそのクラスのインスタンスが返るので 最初は不思議な気がしました。

コード

# subject、gpaのフィールドを定義
SchoolRecord = Struct.new(:subject,:gpa)

# スーパークラスはStructクラス(=Struct.newはStructのサブクラス)
p SchoolRecord.superclass #Struct 

record = SchoolRecord.new("english",3.2)
#recordオブジェクトでフィールドへアクセス
p record.subject #english
p record.gpa #3.2

#ハッシュっぽい使い方
p record[:subject] #english
p record.members #[:subject, :gpa]

hashとの比較

hashとどこが違うのという話がありますが、hashの場合は新たにシンボルを 定義することが出来ます。が、Structはnewした際に定義したメンバーしか使えません。

SchoolRecord = Struct.new(:subject,:gpa)
SchoolRecord[:test] = "test" #error
SchoolRecord.test = "test" #error

参考文献

パーフェクトruby

【デザインパターン勉強】Proxyメソッド

ざっくり概要

ある処理を肩代わりする代わりに出来ない処理が着た場合、 処理を他のクラスに肩代わさせるメソッドです。

生徒と先生の関係を考えてみます。

先生といえども、生徒の質問に全て答えられるわけではないですね。 数学の先生が英語の質問をされたときは、英語の先生(クラス)を呼び出して回答する そんなパターンです。

コード

import abc

class Teacher(metaclass=abc.ABCMeta):
    def __init__(self):
        self.subject = "Math"
    
    @abc.abstractmethod
    def question_1(self):
        pass
        
    @abc.abstractmethod
    def question_2(self):
        pass
    
class MathTeacher(Teacher):
    def question_1(self,question):
        print(question,"two")
        
    def question_2(self,question):
        EnglishTeacher().question_2(question)

class EnglishTeacher(Teacher):
    def question_1(self,question):
        MathTeacher().question_1(self,question)
    
    def question_2(self,question):
        print(question,"緑茶です。")

    
if __name__ == '__main__':
    # 生徒役
    MathTeacher().question_1("1+1 equal?") #1+1 equal ? two
    MathTeacher().question_2("what does mean green tea?") #what does mean green tea? 緑茶です。
        

ざっくり解説

今回の例では、生徒の質問(if name以下)はデフォルトで数学の先生(MathTeacherクラス) としておきます。question_2では、英語の質問なので、数学の先生は英語の先生に答えてもらってます。

状況としては数学の先生が同学年の英語の先生が病気のため代理で授業しているとシチューエーションをイメージして下さい。

mainにある処理のイメージを書き下すと、 question_1は数学の先生なんで余裕ですが、question_2は少しむずかしいかもしれません。そこで、1学年下の英語の先生に頼み込んで 回答している状況を示しています。

現実的な利用シーン(個人的感想)

代理で行なうメリットとしては、重いクラスの代理を軽いクラスで肩代わりすることが可能です。 例えば、

  • (あまり実感はないですが)クラスの初期化に呼び出されるコンストラクタが重い
  • DBをロードしなくてはいけない処理がある

といったケースでは代理を立てることで動きの遅いクラスを軽いクラスに肩代わりさせられます。

参考にしたもの

http://www.techscore.com/tech/DesignPattern/Proxy.html/