aboutsummaryrefslogtreecommitdiff
path: root/posts/serverless-framework-for-racket.md
blob: fe31cd431b84f2d5a49a0256690d2ba018fa1a9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
title: Serverless Frameworkを使ってRacketのプログラムをAWS Lambdaにデプロイしてみた
id: serverless-framework-for-racket
date: 2020-03-26 01:50
---
Qiita を退会したときに消えてしまった記事を復元したものです。

## 目的

RacketのプログラムをServerless FrameworkでAWS
Lambdaにデプロイして実行したい。

## 背景

昨年の年末にAWSが[Custom AWS Lambda
Runtimes](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html)という機能を発表し、任意のプログラミング言語がLambdaで使用できるようになり、マイナーなプログラミング言語のユーザの間で話題になりました。

私にとってはプログラミング言語RacketをAWS
Lambdaで利用できるようになったことが喜ばしく、業務でしか関わることのなかったAWS
Lambdaを趣味で利用しようという気になるほどインパクトの大きい発表でした。

しかし、待っていたところでRacketを本気で使用するためのLambda
Runtimeが登場することはなさそうだったので新しく作る必要がありました。

## 先行事例

### 事例1

既にRacketのプログラムのLambda
Runtimeを作成する方法について紹介されています。  
<https://qiita.com/ojima-h/items/9f42f90c2e63a80584d7>

しかし、この記事ではあくまでもRacketをSchemeとして使用することが前提となっているために、本気でRacketをAWS
Lambdaで使いたい場合には不足していました。

具体的には以下の問題が存在します。

`#lang`によって言語を選択できない

-   `#lang racket`のデフォルトのloadによって読み込んでいるため

カスタムランタイムにないpackageを使用することができない

-   たとえば[aws](カスタムランタイムにないpackageを使用することができない)のような
    package を使うことができない

