将React客户端和Golang服务器放在同一个Dockerfile中。

huangapple go评论85阅读模式
英文:

React client and Golang server in same Dockerfile

问题

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

我有以下项目结构:

zid
    |
    |-web/ (我的React文件夹)
    main.go
    Dockerfile
    |

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

所以我尝试了以下步骤:

# 构建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

# 构建React应用程序
FROM node:alpine as node_builder
COPY --from=go_builder /zid/web ./
RUN npm install
RUN npm run build

# 最终构建阶段,这将是包含Go和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/时,我得到一个页面不可用的错误:ERR_EMPTY_RESPONSE。

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

如图所示,我相信一切都在那里。我漏掉了什么?

编辑:

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

我还不确定是否应该在Dockerfile中EXPOSE端口10000和3000。

我的HandleRequest函数:

// 启动路由器并监听/提供服务。
func HandleRequests() {
	router := SetupRouter()
	router.Run(":10000")
}

我的SetupRouter函数:

// 设置gin路由器
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容器用于开发和学习目的。

英文:

I've build a React client application supported with a API written in Golang. I would like to use Docker to run these both apps using docker run.

I have the following project structure:

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

My goal is to run the main.go file in the zid folder and start the webapplication in the zid/web folder. The main.go file starts a API using Gin Gonic that will listen and serve on port 10000.

So I've tried the following:

# 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

Next I did the following:

  1. Build it with docker build -t zid . (no errors)
  2. Run it with docker run -p 3000:3000 --rm zid

When I run this, it will startup the API, but when I go to http://localhost:3000/ then I get a Page does not work ERR: ERR_EMPTY_RESPONSE.

So the API starts up, but the npm build doens't. I am not sure what I am doing wrong, because the Docker container both contains the correct folders (go and web).

将React客户端和Golang服务器放在同一个Dockerfile中。

As you can see in the image it's all there I believe. What am I missing?

EDIT:

I am using the (*gin.Engine).Run() function to set the listen and serve on port 10000. In my local build my React application is sending request to localhost:10000. I always simply used npm start on the side of my React app (localhost:3000). My goal is to do the same but then all in one Dockerfile.

I am still a little unsure if I should EXPOSE ports 10000 & 3000 in my Dockerfile.

My HandleRequest function:

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

My SetupRouter function:

//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
}

Btw I just want to use the Docker container for Development and learning purpose only.

答案1

得分: 1

我已经使用以下多阶段Docker构建来创建:

  • 静态的VueJS UI HTML资源
  • 编译的Go API HTTP服务器(用于提供上述HTML资源)

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


#
# go build
#
FROM golang:1.16.5 AS go-build
#
# 这里我们直接从git拉取pkg源代码(以及它的所有依赖项)
#
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 ./
# 在这里生成静态的html 'dist':
#
#       /app/vue-go/dist
#
RUN npm i && npm run build
#
# 最终层:只包含go二进制文件和静态的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()
// 服务静态HTML目录:
if conf.StaticDir != "" {
log.Printf("从 %q 服务静态文件在 '/':", conf.StaticDir)
h.Handle(
"/",
http.StripPrefix(
"/",
http.FileServer(
http.Dir(conf.StaticDir), // 例如 "../vue-go/dist",vue.js的html/css/js构建目录
),
),
)
}
// 处理API路由
h.Handle("/users",
authHandler(
http.HandlerFunc(handleUsers),
),
)

然后启动服务:

s := &http.Server{
Addr:    ":3000", // 外部IP/端口
Handler: h,
}
log.Fatal(s.ListenAndServe())

然后进行构建和运行:

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

I've used the following multi-stage Docker build to create:

  • static VueJS UI HTML assets
  • compiled Go API http server (serving the above HTML assets)

Note: both Go and VueJS source is download from one git repo - but you could just as easily modify this to copy the two code-bases from local development directories.


#
# 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"]

I don't use Gin - but to use native net/http fileserver serving APIs and static HTML assets, use something like:

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),
),
)

and start the service:

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

then to build & run:

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

答案2

得分: 1

我找到了一个解决方案!我根据multi-service container创建了一个脚本,并在我的Dockerfile中运行了这个脚本。

我的脚本(start.sh):

#!/bin/sh

# 启动第一个进程
./zid &
ZID_PID=$!

# 启动第二个进程
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中,我执行以下操作:

# 构建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

# 构建React应用程序
FROM node:alpine as node_builder
COPY --from=go_builder /zid/web ./web
WORKDIR /web
RUN npm install

# 最终构建阶段,这将是包含Go和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开发服务器。希望对其他人有所帮助。

英文:

I've found a solution! I've created a script on basis of multi-service container and then a run this script in my Dockerfile.

my script (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

Here I first start my go executable and then I do a npm start

In my Dockerfile I do the following:

# 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

Here I am creating a Go executable, copy and npm install my /web folder and in de final stage build I start my ./start.sh script.

This will start my Golang application and the React development server. I hope it helps for others.

huangapple
  • 本文由 发表于 2021年6月22日 21:09:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/68084178.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定