当我对我的 Go 应用程序进行 docker 化时,它看不到 HTML 模板



我正在尝试用gomail编写通知服务。当我在本地运行我的应用程序时,一切都很好,但当我将应用程序固定化时,我会得到零指针错误,因为gomail找不到html模板。

错误:

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

这是我的档案:

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

我的文件夹结构:

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

代码:

kafka/消费者.go

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

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

在dockerfile的末尾运行COPY --from=builder /app/main .,因此只复制可执行文件。

然而,template.ParseFiles(templateFileName)实现使用os.ReadFile(file),这意味着它在文件系统中查找文件。这些文件不在那里,因为您只复制了可执行文件。

如果你不想改变你的构建过程,并且正在执行Go 1.16或更高版本,请将html文件embed保存到你的可执行文件中:

文件夹结构:

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

templates.go

package templates
import _ "embed"
//go:embed info.html
var Info string
//go:embed invite.html
var Invite string

services/mail.go

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

最新更新