docker-composeでgin+reactの開発環境を作る
ずっとバックエンドAPIにGoを使った開発がしたくて、趣味で作ってたRailアプリを作り直そうと思いまずは開発環境を作ってみた。
フロントに関しては別にこだわりはなかったんだけど、仕事はインフラ+バックエンドがメインでフロントはそこまでスキルがないので担当しているプロダクトにあわせて勉強がてらといういう意味でReactにしてみた。
構成
docker-compose.yml |-- api(ginのソースコード格納) |-- react(フロントのソースコード格納) `-- docker |-- api | |-- Dockerfile | |-- Gopkg.toml(※) | `-- main.go(※) |-- nginx | |-- Dockerfile | `-- nginx.conf `-- node `-- Dockerfile
※ docker/api
配下にある Gopkg.toml
と main.go
だが、後述する docker-compose.yml
内でapiコンテナにマウントさせている./api
配下のものに上書きされる。
ここにわざわざ配置しているのは、apiコンテナ作成時にdep ensure
によるパッケージインストールを実施したいため、両ファイル必要なのだが、マウントのタイミングがこのdep ensure
より後に実施されてしまうため事前にDockerfile内でCOPYしてやる必要があったため。
本当はdocker/api
配下ではなくapi
配下にあるファイルを置きたかったが、DockerfileのCOPYで指定するファイルがDockerfileより上位のディレクトリは指定できないようなので仕方なくこのような形にした。
ちなみにdocker/api/main.go
は空ファイルでもよい。
docker-compose.yml
作ったdocker-compose.yml
はこんな感じ
version: "3" services: node: build: ./docker/node volumes: - ./react:/var/www/html/ working_dir: /var/www/html/horsebase command: yarn start ports: - "3000:3000" db: image: mysql:5.7.22 environment: MYSQL_ROOT_PASSWORD: [password] MYSQL_PASSWORD: [password] MYSQL_DATABASE: [database] volumes: - ./mysql:/var/lib/mysql ports: - "3306:3306" api: build: ./docker/api volumes: - ./api:/go/src/project nginx: build: ./docker/nginx ports: - "1234:1234"
ginはサーバサイドレンダリングはせずにAPI提供のみとして構成している。
各Dockerfile
node
FROM node:8.9.4-alpine WORKDIR /var/www/html/ RUN npm install -g create-react-app
api
FROM golang:1.10.2-alpine3.7 COPY Gopkg.toml /go/src/horsebase-prophet/ COPY main.go /go/src/horsebase-prophet/ WORKDIR /go/src/horsebase-prophet/ RUN apk update \ && apk add --no-cache git \ && go get -u github.com/codegangsta/gin \ && go get -u github.com/gin-gonic/gin \ && go get -u github.com/golang/dep/cmd/dep \ && dep ensure CMD gin -i run
nginx
FROM nginx:latest RUN rm -rf /etc/nginx/conf.d/default.conf COPY ./nginx.conf /etc/nginx/conf.d/default.conf
CORS(Cross-Origin Resource Sharing)問題
Reactのaxiosを使って直接ginのAPIを叩いてもCORSのため、通信できない。
gin側のレスポンスヘッダにAccess-Control-Allow-Originを設定することで通信可能になるという情報もあったが、Chromeだとうまくいかなかった。
Chromeの場合は起動オプションを指定する必要があるらしい。
(参考)
開発時にCORSを無視するGoogleChromeの起動オプション - Qiita
今回はnginxをproxyとして利用し、react→nginx→ginのようにアクセスするようにした。
nginx.conf(抜粋)(これだけでも動く)
server { listen 1234; server_name localhost; location / { proxy_pass http://api:9000; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
react/project/src/App.js
import React, { Component } from 'react'; import axios from 'axios'; class App extends Component { constructor(props) { super(props); this.state = { message: "" }; this.getData = this.getData.bind(this); } getData() { axios .get('http://localhost:1234/api/greeting') .then(results => { const data = results.data; this.setState({ message: data['message'] }); }); } render() { return ( <div> <button onClick={this.getData}>getData</button> <p>{this.state.message}</p> </div> ); } } export default App;
APIへのアクセスはnginxで待ち受けている1234ポートに投げる。
nginxはnginx.conf
のproxy_pass
で定義したapiコンテナ内でginが待ち受けているポートへ。
api/main.go
package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/api/greeting", func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000") c.JSON(200, gin.H{ "message": "hello, world", }) }) r.Run(":9000") }
レスポンスヘッダのAccess-Control-Allow-Origin
にアクセス元のURLをセットする。
これをセットしないとCORSではじかれる。
まとめ
まとめというか、以下なんとかならんかな
- 同じcompose配下のコンテナならCORS意識しなくていい仕組みほしい
- volumesで指定したマウントのタイミングDockerfile内で定義したCMDやdocker-compose.yml内で定義したcommandのタイミングをコントロールしたい