如何在docker中部署带有静态文件的web应用程序?



我用echo构建了一个web应用程序。server.go中的某些源

package main
import ...
type TemplateRenderer struct {
templates *template.Template
}
func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
if viewContext, isMap := data.(map[string]interface{}); isMap {
viewContext["reverse"] = c.Echo().Reverse
}
return t.templates.ExecuteTemplate(w, name, data)
}
func main() {
e := echo.New()
e.Static("/static", "static")
renderer := &TemplateRenderer{
templates: template.Must(template.ParseGlob("public/views/*.html")),
}
e.Renderer = renderer
e.GET("/", func(c echo.Context) error {
return c.Render(http.StatusOK, "index.html", map[string]interface{}{})
})
e.Logger.Fatal(e.Start(":8080"))
}

项目树

├── helper
│   └── string.go
└─── site
├── Dockerfile
├── go.mod
├── go.sum
├── server.go
├── public
│   └── views
│       └── index.html
└── static

我可以运行go run server.go启动服务器,一切工作正常。但是如果用docker运行,会出现错误。

Dockerfile

FROM golang:1.15.2-alpine3.12 AS builder
WORKDIR /app
COPY . .
WORKDIR /app/site
RUN CGO_ENABLED=0 GOOS=linux go build -o server
FROM alpine:3.12
COPY --from=builder /app/site /bin/.
ENTRYPOINT [ "server" ]

通过

创建docker镜像
docker build -t gcr.io/${PROJECT_ID}/myapp -f site/Dockerfile .

在docker中运行app

docker run --rm -p 8080:8080 gcr.io/${PROJECT_ID}/myapp
panic: html/template: pattern matches no files: `public/views/*.html`
goroutine 1 [running]:
html/template.Must(...)
/usr/local/go/src/html/template/template.go:372
main.main()
/app/site/server.go:76 +0x2af

似乎public文件夹没有被复制到图像中。但在构建图像时没有任何错误。怎么了?

首先,您正在使用多阶段Docker构建,这对于仅将编译后的二进制文件复制到最终映像是完美的。但是,在Dockerfile中,您正在复制整个构建目录-二进制server以及所有源代码。

其次,您的主要问题是,当映像作为容器运行时,默认工作目录是/-因此服务器中的任何路径都无法在/bin/public中找到html文件。

如果你需要调试一个docker镜像,特别是如果它是基于linux发行版的镜像,如alpine,只需:

docker run -it myimage /bin/sh

无论如何,这两个简单的修复你的docker:

FROM golang:1.15.2-alpine3.12 AS builder
WORKDIR /app
COPY . .
WORKDIR /app/site
RUN CGO_ENABLED=0 GOOS=linux go build -o server
FROM alpine:3.12
COPY --from=builder /app/site/server /bin
COPY --from=builder /app/site/public /public
ENTRYPOINT [ "/bin/server" ]

在Go 1.16中,您可以将这些文件编译成二进制文件本身。因此,您需要升级主机系统上的Go工具链,以及Dockerfile中构建阶段的FROM行。Go 1.16增加了embed包和一个新的//go:embed指令来支持这个。

首先,你需要告诉编译器嵌入模板文件,构建一个文件系统对象:

import "embed"
// templateFiles contains the raw text of the template files.
//go:embed public/views/*.html
var templateFiles embed.FS

那么,当你去使用它的时候,go 1.16也增加了相应的("html/template").ParseFS函数:

renderer := &TemplateRenderer{
templates: template.Must(template.ParseFS(templateFiles)),
}

现在所有的文件都嵌入到二进制文件中,你不应该得到"file not found"类型错误。您可以考虑只将编译后的二进制文件复制到最终映像中,而不复制其他任何内容。

# Upgrade to Go 1.16
FROM golang:1.16-alpine3.12 AS builder
# Unchanged from original
WORKDIR /app
COPY . .
WORKDIR /app/site
RUN CGO_ENABLED=0 GOOS=linux go build -o server
FROM alpine:3.12
# Only copy the compiled binary and not the source tree
COPY --from=builder /app/site/server /bin
# Generally prefer CMD to ENTRYPOINT
CMD [ "server" ]

最新更新