LoginSignup
27
38

More than 3 years have passed since last update.

サーバーレスなWebアプリを、Dockerコンテナ上に移植する

Last updated at Posted at 2020-04-21

概要

以前Qiitaで公開した、Vue.jsとAWSでつくるシングルページの家計簿アプリを、Dockerコンテナ上に移植しました。そこで、その過程をここに軽くまとめておきます。

GitHubのリポジトリは、こちらです。docker-composeコマンドが使えるサーバーにコードをデプロイすれば、使えるようになると思います。

インストールのコマンド
# コードをGitHubから取得
git clone https://github.com/KMimura/AccountingApp
# 取得されたフォルダに移動
cd AccountingApp
# docker-composeの立ち上げ
docker-compose up -d

元のサーバーレスのアプリ

同アプリは、元々サーバーレスな感じでデプロイしてありました。
tmp.png

Vue.jsで作ったフロントエンドの部分はS3上に静的ホスティングをされていて、各種データは、Lambda上で動くPythonで書かれたAPIプログラムを通して、RDSに格納されていました(LambdaとRDSの組み合わせは悪いですが、自分一人しかアクセスしないアプリだったので、問題はありませんでした)。

これらを、1. S3上にデプロイされてたフロントエンド部分2. Lambda上にデプロイされてたAPI部分3. RDS上にデプロイされてたDB部分の3つのコンテナに分けて、docker-composeで動かせるようにしました。

Docker移行の詳細

1. フロントエンド用コンテナ

元々はS3上に静的ホスティングされていた部分のコンテナです。以下がDockerfileです(ファイル全体はこちら)。

FROM nginx:alpine

COPY ./nginx/website.conf /etc/nginx/conf.d
RUN rm /etc/nginx/conf.d/default.conf
WORKDIR mkdir -p /var/www/accounting
COPY ./website /var/www/accounting

nginxの設定ファイルをCOPYしていますが、内容は以下のようになっています。

server {
    listen *;
    server_name ~^.*$;

    location /accounting/ {
        root /var/www/;
    }

    location /accounting-api {
        proxy_pass http://apiserver:8080/accounting-api;
    }
}

/accounting/にリクエストが来たら静的フロントエンドファイルにアクセスさせて、/accounting-api/にリクエストが来たら、後述のAPIコンテナにアクセスさせます。

ちなみにこのアプリはアクセス制限元のIPアドレスを制限してあるのですが、サーバーレスの時にはs3のポリシー設定でそれを行っていましたが、移行後はnginxの設定でそれを行うようにしました。

2. API用コンテナ

元々はLambda上にデプロイされていたAPIです。以前はAPI Gatewayが、Pythonのプログラムが動くLambda関数にリクエストを振り分けていたのですが、それをGo言語のGinというフレームワークを用いたものに変えました。

API部分のプログラムの一部を抜粋すると、以下のような感じです。

func main() {
    // ~ 省略~
    r := gin.Default()
    r.GET("/accounting-api/", func(c *gin.Context) {
        log.Println(c.Request.URL.Host)
        log.Println(c.Request.URL.Path)
        results := getMethod(c, env)
        // ~ 省略~
        c.JSON(200, result)
    })
    r.POST("/accounting-api", func(c *gin.Context) {
        log.Println(c.Request.URL.Host)
        log.Println(c.Request.URL.Path)
        res := postMethod(c, env)
        status := 200
        message := "Success"
        if !res {
            status = 500
            message = "Internal Server Error"
        }
        c.JSON(status, gin.H{
            "state": message,
        })
    })
    r.DELETE("/accounting-api", func(c *gin.Context) {
        log.Println(c.Request.URL.Host)
        log.Println(c.Request.URL.Path)
        res := deleteMethod(c, env)
        status := 200
        message := "Success"
        if !res {
            status = 500
            message = "Internal Server Error"
        }
        c.JSON(status, gin.H{
            "state": message,
        })
    })
    log.Println("Start Server")
    r.Run(":8080")
}

リクエストのメソッドに応じて呼び出す関数を変えているということが、雰囲気からわかるかと思います(コード全体はこちら)。

このプログラムが動くコンテナのDockerfileが、以下です。

FROM golang:alpine
RUN apk update && apk add --no-cache git
RUN go get -u github.com/gin-gonic/gin && go get -u github.com/jinzhu/gorm && go get -u github.com/go-sql-driver/mysql
RUN mkdir /usr/src && mkdir /usr/src/api
COPY ./api /usr/src/api
WORKDIR /usr/src/api
CMD ["go","run","main.go"]

特に凝ったことはせず、素直に動かしてます。余談ですが、Ginサーバーを動かす際に出たエラーに関して以前戸惑ったので、記事にしてあります。

3. DB用コンテナ

大したことはしてないので、Dockerfileは作ってません。このコンテナについてはdocker-compose.ymlで定義してあります。

Windowsの環境で動かすときにエラーが起こったので、その内容を以前記事にしました。

docker-compose.yml

3つのコンテナをまとめて動かすdocker-compose.ymlは、以下のような設定になっています。

version: '3.3'
services:
  web:
    build: ./website/
    ports: 
      - '80:80'
  db:
    image: mysql:5.7
    volumes: 
      - "/tmp/db/data:/var/lib/mysql"
    restart: always
    hostname: db
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-test}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-test}
      MYSQL_USER: ${MYSQL_USER:-test}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-test}
      TZ: 'Asia/Tokyo'
    container_name: db
    ports:
      - 3306:3306
    command: --innodb-use-native-aio=0
  api:
    depends_on:
      - db
    build: ./api/
    environment:
      MYSQL_DATABASE: ${MYSQL_DATABASE:-test}
      MYSQL_USER: ${MYSQL_USER:-test}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-test}
    container_name: api

webapiに、apidbにそれぞれアクセスするようになっているのですが、そのための正しい設定を模索するのに時間がかかりました。最終的には、上のような感じでうまくいくようになりました。

環境変数系はすべてデフォルト値がtestとなっています。git pullしてきたディレクトリに.envというファイルを作ってその中に変数を書き込むことで、値は変えられます。

まとめ

とりあえず元々動いていたものを丸っとDockerに持ってくることができました。今後は同アプリに認証機能とかをつけてみたいと思います(Ginサーバーで認証機能をつけている例があんまりないので、いろいろ大変そう)。

27
38
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
38