Help us understand the problem. What is going on with this article?

Go(Echo) + Vue.js + nginx の環境をDocker Composeで立てる。

はじめに

goa、ginと触ったので、Echoあたりも触ってみようと思ったのと、自分自身、サーバーサイドもフロントエンドも一括で担当することが多いこともあり、「どうせだ、docker環境でフロントエンドも含めて環境を作ってみっか。」というのが始まりです。

GoコンテナはAPIを配信、Vue.jsでAPIを叩いて、値を画面にレンダリング。
みたいな想定でやっています。また、nginx噛ませてルーティングさせてます。

駄文だったらすいません。
先に言っておくと、長文です。

なお、以下のようにしています。

Go

  • Go Modules
  • ホットリロード

を利用してます。

Vue.js

  • vue-cli3
  • axios

を利用してます。

環境

  • Mac OS X 10.14.6(Mojave)
  • Docker version 19.03.2
  • docker-compose version 1.24.1

※ ベースの環境だけ記述しておきます。GoのバージョンなどはDockerfileに記述してますので。

手順

初めのファイル構造

こっからスタートします。
nginxは後から設定するのでmyapp/etc/nginx配下は一旦空にしています。

myapp
├── docker
│   ├── go
│   │   └── Dockerfile
│   └── vue
│       └── Dockerfile
├── docker-compose.yml
└── etc
    └── nginx
        └── .

各Dockerfileの中身

Go

非常にシンプルです。
Goのバージョンは1.13。
Go Modulesを利用するため、ENV GO111MODULE=onを指定。
ホットリロード環境を作りたいのでfreshgo getしています。

myapp/docker/go/Dockerfile
FROM golang:1.13.1-alpine

ENV GO111MODULE=on

RUN apk update && \
    apk add emacs curl git && \
    go get github.com/pilu/fresh

Vue.js

nodeのバージョンは、現時点(2019年10月)で最新の8.16
(何をみていたのかよくわからないですね。8.16は全然最新じゃないです。)
vue-cli3を利用するので@vue/clinpm installしています。

mysqpp/docker/vue/Dockerfile
FROM node:8.16-alpine

RUN apk update && \
    npm install -g npm && \
    npm install -g @vue/cli

docker-compose.ymlの中身(初期)

nginxの設定は後で足します。
まずは、GoとVue.jsのコンテナを作るところから始めていきたいので。

docker-compose.yml
 version: '3'
 services:
   vue:
     build:
       context: ./docker/vue
     container_name: myapp-vue
     ports:
       - 8080:8080
     volumes:
       - .:/app
     working_dir: /app
     tty: true
     # command: sh -c "cd assets && yarn install && yarn serve"

   go:
     build:
       context: ./docker/go
     container_name: myapp-go
     ports:
       - 8082:8082
     volumes:
       - .:/go/src
     working_dir: /go/src/app
     tty: true
     # command: fresh

ちなみに、govue共にcommandがコメントアウトしているのは、一旦コンテナで作業する必要があるので、コメントアウトしています。
goは、Go Modulesの初期設定をしてからホットリロードを設定しないとエラーになります(なりました)。
vueは、コンテナ内でvue createをしたいので、コメントアウトしています。

コンテナ立ち上げ

まずはなにはともあれ、ビルドします。

$ docker-compose build

※ npm周りでWARNが出る可能性がありますが、ここは無視します。
 

up -dでバックグラウンド立ち上げをします(オプション-d無くてもいいですが、その場合は以降は別タブで作業してください)。

$ docker-compose up -d

 

コンテナが立ち上がっているか確認。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                               NAMES
9897f0c5c15c        nginx               "nginx -g 'daemon of…"   9 seconds ago       Up 7 seconds               0.0.0.0:10080->80/tcp               myapp-nginx
9e8c34dacba8        kaikairanban_go     "/bin/sh"                11 seconds ago      Up 9 seconds               0.0.0.0:8082->8082/tcp              myapp-go
7178d2a496dc        kaikairanban_vue    "docker-entrypoint.s…"   40 seconds ago      Up 39 seconds              0.0.0.0:8080->8080/tcp              myapp-vue

