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

之前使用docker安装过mysql,使用的是别人制作好的镜像。今天使用Dockerfile自己打包一个docker的镜像。这个镜像是一个web的镜像,使用go编写。go非常适合用来写docker的镜像程序,因为go编译后的二进制程序不依赖外外部库(划重点,后面会用到这个知识点),可以非常方便的打包进docker中。
这次打包镜像会用到docker的端口映射和目录挂载这两个特性,后面用到会指出
我的编译和打包是在linux中完成的,文中所有的介绍都是以linux系统为基础

先上web程序的代码

package main

import (
    "net/http"
    "os"
    "strconv"
    "time"
)

const BASE_DIR = "./data/"

func main() {
    http.HandleFunc("/", Hand)
    http.ListenAndServe("0.0.0.0:8088", nil)
}

func Hand(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query()
    one := query.Get("one")
    now := time.Now().Unix()
    fileName := strconv.FormatInt(now, 10)

    file, err := os.OpenFile(BASE_DIR+fileName+".txt", os.O_RDWR|os.O_CREATE, 0666)
    if err == nil {
        file.WriteString(one)
        file.Close()
        w.Write([]byte("ok"))
    } else {
        w.Write([]byte("open file error,"))
        w.Write([]byte(err.Error()))
    }
}

简单说一下逻辑,web会监听8080 端口,当收到客户端的请求时,会在当前目录的/data/下创建一个当前时间戳.txt文件,并把获取到的数据写入到文件中,并把结果返回到网页中

先把代码编译成可执行文件,执行 go build -ldflags "-w -s" main.go-ldflags "-w -s" 这个参数会自动优化代码,这样编译出来的可执行文件体积会变小。
先在本地运行一下看一下效果


浏览器显示正确,再看一下data目录下有没有对应的文件

文件也正常写入了,说明程序是没有问题的

下面开始打包docker

开始编写Dockerfile文件

FROM scratch

WORKDIR /app

VOLUME /app/data

ADD main /app

COPY data /app

EXPOSE 8088

CMD ["/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镜像就完成了。

你可能感兴趣的:(docker学习3,打包一个docker镜像)