之前使用docker安装过mysql,使用的是别人制作好的镜像。今天使用Dockerfile自己打包一个docker的镜像。这个镜像是一个web的镜像,使用go编写。
go非常适合用来写docker的镜像程序,因为go编译后的二进制程序不依赖外外部库(划重点,后面会用到这个知识点),可以非常方便的打包进docker中。 这次打包镜像会用到docker的端口映射和目录挂载这两个特性,后面用到会指出 我的编译和打包是在linux中完成的,文中所有的介绍都是以linux系统为基础
先上web程序的代码
1package main
2
3import (
4 "net/http"
5 "os"
6 "strconv"
7 "time"
8)
9
10const BASE_DIR = "./data/"
11
12func main() {
13 http.HandleFunc("/", Hand)
14 http.ListenAndServe("0.0.0.0:8088", nil)
15}
16
17func Hand(w http.ResponseWriter, r *http.Request) {
18 query := r.URL.Query()
19 one := query.Get("one")
20 now := time.Now().Unix()
21 fileName := strconv.FormatInt(now, 10)
22
23 file, err := os.OpenFile(BASE_DIR+fileName+".txt", os.O_RDWR|os.O_CREATE, 0666)
24 if err == nil {
25 file.WriteString(one)
26 file.Close()
27 w.Write([]byte("ok"))
28 } else {
29 w.Write([]byte("open file error,"))
30 w.Write([]byte(err.Error()))
31 }
32}
简单说一下逻辑,web会监听 8080 端口,当收到客户端的请求时,会在当前目录的 /data/ 下创建一个 当前时间戳.txt 文件,并把获取到的数据写入到文件中,并把结果返回到网页中
先把代码编译成可执行文件,执行 go build -ldflags "-w -s" main.go
,-ldflags “-w -s” 这个参数会自动优化代码,这样编译出来的可执行文件体积会变小。
先在本地运行一下看一下效果
浏览器显示正确,再看一下data目录下有没有对应的文件
文件也正常写入了,说明程序是没有问题的
下面开始打包docker
开始编写Dockerfile文件
1FROM scratch
2
3WORKDIR /app
4
5VOLUME /app/data
6
7ADD main /app
8
9COPY data /app
10
11EXPOSE 8088
12
13CMD ["/app/main"]
打包docker镜像需要在一个镜像的基础上打包,scratch这个镜像是一个很特殊的镜像,是一个空的镜像,大小是0,这样我们打包出来的镜像会很小,如果使用Ubuntu作为基础镜像,打包出来的镜像体积会很大,因为Ubuntu镜像就要有60M+了。更多关于scratch可以查看这里。 用到的Dockerfile命令
- FROM 指定以哪个镜像为基础开始构建镜像
- WROKDIR 指定我们docker中的工作目录,如果这个目录不存在,会自动创建
- VOLUME 指定对外映射的目录
- ADD 将本地的文件添加到docker中
- COPY 和ADD命令相似也是将本地的文件添加到docker中
- EXPOSE 指定对外映射的端口
- CMD 在docker中执行命令,如果有多个CMD,只有最有一个生效
别的命令没什么特殊的,简单说一下ADD和COPY的区别,相同点都是讲文件复制到docker中,不同的是ADD命令功能更多,如果复制到文件是tar,zip等压缩文件时,ADD命令会自动解压缩,ADD还可以从网络上加载文件
Dockerfile文件写好了,下一步开始构建了
使用 docker build -t goweb .
命令构建 goweb 是指定构建后镜像的名字
没有报错,说明构建完成
可以在本地的镜像中看到刚才构建的镜像了,大小只有5.4M
有了镜像开始创建容器
现在用户目录下新建一个 go_web 目录
使用docker run -idt --name goweb -v ~/go_web:/app/data -p 8088:8088 goweb
命令创建容器
之前的文章已经介绍过了,不在赘述了,文章 传送门
看一下容器的运行情况
what,为啥容器退出了,应该是出错了,看一下错误
还记得前面画的重点吗,go编译后的二进制程序不依赖外外部库,其实这句话是错误的 看一下刚才编译的程序依赖那些外部库
现在的main文件依赖4个外部文件,因为我们是基于scratch打包的,scratch是一个空白的镜像,没有这些文件,所以运行就报错了。有两种解决办法
- 使用Ubuntu等有这些库的基础镜像,但是这样会增加打包后镜像的体积,很显然不划算
- 使用静态编译,把这些库编译进main文件中
重新编译go文件
先把原来的main改一个名字mv main main2
重新编译CGO_ENABLED=0 go build -ldflags "-w -s" main.go
CGO_ENABLED=0 指定为静态编译
现在看一下main的外部依赖
现在的main不依赖外部文件了 把之前的docker镜像和容器都删掉,重新打包docker,重新生成容器
现在看一下
容器已经运行了,并且完成了端口映射 在浏览器中试一下
看一下go_web目录
在go_web目录中已经创建了文件,并且内容也正确写入了
到这,自己打包docker镜像就完成了。