Nuxtとgoのアプリケーションをdocker-composeで作っていこうと思う。以前、Nuxtの方は作ったからそれを元に作っていこう。
ディレクトリ構成
ディレクトリ構成はこうしよう。backendとかfrontendディレクトリにそれぞれのソースコードが入るイメージ。
├── backend │ └── Dockerfile ├── docker-compose.yml └── frontend └── Dockerfile
ベストプラクティス的にはダメだけど、とりあえず動かす事が目的だから簡単にいきたい。
ほとんどの場合、空のディレクトリに個々の Dockerfile を置くのがベストです。そうしておけば、そのディレクトリには Dockerfile が構築に必要なファイルだけ追加します。
Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント
あと、goのアプリケーションからdbに接続することを想定するからmysqlのコンテナも作る。postgresはあまり好きじゃないんだ・・・
Nuxtアプリケーションを作成する
buildで指定するDockerfileの場所を変更した。
// frontend/Dockerfile version: '3' services: nuxt-app: # Dockerfileの場所 build: context: ./frontend dockerfile: Dockerfile working_dir: /frontend command: yarn run dev # ホストOSとコンテナ内でソースコードを共有する volumes: - ./frontend:/frontend # コンテナ内部の3000を外部から5000でアクセスする ports: - 5000:3000
別にcontext
使わなくても、build
とdockerfile
で動かすことできるけど、contextではGitリポジトリのURLを指定する事ができるから、まあ柔軟にできる感はある。
書いたら、nuxtのアプリケーションをcreate-nuxt-app
で作る。
$ docker-compose run --rm nuxt-app npx create-nuxt-app
色々聞かれるから、適当に回答しておこう。こんな感じにしておいた。
create-nuxt-app v2.15.0 ✨ Generating Nuxt.js project in . ? Project name frontend ? Project description My impeccable Nuxt.js project ? Author name ? Choose programming language TypeScript ? Choose the package manager Yarn ? Choose UI framework None ? Choose custom server framework None (Recommended) ? Choose the runtime for TypeScript @nuxt/typescript-runtime ? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Choose test framework None ? Choose rendering mode Universal (SSR) ? Choose development tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
作成が終わったら、frontendディレクトリ配下にnuxtのファイル群ができているはず。
goアプリケーションを作成する
Nuxtのアプリケーションの作成は終わったから次はgoのアプリケーションをdocker-composeで書く。
// backend/Dockerfile # 使用するGolangのイメージを指定する FROM golang:1.14.2-alpine ENV GO111MODULE=on # 必要なパッケージなどなどをインストールする RUN apk update \ && apk add --no-cache git EXPOSE 8080
docker-compose.ymlには新しくgo-appっていうservice名で作る。nuxtアプリケーションの記述の下に書いていく。
// docker-compose.yml go-app: build: context: ./backend dockerfile: Dockerfile working_dir: /backend # docker-compose run実行時に実行される command: go run main.go volumes: - ./backend:/backend ports: - "8080:8080"
docker-compose up
で起動したらgoのアプリケーションを動かしたいからcommandにgo run main.go
を指定。なので、main.go
をbackendディレクトリ配下に作る。
// main.go package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World") } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
これで、docker-compose upしよう。serviceを作る時にgo-appという名称で作ったから、go-app指定。
$ docker-compose up go-app
Hello, Worldが出てきますね。
mysqlをdocker-compose.ymlに記載する。
さっきまででNuxtとgoのアプリケーションはdocker-compose.ymlに書いたけど、データベースは作っていない。goからはデータベース使う想定だから、mysqlのコンテナを作ろう。docker-compose.ymlに追加する。
// docker-compose.yml db: image: mysql:5.7 command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: mysql_dev MYSQL_USER: U MYSQL_PASSWORD: passw0rd ports: - "3306:3306"
起動してみよう。
$ docker-compose up db
起動したらmysqlに接続できるか確かめる。
$ docker-compose exec db bash $ mysql -uroot -p パスワードが聞かれるからMYSQL_ROOT_PASSWORDで指定したrootと入力 $ show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | mysql_dev | | performance_schema | | sys | +--------------------+
入れました。
goからmysqlに接続できるようにする
必要なコンテナは揃ったから、次はgoのアプリケーションからmysqlに接続できるようにする。
docker-compose.ymlのgo-appにdepends_on: -db
を追加。
go-app: build: context: ./backend dockerfile: Dockerfile depends_on: - db working_dir: /backend # docker-compose run実行時に実行される command: go run main.go volumes: - ./backend:/backend ports: - "8080:8080"
コードをいじる前に、適当にテーブルとレコードを作っておく。とりあえずusersテーブルにnameカラム作って入れておこう。
$ docker-compose exec db bash $ mysql -uroot -p パスワード入力 $ use mysql_dev $ create table users (id int, name varchar(255)); $ insert into users (id, name) values (1, "U"); $ select * from users;
ここで作ったレコードをgoでselectするようにする。
mysqlに接続するためのパッケージをimportするけど、今回はgo mod使うからbackendディレクトリで以下コマンドでファイルを作る。
$ go mod init backend
そしたら、main.goファイルをいじろう。mysqlにつなげるか確認するだけのコードを書く。
package main import ( "fmt" "net/http" "database/sql" "log" _ "github.com/go-sql-driver/mysql" ) func handler(w http.ResponseWriter, r *http.Request) { db, err := sql.Open("mysql", "root:root@tcp(db:3306)/mysql_dev") if err != nil { log.Fatal(err) } defer db.Close() var name string err = db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name) if err != nil { log.Fatal(err) } fmt.Fprintf(w, name) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
これでdocker-compose upで立ち上げてさっきmysqlにinsertしたレコードのnameが表示されればいいですね。
$ docker-compose up go-app
OK。
Nuxtとつなげる
goのアプリケーションからmysqlにつないでnameを取得できたけど、まだnuxtからは確認できない。Nuxtとgoをつないでnuxtアプリケーションの画面上にnameが表示されるようにしよう。Nuxtではjsonで情報を受け取りたいから、goでjsonを返すようにする。
package main import ( "net/http" "database/sql" "log" "encoding/json" _ "github.com/go-sql-driver/mysql" ) type User struct{ Name string `json:"name"` } func handler(w http.ResponseWriter, r *http.Request) { db, err := sql.Open("mysql", "root:root@tcp(db:3306)/mysql_dev") if err != nil { log.Fatal(err) } defer db.Close() var name string err = db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name) if err != nil { log.Fatal(err) } user := &User{ Name: name, } res, err := json.Marshal(user) if err != nil { log.Fatal(err) } w.Header().Set("Content-Type", "application/json") w.Write(res) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
確認するとjson形式。
これをNuxtで受け取れるようにする。
$ yarn add @nuxtjs/axios
nuxt.config.jsのmodulesにaxiosを追加。
modules: [ '@nuxtjs/axios' ],
index.vueをいじる。modeにuniversalを指定しているからSSRでやる。async asyncData内でaxiosを使ってapiを呼ぶかな。画面上には取ってきたnameを表示させる。
<template> <div class="container"> <div> <logo /> <h1 class="title"> {{ user.name }} </h1> <h2 class="subtitle"> My impeccable Nuxt.js project </h2> <div class="links"> <a href="https://nuxtjs.org/" target="_blank" class="button--green" > Documentation </a> <a href="https://github.com/nuxt/nuxt.js" target="_blank" class="button--grey" > GitHub </a> </div> </div> </div> </template> <script lang="ts"> import Vue from 'vue' import Logo from '~/components/Logo.vue' export default Vue.extend({ components: { Logo }, async asyncData({ app }) { try { const res = await app.$axios.$get("http://go-app:8080") console.info(res) return { user: res } } catch(error) { // エラー処理 } } }) </script> <style> .container { margin: 0 auto; min-height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; } .title { font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; display: block; font-weight: 300; font-size: 100px; color: #35495e; letter-spacing: 1px; } .subtitle { font-weight: 300; font-size: 42px; color: #526488; word-spacing: 5px; padding-bottom: 15px; } .links { padding-top: 15px; } </style>
U表示されてるね。TypeScriptのビルドには失敗するだろうからちゃんとやんないといけないけど、とりあえずの表示はおk。
コード云々は置いておいて、大まかな流れはまあこんな感じできっとできる。