【问题标题】:Dockerfile : no such file or directoryDockerfile:没有这样的文件或目录
【发布时间】:2021-06-23 13:59:04
【问题描述】:

我做了一个 Dockerfile 来将我的开发投入生产。 但是,当我运行 docker 时,出现以下错误:

http: panic serving xxx.xxx.xxx.xxx:xxxxx: open views/public/index.html: 没有这样的文件或目录

我的错误在我的 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 

【问题讨论】:

  • 您只是将二进制文件复制到图像中,而不是 HTML 文件。
  • 是的,但是如果我复制视图和静态文件,我也会遇到同样的问题

标签: docker go dockerfile


【解决方案1】:

您需要将 static 和 views 目录添加到最终图像中,为此在 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

【讨论】:

    【解决方案2】:

    正如其他人所指出的,HTML 文件不会被复制到最终图像中。您可以只添加此复制命令并完成,但您仍在管理可执行文件和可能的许多其他文件,甚至是多个文件的多个目录!有更好的办法。

    在构建 Go 可执行文件时,仅包含实际的 Go 代码本身及其依赖项。 Go 二进制文件中不包含其他静态文件,例如 HTML。但是,从 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.ParseFiles,并在其中传递 embed.FS 对象作为额外的第一个参数。请不要在每次 API 调用时直接从操作系统读取所有模板文件。

    【讨论】:

    • 感谢您的回复! htmlFS 采用 CSS/JS 和 HTML 中的其他内容来制作页面?就我而言,我使用 go 模板来呈现页面。
    • 是的!确切地。您甚至可以将所有这些静态文件放在它们自己的 html/css/js 文件夹中,然后将其放在一个父目录中,然后嵌入整个文件。您可以将所有这些作为静态文件提供,而无需一次又一次地阅读它们。我也嵌入了 SQL 脚本和其他东西!如果你喜欢我的回答,欢迎采纳!
    • 您可以将 embed.FS 直接传递给 http.FileServer (golang.org/pkg/net/http/#FileServer)
    • 就我而言,我看不到如何使用执行模板(示例)实现
    • 您必须熟悉这些文档。看看你在哪里使用 template.ParseFiles(...)?而是使用 template.ParseFS(embed.FS, ...)。此处的文档:golang.org/pkg/html/template/#ParseFS。您现在在代码中执行此操作并将文件复制到最终 Docker 映像的方式意味着您每次调用该路由时都从操作系统读取这些文件。
    猜你喜欢
    • 1970-01-01
    • 2018-03-01
    • 2020-11-07
    • 1970-01-01
    • 2022-08-19
    • 2016-01-04
    • 2017-02-14
    • 2017-03-04
    • 2019-03-17
    相关资源
    最近更新 更多