AWS Lambda+Goで、fujiwara/ridgeからapex/gatewayへ移行する

AWS Lambdaでは以前から、非公式にNode.jsをランタイムとして、Go実装のバイナリを実行させるツールがある。例えばApexとか。さらに、Apex、API Gatewayとの組み合わせを前提としたfujiwara/ridgeというライブラリがあり、net/httpのインターフェースにそった実装ができるようになっていた。

実際に会社でも個人的にも、Apex+Go+fujiwara/ridgeという組み合わせで稼働させているものがある。現時点ではランタイムはNode.jsで動いているが、公式にGoがサポートされるようになったので、移行をしたくてしばらく検証していた。

公式のライブラリ、aws/aws-lambda-goを使用した実装に直接入れ替えるのはしんどそうな感じだったが、apex/gatewayを見つけたのでそれを試してみた。

結果、問題なさそうだったので、とりあえず個人的なやつはそちらに移行した。

移行に伴う修正点

どちらもnet/httpのインターフェースを前提としているので、スムーズに移行できる。

ridgeのサンプルを変更すると以下になる。importするパッケージは当然変わるが、Multiplexerはそのまま使用できる。直接aws-lambda-goを使う必要がないので、大きな変更がなくて助かる。

--- main_old.go  2018-04-07 00:06:44.000000000 +0900
+++ main.go   2018-04-07 00:09:00.000000000 +0900
@@ -4,7 +4,7 @@
    "fmt"
    "net/http"

-  "github.com/fujiwara/ridge"
+   "github.com/apex/gateway"
 )

 var mux = http.NewServeMux()
@@ -15,7 +15,7 @@
 }

 func main() {
-  ridge.Run(":8080", "/api", mux)
+   gateway.ListenAndServe(":8080", mux)
 }

ちょっとapex/gatewayのコードを見てみる

https://github.com/apex/gateway/blob/master/gateway.go#L16

// ListenAndServe is a drop-in replacement for
// http.ListenAndServe for use within AWS Lambda.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, h http.Handler) error {
    if h == nil {
        h = http.DefaultServeMux
    }

    lambda.Start(func(ctx context.Context, e events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
        r, err := NewRequest(ctx, e)
        if err != nil {
            return events.APIGatewayProxyResponse{}, err
        }

        w := NewResponse()
        h.ServeHTTP(w, r)
        return w.End(), nil
    })

    return nil
}

ListenAndServeの中では、lambda.Startを呼び出しつつnet/httpのインターフェースに沿うように対応されている。w.Endevents.APIGatewayProxyResponseが返却されるはず。もう少し読み込んでみる。

https://github.com/apex/gateway/blob/master/response.go#L13

// ResponseWriter implements the http.ResponseWriter interface
// in order to support the API Gateway Lambda HTTP "protocol".
type ResponseWriter struct {
    out           events.APIGatewayProxyResponse
    buf           bytes.Buffer
    header        http.Header
    wroteHeader   bool
    closeNotifyCh chan bool
}

ResponseWriterはこんな構造になっている。

https://github.com/apex/gateway/blob/master/response.go#L39

// Write implementation.
func (w *ResponseWriter) Write(b []byte) (int, error) {
    if !w.wroteHeader {
        w.WriteHeader(http.StatusOK)
    }

    return w.buf.Write(b)
}

WriteのなかではResponseWriter.bufに対しての書き込みが行われるようになっている。

https://github.com/apex/gateway/blob/master/response.go#L77

// End the request.
func (w *ResponseWriter) End() events.APIGatewayProxyResponse {
    w.out.IsBase64Encoded = isBinary(w.header)

    if w.out.IsBase64Encoded {
        w.out.Body = base64.StdEncoding.EncodeToString(w.buf.Bytes())
    } else {
        w.out.Body = w.buf.String()
    }

    // notify end
    w.closeNotifyCh <- true

    return w.out
}

ヘッダーを見て、バイナリのデータかどうか確認しているっぽい。AWS API Gatewayではバイナリデータを返す際にはBase64エンコードされている必要があるので、それにも対応している。

out.Bodyが実際のレスポンスになるわけで、そこにResponseWriter.bufに書き込まれたデータが代入される。