この時点でappディレクトリがmyappディレクトリ内にできているので、ls -laとかで確認してみてください。

Goのコンテナを設定していく

appディレクトリに移動して、main.goファイルを作ります。

$ cd app && emacs main.go

※ emacsの部分は、vimでもなんでもいいです。筆者はemacsを使っているのでこうなります。
 

main.goの中身は一旦こんな感じにしておきます。

main.go
package main

import (
       "fmt"
)

func main () {
     fmt.Println("Hello World !!")
}

 
Goのコンテナに入ります。

$ docker exec -it myapp-go /bin/sh
/go/src/app #
 
# コンテナ内で ls -la で確認してみるとこんな感じになっているはず。
/go/src/app # ls -la
total 4
drwxr-xr-x    4 root     root           128 Oct  3 08:35 .
drwxr-xr-x    5 root     root           160 Oct  3 08:25 ..
-rw-r--r--    1 root     root            90 Oct  3 08:35 main.go
drwxr-xr-x    2 root     root            64 Oct  3 08:25 tmp

 
コンテナ内でGo Modulesの初期設定をします。

/go/src/app # go mod init
go: creating new go.mod: module app

/go/src/app # ls -la
total 8
drwxr-xr-x    5 root     root           160 Oct  3 08:41 .
drwxr-xr-x    5 root     root           160 Oct  3 08:25 ..
-rw-r--r--    1 root     root            20 Oct  3 08:41 go.mod
-rw-r--r--    1 root     root            90 Oct  3 08:35 main.go
drwxr-xr-x    2 root     root            64 Oct  3 08:25 tmp

go.modができましたね。
 
Ctr + dとかでコンテナの外に出て、go.mogファイルの中身を確認すると以下のうようになっています。

go.mod
module app

go 1.13

 
では、Echoを導入していきます。
コンテナの外からで良いので、以下のようにmain.goを編集します。
なんてことはない、Echoのクイックスタート(ほぼ)そのままです。

main.go
package main

import (
       "net/http"
       "github.com/labstack/echo"
)

func main () {
     e := echo.New()
     e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
     })
     e.Logger.Fatal(e.Start(":8082"))
     // e.Startの中はdocker-composeのgoコンテナで設定したportsを指定してください。
}

 
main.goが編集できたら、docker-compose.ymlのgoコンテナの記述部分のcommandのコメントアウトを外します。

docler-compose.yml
# docker-compose.ymlの一部抜粋
go:
  build:
    context: ./docker/go
  container_name: myapp-go
  ports:
    - 8082:8082
  volumes:
    - .:/go/src
  working_dir: /go/src/app
  tty: true
  command: fresh # ここのコメントアウトを外す。

保存したら、docker-compose upで更新します(一旦、一つ前のコンテナ内作業後、docker-compose downでコンテナを停止しておいても良いです)。

以下のような状態になったら、Echo導入完了&ホットリロードもOnになりました。

