概要
以前Qiitaで公開した、Vue.jsとAWSでつくるシングルページの家計簿アプリを、Dockerコンテナ上に移植しました。そこで、その過程をここに軽くまとめておきます。
GitHubのリポジトリは、こちらです。docker-compose
コマンドが使えるサーバーにコードをデプロイすれば、使えるようになると思います。
# コードをGitHubから取得
git clone https://github.com/KMimura/AccountingApp
# 取得されたフォルダに移動
cd AccountingApp
# docker-composeの立ち上げ
docker-compose up -d
元のサーバーレスのアプリ
同アプリは、元々サーバーレスな感じでデプロイしてありました。
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
web
はapi
に、api
はdb
にそれぞれアクセスするようになっているのですが、そのための正しい設定を模索するのに時間がかかりました。最終的には、上のような感じでうまくいくようになりました。
環境変数系はすべてデフォルト値がtest
となっています。git pull
してきたディレクトリに.env
というファイルを作ってその中に変数を書き込むことで、値は変えられます。
まとめ
とりあえず元々動いていたものを丸っとDockerに持ってくることができました。今後は同アプリに認証機能とかをつけてみたいと思います(Ginサーバーで認証機能をつけている例があんまりないので、いろいろ大変そう)。