ExpressでRest APIを開発してDockerでデプロイしてみた

#TL;DR
Dockerの入門がてらREST APIを作ってリモートのVMにDockerを利用してデプロイしてみた。

#開発環境
リモートVM・・・Ubuntu 16.04 LTS
ローカル・・・macOS Mojave 10.14.1

フレームワーク・・・Express 4.x

#リモートのVMにDockerをインストール
```
$ sudo apt update
$ sudo apt install docker.io
$ sudo apt install docker-compose
```

#ローカルの環境にDockerをインストール
[ここ](https://hub.docker.com/editions/community/docker-ce-desktop-mac?tab=description)からDocker Desktopをインストール

```
$ docker
```
これでヘルプがだーっと出てくればインストール成功

# イメージを作成する
とりあえず一旦APIの中身は下記のようにミニマルにしてイメージを作ってみる。

```typescript:app.ts
import * as Express from 'express';
import * as log4js from 'log4js';
import { config } from 'src/config';

log4js.configure(<log4js.Configuration>config.log4js);
const logger = log4js.getLogger();

const app = Express();

app.get('/', (req: Express.Request, res: Express.Response) => {
  return res.send('yeah');
});

const server = app.listen(3000, () => {
  logger.info('listen on port 3000');
});
export { server };
```

#使うパッケージはこちら
`package.json` は今回はこんな感じ

```json:package.json
{
  "name": "my-project",
  "version": "1.0.0",
  "description": "hogehoge",
  "main": "dist/app.js",
  "scripts": {
    "test": "test",
    "build": "gulp build",
    "serve": "pm2 start -f ./dist/app.js -n hogehoge --watch",
    "delete": "pm2 delete timecard -s || :",
    "start": "npm run delete && npm run serve",
    "docker_service": "npm run serve"
  },
  "author": "Makoto Tone",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4",
    "log4js": "^3.0.6",
    "pm2": "^3.2.4"
  },
  "devDependencies": {
    "@types/express": "4.16.0",
    "@types/log4js": "2.3.5",
    "gulp": "3.9.1",
    "gulp-absolute-path": "1.0.4",
    "gulp-typescript": "5.0.0",
    "rimraf": "2.6.2",
    "run-sequence": "2.2.1",
    "tslint": "5.12.0",
    "tslint-microsoft-contrib": "6.0.0",
    "typescript": "3.2.2"
  }
}

```

#Dockerのイメージを作成する
dockerを利用して実行するにあたりまずはイメージを作る必要があります。
ここで使うのが、`DockerFile`です。これは、イメージを作る際に行う処理であり、この処理が行われた状態が再現されるようになります。
中身はこんな感じ。

```dockerfile:Dockerfile
# 公式のnode:8イメージをベースにする
FROM node:8

# これ以降の処理は/usr/src/appで行う。
WORKDIR /usr/src/app

# リポジトリ内のファイルをコンテナ内にコピー
COPY package.json package.json
COPY package-lock.json package-lock.json

# コンテナにパッケージをインストール
# 今回はpm2を使ってデーモン化します
RUN npm install
RUN npm install -g pm2

# リポジトリ内のファイルをまるっとコンテナ内にコピー
# 実際は.dockerIgnoreというファイルを用意して無視するファイルを指定しています。（.gitignore的なやつですね。）
COPY . .

# コンテナ内にコピーされたソースをビルド
RUN npm run build

# ここで指定するコマンドが、コンテナを立ち上げた時に実行されるコマンドになります。
CMD npm run docker_service

```
これをプロジェクトルートに作成し、プロジェクトルートで

```bash
$ docker build --tag=hogehoge ./
```

これでイメージができます。が、後述の`docker-compose`でイメージ作成からまとめてやってくれますのでこのコマンドは使いません。

# コンテナを立ち上げる
`docker run`とかでも立ち上がるようですが、せっかくなので`docker-compose`を利用しようと思います。
今回作った`docker-compose.yml`はこちら。

```yml:docker-compose.yml
version: "2"
services:
  hogehoge:
    build: ./
    container_name: hogehoge
    ports:
      - 80:3000
```
これをプロジェクトルートに配置し、

```bash
$ docker-compose up -d
```

立ち上がっているかどうか確認するため

```bash
$ docker ps
```

コンテナが表示されていれば成功です.

```
CONTAINER ID        IMAGE      COMMAND                  CREATED             STATUS              PORTS                    NAMES
38423578b303        hogehoge   "/bin/sh -c 'npm run…"   57 seconds ago      Up 56 seconds       0.0.0.0:3000->3000/tcp   hogehoge
```

localhost:3000にアクセスすると・・・
![スクリーンショット 2018-12-27 17.45.57.png](https://qiita-image-store.s3.amazonaws.com/0/153926/014d9d12-064e-1a43-313d-8a16d8567565.png)

出ました。成功です。

# リモートのVMで実行する
今回はリモートのVMにSSHでログインし、gitのリポジトリをまるごと持ってきてリモートのVM上でビルドします。
やることは以下の通り。

```bash
$ git clone xxx@xxxxxxxxxxxxx
$ cd yourProject
$ sudo docker-compose up -d
```

そしてリモートVMのIPにアクセスすると・・・
![スクリーンショット 2018-12-27 17.48.24.png](https://qiita-image-store.s3.amazonaws.com/0/153926/90d6aa10-c372-c28b-945d-bec64826dc6a.png)
きました、成功です。

# まとめ
今回はExpressで開発したRestAPIをdockerでイメージ化して、リモート上で実行するという内容でした。
今回はgitのリポジトリをまるごとcloneしましたが、それではdockerの利点を半分も生かせていないと感じたので、今後は作ったイメージそのものを共有してリモートでビルドする必要すらも無くしたいですね。

公開してもいいソースコードならDocker Hubで済むのですが、社内で使うシステムなので公開できないので苦渋の決断です。
来年はコンテナレジストリを社内専用で立てて、社内でimage共有できるようにしていきたいですね。