使用Docker Compose搭建Golang✖️Prisma✖️MySQL开发环境!

首先

在进行个人API开发的过程中,我发现自己在懒散地学习Golang的同时,还在寻找一个不错的数据库迁移工具。在进行调查的时候,我发现了一个名为Prisma的工具。据说它可以利用GraphQL进行数据库迁移,并可以作为某些语言中类型安全的ORM工具使用。

在这里,我们将进行开发环境的搭建,详细说明将留给其他文章。然而,由于不想破坏本地环境,所以尽量使用虚拟环境来进行。

容器配置

在Prisma官方网站上有架构的展示。

Prismaレイヤーアーキテクチャ

客户端与API服务器进行交互,API服务器与数据库之间通过Prisma进行连接。如果要在本地环境中复现此过程,我认为会呈现以下图示的形式。

ローカル.png

使用Docker Compose可以启动API服务器、Prisma服务器和数据库的三个实例,并且只能通过浏览器访问API服务器。
这意味着无法直接从浏览器访问Prisma服务器和数据库。

在本次的开发环境中,我们会更加灵活地建立,以便更方便进行工作。具体来说,就像下图所示的那样。

開発環境.png

最初的图表显示,Prisma Cli的数量正在增加。此外,现在可以访问Prisma服务器。
具体细节将在后文提及,Prisma服务器附带了一个可以从浏览器中进行数据库操作的工具。
在生产环境中,当然不能使用这个工具,但在开发环境中它非常方便,所以我们会打开端口以便可以使用它。

目录结构

最后的目录结构看起来是这样的。

親ディレクトリ
├── api-server
│   └── dockerfile
├── app
│   ├── Gopkg.lock
│   ├── Gopkg.toml
│   ├── datamodel.prisma
│   ├── generated
│   │   └── prisma-client
│   │       └── prisma.go
│   ├── main.go
│   ├── prisma.yml
│   └── vendor
├── docker-compose.yml
└── prisma-cli
    └── dockerfile

一般情况下,Golang目录结构与此大为不同,但这样做是为了方便使用Docker Compose进行操作和指定。在常规项目中,建议遵循Golang的规范和思想,采用正确的目录结构。

API服务器的配置

这次没有特别要注意的事项。
因为这次打算使用Golang来搭建API服务器,所以让我们先安装dep以便进行依赖管理。

FROM golang:1.13-alpine3.10

WORKDIR /go/src/app

RUN apk update \
  && apk add git \
  && go get -u github.com/golang/dep/cmd/dep

Prisma命令行工具的设置

Prisma 官方提供了 Cli 工具。
虽然也有在 Homebrew 上发布的版本,但这次我们将使用 npm 库。

FROM node:13.1-alpine3.10

WORKDIR /usr/src/app

RUN npm install -g yarn \
  && yarn global add prisma

Docker Compose的设置

关于Prisma服务器和MySQL,在官方网页上有示例供参考。我们可以参照这些示例来编写。

version: "3"

services:
  api-server:
    build: ./api-server
    container_name: api-server
    tty: true
    ports:
      - "8080:8080"
    volumes:
      - ./app:/go/src/app

  prisma-cli:
    build: ./prisma-cli
    container_name: prisma-cli
    tty: true
    volumes:
      - ./app:/usr/src/app

  prisma-server:
    image: prismagraphql/prisma:1.34
    container_name: prisma-server
    restart: always
    ports:
      - "4466:4466"
    environment:
      PRISMA_CONFIG: |
        port: 4466
        databases:
          default:
            connector: mysql
            host: database
            port: 3306
            user: root
            password: prisma

  database:
    image: mysql:5.7
    container_name: database
    environment:
      MYSQL_ROOT_PASSWORD: prisma
    volumes:
      - mysql:/var/lib/mysql

volumes:
  mysql: ~

有一些需要注意的地方。

    • API サーバーと Prisma Cli にtty: trueをつける

API サーバーと Prisma Cli は実行し続けるコマンドを持たないため、起動完了後に停止してしまう
停止したコンテナにはアクセスできないので、停止せずにアクセスできる状態を保つための設定

Prisma サーバーにrestart: alwaysをつける

Prisma サーバーは起動すると DB への接続を試みる
MySQL は起動に時間がかかるため、初回の接続は失敗する
接続失敗するとコンテナが落ちてしまうので、接続できるまで再起動し続けるための設定

一旦設定完畢後,請轉至根目錄並啟動。

$ docker-compose up -d

我们可以通过使用$ docker-compose logs命令来查看启动处理的日志。如果您能从日志中确认Prisma服务器已经启动,请尝试在浏览器中访问localhost:4466。如果可以访问,那就表示一切正常。

对数据库的操作

让我们在构建好的环境中操作数据库。
首先进入Prisma Cli。由于采用了Alpine发行版,所以我们将使用ash,而不是bash。

$ docker-compose exec prisma-cli ash

然后进行项目和数据库的初始化。

$ prisma init --endpoint http://prisma-server:4466
$ prisma deploy

在这里,我们指定了http://prisma-server:4466作为终端点。
prisma-server是在docker-compose.yml中指定的Prisma服务器的服务名称。在Docker Compose中,您可以使用服务名称在服务之间进行通信。换句话说,我们在这里指定了”Prisma服务器的4466端口”作为终端点。
prisma deploy将根据datamodel.prisma执行数据库迁移。这个写法似乎遵循GraphQL的规范,所以请参考GraphQL的相关文档。

