简体   繁体   English

当我 dockerize 我的 Go 应用程序时,它没有看到 HTML 模板

[英]When I dockerize my Go app, it's not seeing HTML templates

I'm trying to write notification service with gomail.我正在尝试使用 gomail 编写通知服务。 When I run my app locally everything works just fine but when I dockerize my app I get nil pointer error because gomail cannot find html templates.当我在本地运行我的应用程序时,一切正常,但是当我对我的应用程序进行 dockerize 时,我收到 nil 指针错误,因为 gomail 找不到 html 模板。

Error:错误:

app_1  | panic: runtime error: invalid memory address or nil pointer dereference
app_1  | [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x41dc7c]

Here is my dockerfile:这是我的 dockerfile:

FROM golang@.. as builder

RUN apk update && apk add --no-cache git ca-certificates tzdata && update-ca-certificates

RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/noneexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    "${USER}"

WORKDIR /app

COPY go.mod .
COPY go.sum .

RUN go mod download
RUN go mod verify

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

FROM scratch

COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group
COPY --from=builder /app/main .


EXPOSE 8000

ENTRYPOINT ["./main"]

My folder structure:我的文件夹结构:

notification-service
├── Makefile
├── README.md
├── common
│   ├── constants.go
│   ├── env.go
│   └── logger.go
├── docker-compose.yml
├── dockerfile
├── entities
│   └── invite_struct.go
├── go.mod
├── go.sum
├── main.go
├── router.go
├── services
│   └── mail.go
├── templates
│   ├── info.html
│   └── invite.html
├── kafka
│   ├── consumer.go
│   └── producer.go

Code:代码:

kafka/consumer.go卡夫卡/consumer.go

if ok := mailService.Send("templates/info.html", inviteMessage.Email, "Account Created", content); !ok {
                continue
            }

services/mail.go服务/mail.go

type MailServiceInterface interface {
    Send(template string, to string, subject string, data interface{}) bool
}

type Mail struct {
}

func NewMailServiceClient() MailServiceInterface {
    return &Mail{}
}

func (m *Mail) Send(template string, to string, subject string, data interface{}) bool {
    t, _ := m.parseTemplate(template, data)
    if ok := m.sendMail(*t, to, subject); ok {
        log.Println(fmt.Sprintf("Email has been sent to %s", to))
        return true
    } else {
        log.Println(fmt.Sprintf("Failed to send the email to %s", to))
        return false
    }
}

func (m *Mail) parseTemplate(templateFileName string, data interface{}) (*string, error) {
    t, err := template.ParseFiles(templateFileName)
    if err != nil {
        return nil, err
    }
    buf := new(bytes.Buffer)
    if err = t.Execute(buf, data); err != nil {
        return nil, err
    }
    body := buf.String()
    return &body, nil
}

func (m *Mail) sendMail(template, to, subject string) bool {
    env := common.GetEnvironment()
    from := env.MailUsername
    password := env.MailPassword
    port, _ := strconv.Atoi(env.MailPort)
    mailServer := env.MailServer

    mail := gomail.NewMessage()
    mail.SetHeader("From", from)
    mail.SetHeader("To", to)
    mail.SetHeader("Subject", subject)
    mail.SetBody("text/html", template)

    d := gomail.NewDialer(mailServer, port, from, password)

    if err := d.DialAndSend(mail); err != nil {
        zap.S().Error(err)
        return false
    }
    return true
}

At the end of your dockerfile you run COPY --from=builder /app/main .在 dockerfile 的末尾运行COPY --from=builder /app/main . --from COPY --from=builder /app/main . so you're copying only the executable.所以你只复制可执行文件。

However template.ParseFiles(templateFileName) implementation uses os.ReadFile(file) , which means it looks for files in the file system.但是template.ParseFiles(templateFileName)实现使用os.ReadFile(file) ,这意味着它在文件系统中查找文件。 Those files are not there because you copied only the executable.这些文件不存在,因为您只复制了可执行文件。

If you don't want to change your build process and are on Go 1.16 or above, embed the html files into your executable:如果您不想更改构建过程并且使用的是 Go 1.16 或更高版本,请将 html 文件embed到您的可执行文件中:

Folder structure:文件夹结构:

├── templates
│   ├── info.html
│   └── invite.html
│   └── templates.go <-- new file

templates.go模板.go

package templates

import _ "embed"

//go:embed info.html
var Info string

//go:embed invite.html
var Invite string

services/mail.go服务/mail.go

t, err := template.New("").Parse(templates.Info)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM