【问题标题】:React client and Golang server in same Dockerfile在同一个 Dockerfile 中反应客户端和 Golang 服务器
【发布时间】:2021-06-24 16:20:15
【问题描述】:

我已经构建了一个使用 Golang 编写的 API 支持的 React 客户端应用程序。我想使用 Docker 来使用 docker run 来运行这两个应用程序。

我的项目结构如下:

zid
    |
    |-web/ (my react folder)
    main.go
    Dockerfile
    |

我的目标是运行zid 文件夹中的main.go 文件并启动zid/web 文件夹中的Web 应用程序。 main.go 文件使用 Gin Gonic 启动一个 API,它将在端口 10000 上侦听和服务。

所以我尝试了以下方法:

# Build the Go API
FROM golang:latest as go_builder
RUN mkdir /zid
WORKDIR /zid
COPY . /zid
RUN GOOS=linux GOARCH=amd64 go build -a -ldflags "-linkmode external -extldflags '-static' -s -w" -o /go/bin/zid

# Build the React application
FROM node:alpine as node_builder
COPY --from=go_builder /zid/web ./
RUN npm install
RUN npm run build

# Final stage build, this will be the container with Go and React
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=go_builder /go/bin/zid /go/zid
COPY --from=go_builder /zid/ca /go/ca
COPY --from=node_builder /build ./web
EXPOSE 3000
WORKDIR /go
CMD ./zid

接下来我做了以下事情:

  1. 使用docker build -t zid . 构建它(没有错误)
  2. 使用docker run -p 3000:3000 --rm zid 运行它

当我运行它时,它将启动 API,但是当我转到 http://localhost:3000/ 时,我得到一个 Page does not work ERR: ERR_EMPTY_RESPONSE。

所以 API 启动了,但 npm 构建没有。我不确定自己做错了什么,因为 Docker 容器都包含正确的文件夹(go 和 web)。

正如您在图片中看到的那样,我相信它就在那里。我错过了什么?

编辑:

我正在使用(*gin.Engine).Run() 函数在端口 10000 上设置监听和服务。在我的本地构建中,我的 React 应用程序正在向localhost:10000 发送请求。我总是在我的 React 应用程序 (localhost:3000) 一侧简单地使用 npm start。我的目标是做同样的事情,但所有这些都在一个 Dockerfile 中。

我仍然有点不确定是否应该在我的 Dockerfile 中 EXPOSE 端口 10000 和 3000。

我的 HandleRequest 函数:

//Start the router and listen/serve.
func HandleRequests() {
    router := SetupRouter()
    router.Run(":10000")
}

我的 SetupRouter 功能:

//Setup the gin router
func SetupRouter() *gin.Engine {
    router := gin.Default()
    router.Use(CORSMiddleware())

    router.POST("/auth/login", login)
    router.POST("/component/deploy", deployComponent)
    router.POST("/project/create", createProject)
    router.POST("/diagram/create", createDiagram)
    router.PATCH("/diagram/update", updateDiagram)
    router.DELETE("/diagram/delete/:id", deleteDiagram)
    router.GET("/diagram/:id", getDiagram)
    router.GET("/project/list", getProjectsByUsername)
    router.GET("/project/:id", getProject)
    router.GET("/project/diagrams/:id", getDiagramsOfProject)
    router.DELETE("/project/delete/:id", deleteProject)
    router.GET("/application/list", applicationList)
    router.GET("/instance/status/:id", getInstanceStatus)
    router.GET("/user", getUser)

    return router
}

顺便说一句,我只想将 Docker 容器用于开发和学习目的。

【问题讨论】:

  • 通常你只希望在每个 docker 容器中运行 1 个东西。如果这两件事需要相互通信,您可以使用两个容器的公共网络来实现。或者更简单,使用 docker-compose 将两个容器一起运行。
  • 运行一个同时提供静态文件(React 应用程序)和 REST API 调用的服务器没有任何问题,imo。
  • @super 由于 React 应用程序被编译成静态文件,它不需要自己的服务器或自己的容器;问题中描述的高级方法是有道理的。
  • 在 Go 应用程序中,你能显示设置网络监听器的代码吗? (是否使用端口 3000;是否监听所有接口?)
  • @DavidMaze 编辑了我的问题!

标签: reactjs docker go


【解决方案1】:

我使用以下multi-stage Docker build 创建:

  • 静态 VueJS UI HTML 资源
  • 编译的 Go API http 服务器(服务于上述 HTML 资产)

注意:Go 和 VueJS 的源代码都是从一个 git 存储库下载的 - 但您可以轻松修改它以从本地开发目录复制两个代码库。


#
# go build
#
FROM golang:1.16.5 AS go-build

#
# here we pull pkg source directly from git (and all it's dependencies)
#
RUN     go get  github.com/me/vue-go/rest
WORKDIR /go/src/github.com/me/vue-go/rest
RUN     CGO_ENABLED=0 go build

#
# node build
#
FROM node:13.12.0 AS node-build

WORKDIR /app/vue-go

COPY --from=go-build go/src/github.com/me/vue-go/vue-go ./

# produces static html 'dist' here:
#
#       /app/vue-go/dist
#
RUN npm i && npm run build

#
# final layer: include just go-binary and static html 'dist' 
#
FROM scratch

COPY --from=go-build \
    /go/src/github.com/me/vue-go/rest/rest \
    /app/vue-go

COPY --from=node-build \
    app/vue-go/dist \
    /app/dist/

CMD ["/app/vue-go"]

我不使用 Gin - 但要使用原生 net/http 文件服务器服务 API 和静态 HTML 资产,请使用类似:

h := http.NewServeMux()

// serve static HTML directory:
if conf.StaticDir != "" {
    log.Printf("serving on '/' static files from %q", conf.StaticDir)
    h.Handle(
        "/",
        http.StripPrefix(
            "/",
            http.FileServer(
                http.Dir(conf.StaticDir), // e.g. "../vue-go/dist"  vue.js's html/css/js build directory
            ),
        ),
    )
}


// handle API route(s)
h.Handle("/users",
    authHandler(
        http.HandlerFunc(handleUsers),
    ),
)

并启动服务:

s := &http.Server{
    Addr:    ":3000", // external-facing IP/port
    Handler: h,
}

log.Fatal(s.ListenAndServe())

然后构建并运行:

docker build -t zid .
docker run -p 3000:3000  --rm zid

【讨论】:

  • 啊,是的,使用多阶段 Docker 构建的有趣解决方案。但是我已经通过运行 .sh 脚本找到了在一个容器中使用多个服务的解决方案。
【解决方案2】:

我找到了解决方案!我在multi-service container 的基础上创建了一个脚本,然后在我的 Dockerfile 中运行这个脚本。

我的脚本(start.sh):

#!/bin/sh

# Start the first process
./zid &
ZID_PID=$!

# Start the second process
cd /web 
npm start &
WEB_PID=$!

# Naive check runs checks once a minute to see if either of the processes exited.
# This illustrates part of the heavy lifting you need to do if you want to run
# more than one service in a container. The container exits with an error
# if it detects that either of the processes has exited.
# Otherwise it loops forever, waking up every 60 seconds

while sleep 60; do
  ps -fp $ZID_PID 
  ZID_PROCESS_STATUS=$?
  if [ $ZID_PROCESS_STATUS -ne 0 ]; then
    echo "ZID process has already exited."
    exit 1
  fi
  
  ps -fp $WEB_PID 
  WEB_PROCESS_STATUS=$?
  if [ $WEB_PROCESS_STATUS -ne 0 ]; then
    echo "WEB process has already exited."
    exit 1
  fi
done

在这里,我首先启动我的 go 可执行文件,然后执行 npm start

在我的 Dockerfile 中,我执行以下操作:

# Build the Go API
FROM golang:latest as go_builder
RUN mkdir /zid
WORKDIR /zid
COPY . /zid
RUN GOOS=linux GOARCH=amd64 go build -a -ldflags "-linkmode external -extldflags '-static' -s -w" -o /go/bin/zid

# Build the React application
FROM node:alpine as node_builder
COPY --from=go_builder /zid/web ./web
WORKDIR /web
RUN npm install

# Final stage build, this will be the container with Go and React
FROM node:alpine
RUN apk --no-cache add ca-certificates procps 
COPY --from=go_builder /go/bin/zid /go/zid
COPY --from=go_builder /zid/static /go/static 
COPY --from=go_builder /zid/ca /go/ca
COPY --from=node_builder /web /web
COPY --from=go_builder /zid/start.sh /go/start.sh
RUN chmod +x /go/start.sh
EXPOSE 3000 10000
WORKDIR /go
CMD ./start.sh

在这里,我正在创建一个 Go 可执行文件、复制和 npm install 我的 /web 文件夹,并在最后阶段构建我启动我的 ./start.sh 脚​​本。

这将启动我的 Golang 应用程序和 React 开发服务器。我希望它对其他人有所帮助。

【讨论】:

  • 很好奇您的更大目标是什么。 npm start 是快速本地开发的理想选择 - 因为它包括在源文件更改时进行实时重新编译。在 Docker 上下文中,源文件是静态的——因此不需要持续重新编译。除非您打算使用 docker -v 卷标志将本地目录挂载到您的 docker 容器中?
  • 啊,是的,我的目标是通过 Docker 在本地运行我的 Golang 服务器和 React 应用程序。我同意我不需要实时重新编译,但我不确定如何使用静态文件以另一种方式启动我的 React 应用程序。我已经尝试过npm run build,但我不知道如何在 Docker 中启动该构建。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-29
  • 1970-01-01
  • 2015-05-09
  • 2017-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多