我做了一个 Dockerfile 来将我的开发投入生产。 但是,当我运行 docker 时出现以下错误:
<块引用>http: panic services xxx.xxx.xxx.xxx:xxxxx: open views/public/index.html: no such file or directory
我的错误在我的 Dockerfile 中,但我不知道在哪里..
我的 Dockerfile:
FROM golang:alpine as builder
RUN apk update && apk add --no-cache git ca-certificates gcc g++ make && update-ca-certificates
RUN adduser -D -g '' appuser
WORKDIR /usr/src/app
COPY . .
RUN go mod download
RUN go mod verify
WORKDIR /usr/src/app/cmd/web
RUN make docker
FROM scratch
WORKDIR /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /usr/src/app/cmd/web/web /web
USER appuser
ENTRYPOINT ["./web"]
CMD [":9000"]
我的程序中路由“/”的示例:
func (s *server) handleIndex() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
auth, _ := strconv.ParseBool(s.GetSessionValue(w, r, "authenticated"))
files := []string{
"views/public/index.html",
"views/public/nav.html",
}
templates := template.Must(template.ParseFiles(files...))
if err := templates.ExecuteTemplate(w, "index", authData{Authenticated: auth, NotAuthenticated: !auth}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
文件树:
ants-map/
┣ auth/
┣ cmd/
┃ ┗ web/
┃ ┣ static/
┃ ┣ views/ .html file here
┃ ┣ Makefile
┃ ┣ main.go
┃ ┣ routes.go
┃ ┣ routes_public.go
┃ ┗ server.go
┣ database/
┣ .gitignore
┣ Dockerfile
┣ README.md
┣ go.mod
┗ go.sum
答案 0 :(得分:1)
您需要将静态和视图目录添加到最终图像中,为此在 USER appuser
之前添加以下 2 行:
COPY --from=builder /usr/src/app/cmd/web/views /web/views
COPY --from-builder /usr/src/app/cmd/web/static /web/static
答案 1 :(得分:1)
正如其他人指出的那样,HTML 文件不会被复制到最终图像中。您可以添加此复制命令并完成,但是您仍然在管理可执行文件和潜在的许多其他文件,甚至是多个文件的多个目录!有更好的方法。
在构建 Go 可执行文件时,仅包含实际的 Go 代码本身及其依赖项。其他静态文件(如 HTML)不包含在 Go 二进制文件中。但是,从 Go 1.16 开始,现在有一个解决方案,即 Go Embed。
处理静态文件的现代方法是将它们嵌入到您的 Go 程序中。例如,如果您有一个 Go 文件与另一个 HTML 文件目录位于同一目录中,那么您可以像这样嵌入整个 HTML 目录:
//go:embed html/*
var htmlFS embed.FS
然后,当您确实需要使用文件时,您可以按名称查找:
htmlBytes, err := htmlFS.ReadFile("html/index.html")
这将以字节的形式为您提供该文件的内容。然后,您可以在响应中将此文件作为字节发送。
嵌入不仅为您提供真正的单文件程序体验,而且由于文件的内容在内存中,而不是每次发送文件时都必须将文件读入内存,因此它的性能也更高。
更新:在上面的 OP 示例中,对于文件系统,您可以使用 template.ParseFS 并将 template.ParseFiles
对象作为额外的第一个参数传递,而不是 embed.FS
。请不要在每次 API 调用时直接从操作系统读取所有模板文件。