写在前面
本文的Go web项目代码摘自于 汪明《Go并发编程实战》一书中的第十一章。Docker部署过程借鉴了juejin.cn/post/689904… 这篇文章。但在实际部署过程中遇到一些文件路径问题,通过debug最终使得Go web项目能够在所有终端通过ip:port访问,在此记录一下踩坑点,希望对大家有所帮助。
部署过程
代码目录结构
import ( "net/http" "fmt" "myweb/web" ) func main() { web.RegisterRouter() //在static.go中定义 fmt.Println("server start") err := http.ListenAndServe(":8080", nil) //监听的端口号 if err != nil { fmt.Println("ListenAndServe: ", err) } }
`server.go`中包含`main`函数,通过调用`web`目录下的文件来实现服务。另外`index.html`是默认访问的静态资源,当url请求不是`index.html`时便会返回`error.html`。`font`、`css`、`images`、`js`目录下都存放一些静态文件以供`index.html`调用访问。最笨的办法是将所有的源码都打包进一个Docker镜像中。
编写一个Dockerfile文件
```Dockerfile
FROM golang:alpine //设置基础镜像,会提供go的运行环境
RUN mkdir /build
#将代码复制到/build中,此时.是docker build命令所指定的目录,也就是Dockerfile所在目录
COPY . /build
#当前工作目录变为/build,后面的. 指代的便是/build
WORKDIR /build
#将代码编译成二进制可执行文件
RUN go build -o app .
#暴露端口
EXPOSE 8080
#需要运行的命令
CMD ["/build/app"]
在实际编写过程中,如果碰到“源码正常运行,上了Docker出现 # panic: runtime error: invalid memory address or nil pointer dereference ”,此时就要考虑是不是文件目录是否放错。如果不对位,这个错误其实在对应点上fmt.Println(err)便是找不到指定文件。所以编写Dockerfile一定要根据自己的文件结构去编写。
docker build . -t goweb_app:1.1
上述命令中,由于这条docker build运行在含有Dockerfile的目录下,所以只需要.(表示当前目录)。 -t指定了镜像的名称。可以通过docker build --help查看一些参数的作用。
注意,一般在构建镜像的时候都会打上版本号(Tag),每重新构建一次镜像便更新tag。我们来看下上述Dockerfile编出来的镜像大小。
docker images //查看所有镜像
两阶段构建镜像
Go编译之后得到的是一个二进制可执行文件,我们只要这个二进制文件,以及静态文件即可(有的项目还可能有配置文件)。
FROM golang:alpine as builder
# 容器环境变量添加,会覆盖默认的变量值
ENV GO111MODULE=on\
GOOS=linux \
CGO_ENABLED=0 \
GOARCH=amd64
WORKDIR /build
COPY . .
RUN go build -o app .
FROM scratch
WORKDIR /secbuild
COPY --from=builder /build/app /secbuild
COPY --from=builder /build/index.html /secbuild
COPY --from=builder /build/error.html /secbuild/error.html
COPY --from=builder /build/css /secbuild/css
COPY --from=builder /build/js /secbuild/js
COPY --from=builder /build/images /secbuild/images
COPY --from=builder /build/font /secbuild/font
EXPOSE 8080
CMD [ "/secbuild/app" ]
这里面坑比较多,尝试很久才这样的写法才能正常运行。
可以看到,第一阶段的镜像有339MB,第二阶段的镜像只有8.6MB。当我们删除掉基础镜像后,仅仅使用8.6MB的镜像创建并运行容器,看看能否正常运行。
我们看看在手机端输入http://云主机ip:8080/index.html请求时,看看能否正常响应。
页面正常返回,大功告成!以后就按照上面的模式写多阶段Dockerfile了!
另外,如果在宿主机(不是云主机,而是去ssh这个云主机的主机)输入http://localhost:8080/index.html时,显示localhost拒绝连接:
在本地浏览器输入http://localhost:8080/index.html,返回如下:
Dockerfile的编写,以及在浏览器上测试网页坑确实还挺多的,共勉!