いずれもSchemeのCustom AWS Lambda
Runtimeとしては問題のないことですが、[Programming-Language Programming
Language](https://felleisen.org/matthias/manifesto/sec_pl-pl.html)であるRacketにとっては致命的な問題です。この問題を解決するために、新しくRacketをLambdaへデプロイする方法を確立する必要があります。

### 事例2

実は、AWSがCustom AWS Lambda
Runtimeに対応する前から任意のプログラミング言語が実行可能でした。

  
<https://www.lambrospetrou.com/articles/aws-lambda-meets-racket/>

こちらの方法は、別の言語とRacketのプログラムをコンパイルした結果のバイナリを一緒にデプロイし、初期化時にRacketのプログラムを起動してLambda関数が呼ばれる度に、引数を標準入力へ流し込むことによってLambda関数でRacketのプログラムを実行しています。

しかし、この方法はCustom AWS Lambda
Runtimeが登場する前では大変有益でしたが、現在はこのような遠回りをする必要はありません。また、AWS
Lambdaでは標準出力がログ出力に割り当てられていますが、この方法では標準出力が結果の返却に使われてしまうという点で都合がよくありません。

よってこの方法は今となっては微妙なのでやはり新しく作る必要がありました。

## Serverless Frameworkについて

[Serverless
Framework](https://serverless.com/)自分でサーバの管理はしたくなくて、冗長性の確保やオートスケーリングなどのインフラ側を誰かに代わりにやって欲しいと思う人にはうってつけのツールです。詳しくはリンク先を参照してください。

今回の記事では、AWSのLambda関数とLambda
Runtimeのデプロイを行うためにServerless
Frmeworkを使用することにしました。

## やったこと

Severless Frameworkを使ってRacketのプログラムをAWS
Lambdaにデプロイするために行ったことを紹介します。

### aws-bootstrap-runtimeの作成

[aws-bootstrap-runtime](https://github.com/tojoqk/aws-lambda-bootstrap-runtime)とは、Custom
AWS Lambda Runtimeの実装をLambda関数側に移すためのCusmtom AWS Lambda
Runtimeです。

......というと意味不明ですが、このCustom AWS Lambda
Runtimeのやることは単純で、ただLambda関数としてデプロイされたbootstrapという名前の実行ファイルを実行するだけです。

[こちら](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html)のチュートリアルで説明されている通り、Custom
AWS Lambda
Runtimeとはbootstrapという名前の実行ファイルを用意して、そこで初期化処理やLambda関数が呼ばれた際の処理などを行うことによって実装されるものです。

私も最初は真面目にbootstrapファイルにRacketのLambda
Runtimeを一生懸命実装していたのですが、Minimal
Racketでは不十分なのでFullのRacketをインストールしようとすると、Racketに同封された実際には不要なライブラリとドキュメントによって容量が膨れ上がりLambda
Runtimeに置けるデータの容量の上限を超えてしまったり、実装に失敗するたびにいちいちバージョンを上がって永久に戻せなかったり、RacketのpackageをLambda関数側でデプロイするのが困難だったりしました。

そこで、Custom AWS Lambda
Runtimeの実装はRacketのライブラリとして提供し、Racketのプログラムをコンパイルした結果をbootstrapという名前にして、aws-bootstrap-runtimeにデプロイすることによって解決しました。

ちなみに、このbootstrap runtimeはCustom AWS Lambda
Runtimeが使用できる全てのリージョン対してデプロイされていて、全てのアカウントからアクセス可能な状態になっています。

### aws-lambda-serverless

RacketのCustom AWS Lambda
Runtimeの構築が面倒くさいという問題は解決したのですが、今度はLambda関数側のデプロイが大変になりました。
Custom AWS Lambda
Runtimeの実装を含みつつ、Lambda関数に対応したRacketの手続きが呼び出されるようにしないといけません。
この問題を解決するのが[aws-lambda-serverless](https://github.com/tojoqk/aws-lambda-serverless)です。

これはServerless
Frameworkを使用することを前提としたライブラリで、Lambda関数としてRacketの手続きを簡単にデプロイすることができます。

## 実例

では試しに、`Hello World`を返すようなLambda関数をこのライブラリを使って作ってみましょう。
しかし、ここで残念なお知らせがあり、今のところ64bitのGNU/Linux環境からしかデプロイできません(さらにAmazon
GNU/Linux 2からしか試していません)。
これ以外の環境で作業している人はVMやコンテナ技術などの仮想環境を使用するか、GNU/LinuxなどのOSがインストールされたマシンを用意しましょう

また、[Racket](https://download.racket-lang.org/)と[Serverless
Framework](https://serverless.com/framework/docs/getting-started/)がインストールされていることが前提です。
もしもインストールしていない場合は、必ずインストールしましょう。

### aws-lambda-serverlessをインストールする

    raco pkg install https://github.com/tojoqk/aws-lambda-serverless.git

### Lambda関数として呼び出すRacketの手続きを実装する

    #lang racket
    (require json)

    (define (hello jsexpr)
      (jsexpr->string
       (hash 'body "Hello, world!"
             'input jsexpr)))
    (provide hello)

これを、 handler.rkt という名前で保存します。

### serverless.ymlを準備する(ただし、先頭に`#lang aws-lambda-serverless`と記述する必要があります)

    #lang aws-lambda-serverless

    service: test

    provider:
      name: aws
      runtime: provided
      memorySize: 128
      timeout: 1
      region: ap-northeast-1

    functions:
      hello:
        handler: handler.hello
        layers:
          - arn:aws:lambda:ap-northeast-1:488514468674:layer:bootstrap:2

### serverless.ymlをコンパイルしてbootstrapという名前の実行可能なバイナリを作る

    raco exe --orig-exe -o bootstrap serverless.yml

### デプロイする

    sls deploy

これでLambda関数がデプロイできたはずです。
Lambda関数を呼び出して`Hello, world!`を含むJSONが返ってきたら成功です。

    sls invoke -f hello

## まとめ

以上によってRacketのプログラムを気軽にAWS
Lambdaにデプロイできるようになりました。 RacketでAWS
Lambdaを使ってみたいなと思ったら是非参考にしてみてください。