lambda + RDB なシステムのローカル開発環境をdockerで


いつか誰かのあら＾〜便利になることを祈ってます。僕です。
[あら便利カレンダー2018](https://benri2018.rnd.cloudpack.jp/)

# 概要

serverless framework(併せてserverless offline)を用いたlambdaの開発を行うにあたり、ローカル開発環境の準備が必要だった。

DBはmariadb（mysql）。

そんなローカル開発環境をdockerコンテナで用意してみた。

# 構成

以下のモジュールを突っ込んだコンテナをdocker composeで構築する。

- nodeコンテナ
    - typescript
    - serverless framework
    - serverless offline
    - ts-loader
    - serverless webpack
    - mysql
    - その他諸々
- mariadbコンテナ
    - こっちは特にない

## 諸々のバージョン

大事そうなとこだけ抜粋。

- node: 8.11.3
- mariadb: 10.2.16
- typescript: 2.9.2
- webpack: 4.16.0
- serverless webpack: 5.2.0


# コンテナにするメリット

- dockerさえあれば開発もデプロイもできる
    - いつまでもあのころのmacのまま、環境汚染しない
    - デプロイ等で`sls`なコマンドを叩く時もコンテナ内で（コンテナ経由で）実行する
- コンテナ使うと環境の共有も楽
- VM重すぎるよぉ😫みたいなつらみもない


引き続き頑張ってdockerおじさん目指したい次第。


# ディレクトリ構造

sample_project/
　├ app/
　│　└ tsのいろいろ
　├ Dockerfile
　├ docker-compose.yml
　├ package.json
　├ webpack.config.js
　├ tsconfig.json
　└ serverless.yml

みたいな感じ。



# Dockerfile

アプリを載せるnodeのコンテナイメージを作るのに用意した。

railsとかだと依存物が結構あるので、`apk add` でパッケージの追加が必要だった。
~~みんな大好きnokogiriとかあのへんの兼ね合いだったかな~~

でも、nodeだとdocker hubにあるイメージのままほとんど手を加えてない。楽。

```Dockerfile:Dockerfile
FROM node:8-alpine

RUN mkdir /application

WORKDIR /application

COPY package.json ./

RUN \
    set -x \
    && npm install
```



# docker-compose.yml

```yaml:docker-compose.yml
version: '3'
services:
  slsdb:
    image: mariadb:10.2
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --skip-character-set-client-handshake
    ports:
    - "3306:3306"
    environment:
    - MYSQL_ROOT_PASSWORD
    - MYSQL_DATABASE
  slsapp:
    build: .
    command: npx serverless offline --host 0.0.0.0 --port 3000
    volumes:
    - .:/application
    - /application/node_modules
    ports:
    - "3000:3000"
    depends_on:
    - slsdb
```

コンテナ立ち上げ時にserverless offlineが動く。

## ここがポイント

`slsapp`の`volumes`に注目。

> volumes:
- .:/application
- /application/node_modules

`.:/application`は、**"ホストのdocker-compose.ymlと同じ場所のものをコンテナの/applicationにマウントする"** 、というよくある感じの記述です。

`/application/node_modules`はというと、この記述があることで以下のような挙動をする。

- Dockerfileを元にビルド
- コンテナには`node_modules`が作成される
- compose時にカレントディレクトリをマウント
- コンテナ内に存在した`node_modules`を寄せておいて、マウント後に寄せておいた`node_modules`を置き直す

つまり、ビルド時にコンテナ内できる`node_modules`を生かしたまま、ホストのファイル群をマウントできる感じ。

こうすることで、完全にlinux用の`node_modules`が作られる。

もしOS依存のモジュールがあった場合、macで作られた`node_modules`がalpineに入っていて動かないなんてことがあるかもしれない。
それを防げる。


こちらのサイト大変勉強になりました。
https://shotat.hateblo.jp/entry/2016/12/01/221631


## 補足:ライブリロード

ローカルでファイル編集するたび、tsのコンパイルがされ、ログ出力がされる状態にしたかった。

以下のような感じで設定すれば大丈夫そう。

```json:tsconfig.json
"noEmitOnError": false
```

参考までにwebpackの設定も載せておく。

```js:webpack.config.js
const path = require("path");
const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  mode: "none",
  entry: slsw.lib.entries,
  target: "node",
  devtool: "source-map",
  externals: [nodeExternals()],

  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader"
      }
    ]
  },

  resolve: {
    extensions: [".ts", ".js", ".tsx", ".jsx"]
  },

  output: {
    libraryTarget: "commonjs",
    path: path.join(__dirname, "dist", "lambda"),
    filename: "[name].js"
  }
};
```

変更を保存するたびに、エラーがあろうがなかろうがコンパイルしてログ吐くって感じになります。


ただ、WebStormでの挙動がちょっと怪しい雰囲気。
webpackの "[Saving in WebStorm](https://webpack.js.org/configuration/watch/#saving-in-webstorm)" がちょっと気になる。
そのほか自動保存のスパンを1secとかにしてみたりして、調査中。


# コンテナ経由でserverlessコマンド

例として`serverless info`を実行するときのコマンドがこんな感じ。

```
$ docker-compose run --no-deps --rm slsapp npx serverless info
```

`docker run`とかで実行しちゃうと、`docker-compose.yml`で`slsapp`に環境変数を設定した場合なんかはそれが適用されないので注意。

デプロイ等もこんな感じのコマンドで実行できる。