$ docker-compose up
myapp-vue is up-to-date
Recreating myapp-go ... done
Recreating myapp-nginx ... done
Attaching to myapp-vue, myapp-go, myapp-nginx
myapp-go | 8:53:07 runner      | InitFolders
myapp-go | 8:53:07 runner      | mkdir ./tmp
myapp-go | 8:53:07 runner      | mkdir ./tmp: file exists
myapp-go | 8:53:07 watcher     | Watching .
myapp-go | 8:53:07 main        | Waiting (loop 1)...
myapp-go | 8:53:07 main        | receiving first event /
myapp-go | 8:53:07 main        | sleeping for 600 milliseconds
myapp-go | 8:53:08 main        | flushing events
myapp-go | 8:53:08 main        | Started! (5 Goroutines)
myapp-go | 8:53:08 main        | remove tmp/runner-build-errors.log: no such file or directory
myapp-go | 8:53:08 build       | Building...
myapp-go | 8:53:14 runner      | Running...
myapp-go | 8:53:14 main        | --------------------
myapp-go | 8:53:14 main        | Waiting (loop 2)...
myapp-go | 8:53:14 app         |
myapp-go |    ____    __
myapp-go |   / __/___/ /  ___
myapp-go |  / _// __/ _ \/ _ \
myapp-go | /___/\__/_//_/\___/ v3.3.10-dev
myapp-go | High performance, minimalist Go web framework
myapp-go | https://echo.labstack.com
myapp-go | ____________________________________O/_______
myapp-go |                                     O\
myapp-go | 8:53:14 app         | ⇨ http server started on [::]:8082

上記の状態で、localhost:8082にアクセスすると、以下のような画面が表示されます。
スクリーンショット 2019-10-03 12.21.43.png

見事に世界に挨拶ができましたね。

ちなみに、go.modはこんな感じになっています。

go.mod
module app

go 1.13

require (
        github.com/labstack/echo v3.3.10+incompatible
        github.com/labstack/gommon v0.3.0 // indirect
        golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc // indirect
)

Vueのコンテナを設定していく

コンテナが立ち上がっている状態で、vueのコンテナに入ります。

$ docker exec -it myapp-vue /bin/sh
/app #

vue-cliでvue環境を設定していきます。

/app # vue create assets

上記コマンドを打つと、質問形式で色々聞かれます。

?  Your connection to the default yarn registry seems to be slow.
   Use https://registry.npm.taobao.org for faster installation? (Y/n) Y ←Yesにします。

と聞かれた後、Vueの設定に入っていきます。
お好みで設定すれば良いと思いますが、今回は以下のようにします。

スクリーンショット 2019-10-03 12.41.11.png
スクリーンショット 2019-10-03 12.45.38.png

Enterを無心で押していくと、以下のような感じで設定が始まります。
スクリーンショット 2019-10-03 12.45.54.png

サクセスすると、以下のような表示になるかと思います。
スクリーンショット 2019-10-03 18.10.01.png
 
ここまできたら、Ctr + dでコンテナを抜けます。
docker-compose downで一旦コンテナを落としておきます。
そして、docker-compose.ymlのvueコンテナの部分でコメントアウトにしていた部分のコメントアウトを外します。

vue:
  build:
    context: ./docker/vue
  container_name: myapp-vue
  ports:
    - 8080:8080
  volumes:
    - .:/app
  working_dir: /app
  tty: true
  command: sh -c "cd assets && yarn install && yarn serve" # ここのコメントアウトを外す。

 
バックグラウンドでコンテナを立ち上げた後、vueコンテナのlogだけを表示させてみます。

$ docker-compose up -d
$ docker-compose logs -f vue

 
以下のような感じでvueコンテナが立ち上がればOKです。
スクリーンショット 2019-10-03 18.17.43.png
 
上記画面が出た状態で、localhost:8080にアクセスすると、以下のような画面が表示されます。
スクリーンショット 2019-10-03 18.20.06.png
Vue.js Appの世界に無事、迎え入れられてもらうことができました。

nginxのコンテナを作る

一旦、docker-compose downで、コンテナを落としておきます。
そして、docker-compose.ymlに追記&nginx.confを作ります。

docker-compose.yml
version: '3'
services:
  vue:
    build:
      context: ./docker/vue
    container_name: myapp-vue
    ports:
      - 8080:8080
    volumes:
      - .:/app
    working_dir: /app
    tty: true
    command: sh -c "cd assets && yarn install && yarn serve"

  go:
    build:
      context: ./docker/go
    container_name: myapp-go
    ports:
      - 8082:8082
    volumes:
      - .:/go/src
    working_dir: /go/src/app
    tty: true
    command: fresh
  # こっから下を追加
  nginx:
    image: nginx
    depends_on:
      - go
    container_name: myapp-nginx
      ports:
      - 80:80
    environment:
      - TZ=Asia/Tokyo
    volumes:
      - ./etc/nginx/nginx.conf:/etc/nginx/nginx.conf
