docker学习3-打包一个docker镜像

之前使用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命令

  1. FROM 指定以哪个镜像为基础开始构建镜像
  2. WROKDIR 指定我们docker中的工作目录,如果这个目录不存在,会自动创建
  3. VOLUME 指定对外映射的目录
  4. ADD 将本地的文件添加到docker中
  5. COPY 和ADD命令相似也是将本地的文件添加到docker中
  6. EXPOSE 指定对外映射的端口
  7. CMD 在docker中执行命令,如果有多个CMD,只有最有一个生效

别的命令没什么特殊的,简单说一下ADDCOPY的区别,相同点都是讲文件复制到docker中,不同的是ADD命令功能更多,如果复制到文件是tar,zip等压缩文件时,ADD命令会自动解压缩,ADD还可以从网络上加载文件

Dockerfile文件写好了,下一步开始构建了 使用 docker build -t goweb .命令构建 goweb 是指定构建后镜像的名字

build

没有报错,说明构建完成

可以在本地的镜像中看到刚才构建的镜像了,大小只有5.4M

有了镜像开始创建容器 现在用户目录下新建一个 go_web 目录 使用docker run -idt --name goweb -v ~/go_web:/app/data -p 8088:8088 goweb命令创建容器 之前的文章已经介绍过了,不在赘述了,文章 传送门 看一下容器的运行情况

what,为啥容器退出了,应该是出错了,看一下错误

还记得前面画的重点吗,go编译后的二进制程序不依赖外外部库,其实这句话是错误的 看一下刚才编译的程序依赖那些外部库

现在的main文件依赖4个外部文件,因为我们是基于scratch打包的,scratch是一个空白的镜像,没有这些文件,所以运行就报错了。有两种解决办法

  1. 使用Ubuntu等有这些库的基础镜像,但是这样会增加打包后镜像的体积,很显然不划算
  2. 使用静态编译,把这些库编译进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镜像就完成了。

发表了56篇文章 · 总计128.44k字
本博客已稳定运行
© QX
使用 Hugo 构建
主题 StackJimmy 设计