当初始化完成后,让我们检查数据结构。请通过浏览器访问localhost:4466/_admin,以确认按照datamodel.prisma所定义的数据结构进行创建。如果能够成功创建,就表示一切正常。

スクリーンショット 2019-11-24 13.51.06.png

通过这个页面可以进行数据库操作。好像叫Prisma Admin。
试着注册一些数据吧。点击右中间附近的加号,输入值到屏幕右侧的选项卡中。最后点击屏幕右下方的保存按钮即可完成。

让我们最后为 Golang 生成 Prisma Client,然后就结束吧。在 prisma.yml 文件中添加生成 Golang 用 Prisma Client 的配置,并执行生成命令。

endpoint: http://prisma-server:4466
datamodel: datamodel.prisma

generate:
  - generator: go-client
    output: ./generated/prisma-client/
$ prisma generate

如果一切顺利,文件应该在您指定的输出目录生成。

API服务器

那么,让我们使用生成的Prisma Client来创建API服务器。
您可以像之前一样使用Shell登录API服务器,但我建议您使用VSCode插件Remote-Containers。
只要可以使用Shell即可。
首先,让我们尝试使用刚刚生成的Prisma Client获取数据。我们将参考官方网站的这一部分和这一部分。

$ cd /go/src/app
$ dep init
$ touch main.go
package main

import (
    "app/generated/prisma-client"
    "context"
    "fmt"
)

func main() {
    client := prisma.New(nil)
    ctx := context.TODO()

    users, err := client.Users(nil).Exec(ctx)
    if err != nil {
        panic(err)
    }

    fmt.Println(users)
}

没问题,由于正在学习Golang,所以请体谅质量。
暂时先试着运行一下吧。

$ go run main.go
[{ck3ez5b7e00110766n4zr6wro Alice} {ck3ez5b8u001507666qcbs2qy Bob} {ck3ez5b9b001907668hf3pgyw Charles}]

我可以检索到已在数据库中注册的“Alice”、“Bob”和“Charles”的信息,ck3e~是自动生成的ID字符串。
剩下的就是以Json格式对该HTTP请求进行响应了。让我们根据这篇文章和这篇文章继续进行。

package main

import (
    "app/generated/prisma-client"
    "context"
    "log"
    "net/http"
    "reflect"

    "github.com/ant0ine/go-json-rest/rest"
)

var ctx = context.TODO()
var client = prisma.New(nil)

func main() {
    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)

    router, err := rest.MakeRouter(
        rest.Get("/users", GetUsers),
    )
    if err != nil {
        log.Fatal(err)
        panic("Failed to setup router.")
    }

    api.SetApp(router)
    log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

func GetUsers(w rest.ResponseWriter, r *rest.Request) {
    users, err := client.Users(nil).Exec(ctx)
    if err != nil {
        handleError(w, err)
        return
    }

    usersMap := usersToMap(users)
    w.WriteJson(usersMap)
}

func handleError(w rest.ResponseWriter, err error) {
    log.Fatal(err)

    result := map[string]string{"error": "server error"}

    w.WriteJson(result)
    w.WriteHeader(http.StatusInternalServerError)
}

func usersToMap(users []prisma.User) map[string]map[string]string {
    result := make(map[string]map[string]string)

    for _, user := range users {
        tag := reflect.TypeOf(user).Field(1).Tag.Get("json")
        result[user.ID] = map[string]string{tag: user.Name}
    }

    return result
}

出来上がったら、我们执行并尝试访问。访问的目标是localhost:8080/users。

$ go run ./main.go
{
  "ck3ez5b7e00110766n4zr6wro": {
    "name": "Alice"
  },
  "ck3ez5b8u001507666qcbs2qy": {
    "name": "Bob"
  },
  "ck3ez5b9b001907668hf3pgyw": {
    "name": "Charles"
  }
}

如果可以获取到这样的Json响应,就可以了!

最后

所以,我们可以在虚拟环境中构建以Prisma服务器为核心的开发环境。如果进一步发展,很快就可以实现一个简单的API服务器。但是,我并没有详细介绍GraphQL的细节和Prisma Client的使用方法,为了正常运行,我还需要更多的学习。需要记住的东西会不断增加。

这次我们使用了原始版的Prisma,但目前正在开发计划中的Prisma2即将发布。
由于还不支持Golang(截至2019/11/30),所以我们这次没有使用,但希望尽快能够在Golang中使用。

请参考相关资料。

请参照相关资料。

普瑞斯瑪(Prisma)相關

Prisma – 最快的GraphQL服务器实现
Prisma
Prisma文档
Prisma官方服务器镜像
Prisma2

Docker 相關

终于应该停止使用 docker-compose 中的 links,并使用网络来解决容器之间的名称解析问题,这是 Dockerfile 的最佳实践,以及 Compose 文件的参考资料。

Go相关

据说Go有一个标准的目录结构。
在VSCode中配置Go的模块设置。
尝试使用golang编写REST API ①。
Go-Json-Rest。
标准的Go项目布局。
向Go的结构体添加标签的基本元信息。
Golang。
dep(Golang的依赖管理工具)。

其他

远程容器
图战舰
绘图工具.io

广告
将在 10 秒后关闭
bannerAds