nginx.conf
worker_processes auto;

events {
  worker_connections 1024;
}

http {
  server {
    listen 80;

    location /api/ {
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Host $host;
      proxy_pass http://go:8082/;
    }

    location / {
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Host $host;
      proxy_pass http://vue:8080/;
    }
  }
}

 
準備ができたら、docker-compose up -dでコンテナを立ち上げます。
docker-compose logs -fなどで、go、Vueどちらのコンテナも立ち上がって、準備ができたのを確認できたら、http://localhost でアクセスしてみましょう。
スクリーンショット 2019-10-03 18.20.06.png
この画面になったらOKです。

Vue側からGo側へリクエストを送り、値を得る。

axiosを使って、GoのAPIを叩き値を取ってくることを想定して、もう少しファイルを編集していきます。

axiosを使えるようにする。

axiosを使えるようにするため、package.json(assetsディレクトリ内にあるはずです)を編集します。
dependenciesの部分にaxiosを追加します(バージョンは、2019年10月最新のものにしてます)。
併せて、console.log()が入っているときにWaringが出ないようにするため、esLintConfgrulesに、
"no-console": "off"を追加します。

package.json
{
  ...
  "dependencies": {
    "core-js": "^2.6.5",
    "vue": "^2.6.10",
    "axios": "^0.19.0"
  },
  ...
  ...
  "eslintConfig": {
    ...
    "rules": {
      "no-console": "off"
    },
    ...
  },
  ...
}

 
グローバルにaxiosを使えるように、main.jsに登録をします。

main.js
import Vue from 'vue'
import App from './App.vue'

// ここから
import axios from 'axios'
Vue.prototype.$axios = axios
// ここまでを追加

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

ファイルが編集できたら、docker-compose restart vueで、設定を更新します。

axiosで、GoのAPIを叩く。

今回は超簡易的に、

  1. ブラウザで http://localhost にアクセス。
  2. APIが叩かれる(GET)。
  3. Go側からレスポンスを返す。
  4. console.log()を使って、ブラウザのコンソール画面でレスポンスの中身を確認する。

ということを行います。

では、App.vueを編集していきます。
Vueのcreatedフックのなかで、axiosを使うように記述します。
(変更箇所のあるscriptタグのブロックだけ以下に表示させてます。)

App.vue
// <template> のブロックがある

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'app',
  components: {
    HelloWorld
  },
  // createdの中でaxiosを使います。get()の中のURLは、nginx.confで設定してるので、 /api/ になっています。
  created () {
    this.$axios.get('http://localhost/api/')
      .then(response => {
        console.log(response)
      })
  }
}
</script>

// <style> のブロックがある

できたら、ブラウザの開発者ツールでコンソールタブを開いて、 http://localhost にアクセスしてみましょう。
(もしくは、リロードしてみましょう。)

スクリーンショット 2019-10-04 12.51.09.png

画像のように、レスポンスが返ってきているのが確認できたらOKです。
あとは、返ってきた値を上手く調理するなどして、画面にレンダリングするとかしてみてみるといいんじゃないでしょうか。
(その辺は、余裕あったら続きを書こうと思います。記録としても残しておく意味で。)

以上。

69incat
教育系営業→生保営業→人材屋(キャリアアドバイザー)→自社Web系中小でWebエンジニア→医療人材サービス、Web系企業でエンジニア・プログラマー。 サーバーサイド、フロントエンド、時々インフラをやってる人。 Perl、Go、PHP、HTML、CSS、Javascript、MySQL、AWS...などなど。 最近は、PHP、Go、Vue、Reactなどなど...。 猫と音楽は正義。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away