ResponseWriter.closeNofityChtrueが送信されるようになっているが、これはapex/gateway内部では利用していないっぽい。利用側に通知するためのチャネルのようだ。(経緯としてはfeat(Response) Add CloseNotify support. by wolfeidau · Pull Request #7 · apex/gatewayに書いてある)

最終的には、ResponseWriteroutevents.APIGatewayProxyResponseになっているので、それを返すようにしている。

気になるところ

ちゃんとコード全体を見ていないのだけど。

fujiwara/ridge ではできていた以下の点が apex/gatewayでできるのかきちんと確認してない(必要性もないので)

  • Proxy Integrationに対応していて、リクエストがあったパスの情報をきちんとMultiplexerで処理できるようになっていた
    • 自分の要求としては/を前提にしているので、そこまで確認してない
  • ローカルでの起動と実行
    • 自分ではそこまで必要性がなく、実際今まで使ったことがないんだけど

参考

VSCodeをメモアプリとして使う

Boostnoteを使ってたんだけど、プレビュー周りの挙動が好きじゃなかったので、VSCodeでメモできるようにスクリプトを書いてみた。

雑な感じでzshのfunctionとして設定してみた。

function memo() {
  if [ -d $MEMO_HOME ]; then
    cd $MEMO_HOME
    local filename=$(date '+%Y%m%d_%H%M%S').md
    echo -e "# $(date '+%Y-%m-%d %H:%M:%S')\n" > ./src/$filename
    vs --wait --extensions-dir ./ext --goto src/$filename:2 ./src
    git add -A .
    git commit -m "add $filename"
  fi
}

前提

  • MEMO_HOMEとしてどこか指定しておく
  • 配下にextsrcを作成しておく
  • extは拡張機能を置く
    • できるだけ起動を軽くするため、普段使っている拡張機能は有効にならないようにしたい
  • srcはメモのファイルを置く
  • gitでも管理する
    • extはコミットしないようにgitignoreに追記しておく

挙動

  1. 規定のディレクトリに移動する
  2. 日時のMarkdownファイルを作成し、ヘッダーに日時を埋めておく
  3. VSCodeを起動する
  4. VSCodeが終了したらgitのコミットを行う

VSCodeは以下のオプションをつけている。

  • 終了するまで待つ(--wait)
  • extを拡張機能ディレクトリにする(--extensions-dir)
  • ファイルの2行目を指定して開く(--goto)

実際使ってみると、ファイルの一覧が欲しくなったり、Alfredと連携したくなってきたので、そのうちまた修正したい。

AirPodsを買ったけどけっこう良かった

2週間くらい前にAirPodsを購入したけどけっこう良かった。今までにBluetoothイヤホンを2つ買ってたので、今回3つ目にしてようやく満足の行くものが買えたと思う。

とりあえずこだわりなくて、カナル型苦手って人はAirPodsおすすめ。

カナル型はしんどい

なぜかわからないけど市販されているBluetoothイヤホンはほとんどがカナル型になっている。でもカナル型は耳がいたい。

最初に買った3000円くらいのやつは耳が痛くなってやめた。

カナル型ではないフィリップスのイヤホン

しばらくしてからやっぱりワイヤレスのイヤホンが欲しくなって、たまたまヨドバシ寄ってみたときに見つけたフィリップスのイヤホンを買った。

たぶんこれ。

ヨドバシ.com - PHILIPS フィリップス SHB5250BK [Bluetoothイヤホン ブラック] 通販【全品無料配達】

でもこれも最初のやつもそうだったけど、iPhoneと一緒に使うときの音量の具合が非常に良くない。

普通の付属品のイヤホンと同じような感覚での音量調整ができず、音がデカい感じになってしまう。

あと、やっぱりiPhone付属品とはフィット感が若干違ってて、耳はスカスカするし首元のケーブルはなんか邪魔だし、あまり使い心地が良くなくて、結局使わなくなっていた。

AirPods普通に市販されてるのを知った

なんかauでしか売ってない気がしてて全然検討もしてなかったんだけど、最近会社の人で数人使っているっぽいのを見かけて、改めて調べてみた。すると普通にネットで買えるし、だったら迷わず買うか〜って感じで。

実際使ってみると

  • 音量の問題はなく、有線イヤホンと同じ使い心地
  • 首回りにワイヤがないのでマフラーしてても気にならない
  • フィット感も問題なく、特に落ちたりズレたりの心配もない

って感じで今まで買ったイヤホンとは違ってけっこう使い心地が良かった。

充電については、イヤホン自体の充電はケースにしまうだけだし、ケースも頻繁に充電しなくても大丈夫そう。先週末に充電したけど、その時の残量は50%切ってなかったと思う。

嫌なとこ

耳に装着した時の音がデカい。

これが唯一の嫌なとこで、このボリュームは小さくできないっぽい。

保護ケース?

なんかアマゾンでお勧めされてた保護ケースのようなカラビナがついたものを買って、それにしまってる。

Amazon | AirPodsケース、AppleワイヤレスイヤホンAirPod用AhaStyleシリコンショックプルーフ保護カバー (グレー) | AHASTYLE | イヤホン・ヘッドホンケース

とにかくイヤホンについてはお手軽層なんで、買って良かった。

勉強するための時間の工夫

子育てエンジニアが時間取れないという話は度々目にする。

去年の何かのアドベントカレンダーで見た気がするし、最近ツイッターでも「子育て中は勉強の時間取れない」というものを見かけた。

ここでいう子育てってのは、たぶん乳幼児あたりを指しているとは思うが、自分もそうで、なかなかまとまった時間が取れていない。

とりあえず以下の時間帯で勉強できないか試行錯誤はしている。

  • 通勤中
  • お昼休み
  • 子供の就寝後
  • 早起きして

おそらくこれ以外の時間帯は無理っぽい。

通勤中

電車での移動時間は片道30分くらい。

10時出勤のおかげで、朝の電車はそこそこ空いていて、座れる日と座れない日が半々くらい。帰りは始発駅なので、時間に余裕がある時は座れるまで待てるが、そうでない時は座らずに帰る。

座れる時はブログを書くようにしている。スマホで書いているので、スピードは遅く、大体公開に至るまで1週間かかる。時間のムダなような気持ちと、何かしらアウトプットしないと、という気持ちでせめぎあっている感じ。

座れない時は、インプット中心。ツイッターとかで話題になってた記事やスマホでも読める(読みやすい)サイトを読んだりする。読書はあまりしてない。

お昼休み

食事はささっと済ませて45分ほど時間がある。

アウトプットではないが、PCが使えるので、気になるツールとかを触ってみたりとか、コード書いたりとかしている。後はネットで調べものが多いかな。

社内では個人タブレットの使用が禁止されているので、電子書籍はあっても会社では読めず。どうしても読破したい本があれば、紙の本で購入して読書していることもあるが、まれ。

子供の就寝後

週末、昼寝をしなかった場合には子供も早く寝る(10分とか)ので、その時は何か作業することが多い。でも12時過ぎには寝たいので、できても2時間くらい。

あとは早く帰れなかった時には、先に奥さんが寝かしつけしているので、そういう時も何か作業はできてる。

以前は寝付くまで起きてて、それから何かしら作業することをやっていた。でも、子供が寝ないことに対してイライラするようなことがあって、さすがに良くないので、寝かしつけをする時は一緒に早く寝るようにした。

早起きして

子供と一緒に早く寝て早起きした時や、なんか明け方早めに起きてしまった時に、勉強することができる。最近はちょっと起きれなくてやってないけど。

Kindleで読書していることが多い。あとは未読のブログを消化したりとかする。

全体的に

なんかPCを起動しようって気持ちにならないことが多くて、インプットが多い感じになってる。

あと、やっぱり勉強しないとって気持ちが湧いてこないこともあって、特に仕事が忙しくなると、疲れが先にくるので勉強する余裕がなくなる。

余暇時間も正直ダラダラ過ごしたいとも思うし、そういった誘惑もある。

たぶん、以前は勉強に充てていたであろう時間の分が子育てに回っていて、余暇時間というかダラダラしている時間はおそらく変わってない。

なので、やっぱりダラダラする時間を勉強の時間に変えていかないといけないんだと思う。

iPhoneが壊れたんで交換した

先週末に破損したiPhoneを、エクスプレス交換サービスを利用して交換したのでメモしておく。

まとめ

  • エクスプレス交換サービスがとりあえずラク
    • iPhoneを探すをPCとかからオフにしておく
    • IMEIを確認しておく
  • iPhoneのバックアップはこまめにしとく
  • リカバリ前に交換機のiOSバージョンを上げておく
  • LINEは移行元の端末が操作できなくても良い
  • モバイルsuicaの移行が地味に不便
  • 電話回線のキャリア用のプロファイルをインストールしとく

特に復元はぱっと見ある程度出来てそうな気がしてしまうんだけど、アプリによって復元のさせ方が変わっているので要注意。

iPhoneの破損具合

落下させた時に、4Gとか表示される上の辺りを中心に破損した。どうやら液晶までやってしまったっぽい。

  • 画面のガラスがバキバキに割れた
  • 画面の上半分くらいの表示も乱れて、なんとか閲覧できる
  • 誤タップが発生して、まともに操作できない

たまに操作できる瞬間があったので、なんとか家族とLINEできたのと、設定からシリアル番号とIMEIは確認できた。

サポートに電話

とりあえず急いで交換したかったし、AppleCareも切れてなかったので、エクスプレス交換サービスを利用することにした。

ウェブから申し込めなさそうだったので、電話で。通常のサポートダイヤルとは別っぽい?

2回かけたが、1回目は12時過ぎですぐにつながって、2回は18時半ごろで、こちらは5分ほど待った。

必要な情報や設定

電話をするにあたって以下の内容を確認された。

  • AppleID
  • 郵便番号と住所
  • IMEI
  • iPhoneを探すがオフになっているか

IMEIっての初めて聞いたんだけど、設定から確認できる項目で、シリアル番号とは別の識別子。シリアル番号は控えてたけど、IMEIは控えてなかったので、電話中に確認した(この時は幸い確認できた)。

iPhoneを探すの設定がうまく変更できず(昼休みだったので、4G回線が混んでたのかも、ネットワークの問題っぽい)、エクスプレス交換サービスで発送する、という段取りまでで一旦終了した。

この時受付番号を教えてもらった。

支払いについて

自宅に帰ってからiPhoneを探すをオフにして再度電話した。

  • クレジットカードでの支払いのためのURLをメールで送ること
    • その場でメールが届いたか確認した
  • いったん本体全額分の与信を抑えられ、後から修理費用が確定されること
    • と言われたように思っていたが、修理代は最初から請求されるみたい
  • 修理の内容によるが、破損の状態からおそらく11800円+税がかかるだろうこと
  • 2〜3営業日で交換品が届くが金曜日の18時過ぎの対応のため、水曜日とかになるかもしれないこと

を案内してもらって終了した。

費用は破損の状態に関わらないっぽい。画面のガラスの破損の場合のみ3千円くらいで対応できるよう。

バックアップ

普段からこまめにバックアップはしてなかったので、交換前にバックアップを行うことにした。

もともと同期していたPCが使えなかった都合で、別のPCと初めて接続したため、iPhone側で確認が必要になった。

この時、パスコードを入力する必要があったが、誤タップが何度も発動してしまい、認証エラーを繰り返し、しばらくiPhoneを操作できなくなる…という事態が起きた。かなり焦った。

何とかパスコードの入力も成功してバックアップを取ることができた。

交換品の発送、受け取りと交換返品

サポートの案内通りだと、到着は水曜日かなと思っていたが、日曜日には発送しましたメールがAppleから届いた。月曜日のお昼頃にはヤマトの人が来て交換品を受け取った。月曜は自宅にいたので自分で対応できた。

受け取ったものは、

  • 新しい交換機のiPhone
  • SIMカードを出し入れするためのピン
  • こちらからAppleへの発送伝票の控え

受け取った時の箱にそのまま返品する端末を入れて、ヤマトの人に返すことになる。

もしかして運送中の何かしらのトラブルで返品した端末がAppleに届かない、みたいなことがあると全額負担になってしまうので、この控えはすぐに捨てない方が良さそう。

火曜日にはAppleから届いたよメールが来てた。早いな〜。

バックアップからの復元

到着した交換機にバックアップから復元を行おうとしたが、つまづいた。

このiPhoneのソフトウェアが古すぎるため、バックアップ”○○○”はこのiPhoneの復元には使用できません

というエラー。解決するためには、先に交換機のiOSのバージョンを上げないといけない。

  1. iTunesで新規端末として登録
  2. iOSのバージョンアップ
  3. バックアップからリカバリ

という手順になる。

LINEのアカウント復旧

SMSを利用した認証を行うと、旧端末が操作できなくても問題ない。トークの履歴やスタンプなどの復旧も特に問題なかった。

モバイルsuicaの復旧

これが結構めんどくさい。

  1. Suicaの再発行手続き
  2. 再発行手続きしても何の通知もない(サイト上で完結する)
  3. 再発行手数料はかからない
  4. 翌朝5時まで待つ
  5. Walletから追加
  6. SuicaアプリではなくWalletで対応する

再発行時は、メールの1通くらい送ってくれても良さそうなもんだけど、そういうのはない。再発行の翌日にならないと、Walletから追加することはできない。

公式の手順は以下。

キャリア用のプロファイルをインストールする

バックアップから復元してても入ってないらしくて、受け取った翌朝通勤電車でネット見れないくて気づいた。

違う端末があったのでそちらでテザリングしてなんとかインストールできたけど、ちょっとこれは困った。家で作業してる時はWi-Fiで繋がってて、気づかないし。

ほかのアプリとか

ほかのアプリは認証がそこまで厳しくないので、まずはパスワードマネージャーを復旧してから、そこから地味にサインインしていく感じ。

PlatformIOとVSCodeでArduino Microの開発環境を整える

Arduino Micro を購入して電子工作入門をしたけど、ArduinoIDEではなくVSCodeでコード書きたいので設定を行った。

参考:

PlatformIO Core のインストール

まずは、PlatformIO Coreを先にインストールしておく必要がある。

Pythonのバージョンは2.7に対応していて、pipでインストールできるとのことなので、virtualenvで環境を独立させることにした。pip、virtualenvのどちらもインストールされていなかったので、そこから。

sudo python -m ensurepip
sudo pip install virtualenv

cd mykeyboard
virturalenv venv
source ./venv/bin/activate

pip install platformio

VSCodeにPlatformIOの拡張機能をインストール

拡張機能の検索窓でPlatformと入力すると、拡張機能が表示される。個人が作ったものがあるので PlatformIO.Org が提供しているものインストールするように注意する。

インストール後、プロジェクトの開始もドキュメントにそってやっていったら特に問題はなかった。

ビルドやアップロードは下部のバーにあるアイコンからも実行できる。

実行するとTerminalのペインが有効になって、実行が確認できる。

画像引用:http://docs.platformio.org/en/latest/ide/vscode.html#installation

特定のワークスペースのみ拡張機能を有効にする

特に設定を行わないとPlatformIOの拡張機能は関係ないワークスペースでも有効になってしまう。VSCode自体の起動が若干もたつく感じがするのと、 不要なPlatformIO関連のチェックが非同期で実行され無駄な感じがする。

いったん拡張機能を無効にしておき、Arduino用のワークスペースで有効にすることができる。

  1. 拡張機能の歯車アイコンをクリック
  2. Disable を押してデフォルトでは無効にする
  3. Arduino用のワークスペースEnable(Workspace) を選択してワークスペースだけで有効にする

Ability to enable extensions only on specific workspaces

コマンドラインからビルドやアップロードする

VSCode上でタスクランナーなどを設定するのも良いが、Terminalからも作業を行いたい。

PlatformIO Coreをインストール済みなので、それを使用する。platform あるいは pio というコマンドが使えるようになっている。

# ビルドを行う
pio run

# アップロードを行う(差分があるとビルドも行う)
pio run -t upload

# ビルド済みのファイルを削除
pio run -t clean

PlatformIO IDE の各種タスクもこれらを実行しているようだ。実行時のコマンドがログに出ている。

また設定はワークスペースにある platformio.ini を参照する。このファイルはPlatformIO IDEでプロジェクトを開始していた場合には最初のウィザードで作成されていると思う。

[env:micro]
platform = atmelavr
board = micro
framework = arduino

こういう内容のファイルになっている。

参考: