docker - 当我 dockerize 我的 Go 应用程序时,它没有看到 HTML 模板
问题描述
我正在尝试使用 gomail 编写通知服务。当我在本地运行我的应用程序时,一切正常,但是当我 dockerize 我的应用程序时,我得到零指针错误,因为 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
代码:
卡夫卡/consumer.go
if ok := mailService.Send("templates/info.html", inviteMessage.Email, "Account Created", content); !ok {
continue
}
服务/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 或更高版本,embed
请将 html 文件放入可执行文件中:
文件夹结构:
├── templates
│ ├── info.html
│ └── invite.html
│ └── templates.go <-- new file
模板.go
package templates
import _ "embed"
//go:embed info.html
var Info string
//go:embed invite.html
var Invite string
服务/mail.go
t, err := template.New("").Parse(templates.Info)
推荐阅读
- azure-devops - Azure YAML Pipelines:如何在 Azure DevOps 的另一个项目中引用一个项目和 repo
- for-loop - FOR循环/向量化数列
- lua - 如何在 ROBLOX 中制作杀手机器人
- python - 在 Python 中录制时检测并分割静音
- mongodb - $ 查找 mongodb 对象数组
- python - Flask、Asyncio、BS4、Requests-html ValueError:信号仅在主解释器的主线程中有效
- android - 使用 replace() 时片段未替换
- blockchain - 使用 web3.js 调用以太坊合约时出现意外错误
- spring - Spring Boot REST API - 字段 ID 的类型不匹配
- flutter - Github 中的 App 登录和 Flutter 中的身份验证