关于制作Docker镜像?| Dockerfile快速开始_dockerfile构建镜像-程序员宅基地

技术标签: 云原生(Cloud Native)、云计算、虚拟化  # CI/DI  dockerfile  # 构建工具  docker  

一、关于制作Docker镜像?

46 | 如何制作Docker镜像?
参考URL: https://time.geekbang.org/column/article/417216
要落地云原生架构,其中的一个核心点是通过容器来部署我们的应用。如果要使用容器来部署应用,那么制作应用的 Docker 镜像就是我们绕不开的关键一步。

1. Docker 镜像的构建原理和方式

构建一个 Docker 镜像,最常用的有两种:

  • 通过docker commit命令,基于一个已存在的容器构建出镜像。
  • 编写 Dockerfile 文件,并使用docker build命令来构建镜像。

上面这两种方法中,镜像构建的底层原理是相同的,都是通过下面 3 个步骤来构建镜像:

  1. 基于原镜像,启动一个 Docker 容器。
  2. 在容器中进行一些操作,例如执行命令、安装文件等。由这些操作产生的文件变更都会被记录在容器的存储层中。
  3. 将容器存储层的变更 commit 到新的镜像层中,并添加到原镜像上。

1.1 Docker 镜像的构建方式使用场景和选型

docker commit这种镜像构建方式通常用在下面两个场景中:

  • 构建临时的测试镜像;
  • 容器被入侵后,使用docker commit,基于被入侵的容器构建镜像,从而保留现场,方便以后追溯。

除了这两种场景,我不建议你使用docker commit来构建生产现网环境的镜像。

原因如下:

  • 使用docker commit构建的镜像包含了编译构建、安装软件,以及程序运行产生的大量无用文件,这会导致镜像体积很大,非常臃肿。
  • 使用docker commit构建的镜像会丢失掉所有对该镜像的操作历史,无法还原镜像的构建过程,不利于镜像的维护。
  • Dockerfile 的操作流程可以通过docker image history [镜像名称]查询,方便开发者查看变更记录。

在实际开发中,使用Dockerfile来构建是最常用,也最标准的镜像构建方法.

使用 Dockerfile 构建镜像,本质上也是通过镜像创建容器,并在容器中执行相应的指令,然后停止容器,提交存储层的文件变更。和用docker commit构建镜像的方式相比,它有三个好处:

  • Dockerfile 包含了镜像制作的完整操作流程,其他开发者可以通过 Dockerfile 了解并复现制作过程。
  • Dockerfile 中的每一条指令都会创建新的镜像层,这些镜像可以被 Docker Daemnon 缓存。再次制作镜像时,Docker 会尽量复用缓存的镜像层(using cache),而不是重新逐层构建,这样可以节省时间和磁盘空间。
  • 把这一切都放到一个 Dockerfile 里,既没有源码泄漏,又不需要用脚本去跨平台编译,还获得了最小的镜像。

在实际生产环境中,标准的做法是通过 Dockerfile 来构建镜像。使用 Dockerfile 构建镜像,就需要你编写 Dockerfile 文件。

二、Dockerfile快速开始

什么是Dockerfile

Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。

  • dockerfile是自定义镜像的一套规则
  • dockerfile由多条指令构成,Dockerfile中的每一条指令都会对应于Docker镜像中的每一层。

docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile。

docker build命令会读取Dockerfile的内容,并将Dockerfile的内容发送给 Docker 引擎,最终 Docker 引擎会解析Dockerfile中的每一条指令,构建出需要的镜像。

Dockerfile的基本结构

Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。

Dockerfile 常用的指令

Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。

FROM命令

FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
  • FROM:指定基础镜像,必须为第一个命令
  • FROM指令用于指定基础镜像,ARG是唯一可以位于FROM指令之前的指令,一般用于声明基础镜像的版本。
  • –platform选项可用在FROM多平台镜像的情况下指定平台。例如,linux/amd64、lunux/arm64、windows/amd64。
  • AS name表示为构建阶段命令,在后续FROM和COPY --from=name说明中可以使用这个名词,引用此阶段构建的映像。
  • tag或digest值是可选的。如果您省略其中任何一个,构建器默认使用latest标签。如果找不到指定tag,构建起将返回错误。
ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app
FROM extras:${CODE_VERSION}
CMD  /code/run-extras

LABEL

LABEL 指令将元数据添加到镜像。LABEL 是键值对。要在 LABEL 值中包含空格,请像在命令行中一样使用引号和反斜杠。

WORKDIR

用来指定当前工作目录(或者称为当前目录)

当使用相对目录的情况下,采用上一个WORKDIR指定的目录作为基准

相当与cd 命令,但不同的是指定了WORKDIR后,容器启动时执行的命令会在该目录下执行

RUN

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

RUN 主要用于在Image里执行指令,比如安装软件,下载文件等。
COPY指令从复制文件、目录到镜像文件系统的。

COPY

编写Dockerfile的时候copy宿主机文件到镜像中。

CMD

Dockerfile只允许使用一次CMD命令。使用多个CMD会抵消之前所有的命令,只有最后一个命令生效。一般来说,这是整个Dockerfile脚本的最后一个命令。

FROM ubuntu
CMD ["/usr/bin/wc","--help"]

CMD有三种形式:

  • CMD [“exec”,“param1”,“param2”]:使用exec执行,推荐方式。
  • CMD command param1 param2:在/bin/sh中执行,可以提供交互操作。
  • CMD [“param1”,“param2”]:提供给ENTRYPOINT的默认参数(极少这样使用)。

EXPOSE

EXPOSE指令通知容器在运行时监听某个端口,可以指定TCP或UDP,如果不指定协议,默认为TCP端口。但是为了安全,docker run命令如果没有带上相应的端口映射参数,Docker并不会将端口映射出去。

EXPOSE 80/tcp
EXPOSE 80/udp

指定映射端口方式:

docker run -P:将所有端口发布到主机接口,每个公开端口绑定到主机上的随机端口,端口范围在/proc/sys/net/ipv4/ip_local_port_range定义的临时端口范围内。

docker run -p :显式映射单个端口或端口范围。

ENTRYPOINT

ENTRYPOINT 指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有其他传入值作为该命令的参数。

一个Dockerfile中只能有一个ENTRYPOINT命令。如果有多条,只有最后一条有效。

无参的方式:
ENTRYPOINT [“/usr/sbin/nginx"]

指定参数的方式:
ENTRYPOINT [“/usr/sbin/nginx”, “-g”, “deamon off"]

docker run 的–entrypoint 标志可以覆盖原Dockerfile中的ENTRYPOINT 指令。

注意理解该命令, 该命令 是指定你每次 docker run启动容器的时候,都会自己执行的一个程序!!!。

CMD与ENTRYPOINT的关系
  • CMD可以为ENTRYPOINT提供参数,ENTRYPOINT本身也可以包含参数,但是可以把需要变动的参数写到CMD里面,而不需要变动的参数写到ENTRYPOINT里面;

  • ENTRYPOINT使用第二种shell方式会屏蔽掉CMD里面的命令参数和docker run后面加的命令。

  • 在Dockerfile中,ENTRYPOINT指定的参数比运行docker run时指定的参数更靠前。

ENTRYPOINT/CMD最后一条命令为无限运行的命令:
这句话才是使用ENTRYPOINT的精髓。
在Docker Daemon模式下,entrypoint、cmd命令的最后一个命令,一定是要当前容器需要一直运行的,才能防止容器退出。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令。换句话说实际执行时,会变成 “”

工作中常用技巧总结

Dockerfile中使用变量

通过关键字ARG,ENV设置变量

ARG arg1=test
ENV env1=production

注意:

  • 不能通过表达如$(uname -a)进行设置,只能设置为常量
  • ARG设置的变量在构建完成后,就会丢失。即在Docker中无法引用该变量
  • ENV设置的变量在Docker中可以通过如${env1}访问

在RUN中设置变量
在RUN通过arg=someValue中设置变量,以下脚本先获取Debain的系统版本号,并设置到了os_release变量中,在后续的命令中可以通过${os_release}进行引用

RUN os_release="$(cat /etc/os-release | grep VERSION_CODENAME | awk -F '=' '{print $2}')" &&\
    echo "deb http://mirrors.aliyun.com/debian/ ${os_release} main non-free contrib\n\
    deb http://mirrors.aliyun.com/debian-security ${os_release}/updates main\n\
    deb http://mirrors.aliyun.com/debian/ ${os_release}-updates main non-free contrib\n\
    deb http://mirrors.aliyun.com/debian/ ${os_release}-backports main non-free contrib\n"\
    > /etc/apt/sources.list

一个RUN命令,相当于新打开一个Shell。所以上一个RUN设置的变量无法在下一个RUN中使用。

因此如果你需要在build期间使用某些变量,那么ARG是最好的选择。如果你是想在运行期间使用,那么ENV是唯一的选择。

ENV主要是定义环境变量,在docker run的时候ENV的配置会加载到容易内部,但ARG的参数在内部是没法看到的。同时也可以通过下面命令更改ENV的默认值:

docker run -e var=yyy
如果现在我想在BUILD的时候,改变我的环境变量,而不是每次RUN的时候更改,需要怎么做?

ARG和ENV 两者结合使用

ARG var
ENV var=${
    var}

在dockerfile内部可以这样控制命令的参数。

ARG protocal
ARG address
ARG port

ENV protocal=${
    protocal} \
    address=${
    address} \
    port=${
    port}

CMD /usr/bin/lightweightservicediscovery --listen=${
    PROTOCAL:-ipv4}:${
    ADDRESS:-0.0.0.0}:${
    port:-49188}

//如果读取环境变量失败再采用后面的默认值。

这样既可以在build的时候通过docker build --build-arg var=xxx 来传递参数,也可以通过在运行的时候通过docker run -e var=yyy来传递参数。

实战小demo:

docker build --build-arg INSTALL_ZIP=myinstall.zip   -t centos-test:v4 .
ARG INSTALL_ZIP
COPY ./${
    INSTALL_ZIP} /root/
RUN chmod 755 ${
    INSTALL_ZIP}
RUN unzip ${
    INSTALL_ZIP}

三、如何制作Docker镜像?

docker build 命令制作docker镜像

docker build 命令用于使用 Dockerfile 创建镜像。

docker build [OPTIONS] PATH | URL | -

OPTIONS说明:

  • –build-arg=[] :设置镜像创建时的变量;
  • –cpu-shares :设置 cpu 使用权重;
  • –cpu-period :限制 CPU CFS周期;
  • –cpu-quota :限制 CPU CFS配额;
  • –cpuset-cpus :指定使用的CPU id;
  • –cpuset-mems :指定使用的内存 id;
  • –disable-content-trust :忽略校验,默认开启;
  • -f :指定要使用的Dockerfile路径;
  • –force-rm :设置镜像过程中删除中间容器;
  • –isolation :使用容器隔离技术;
  • –label=[] :设置镜像使用的元数据;
  • -m :设置内存最大值;
  • –memory-swap :设置Swap的最大值为内存+swap,"-1"表示不限swap;
  • –no-cache :创建镜像的过程不使用缓存;
  • –pull :尝试去更新镜像的新版本
  • –quiet, -q :安静模式,成功后只输出镜像 ID;
  • –rm :设置镜像成功后删除中间容器
  • –shm-size :设置/dev/shm的大小,默认值是64M;
  • –ulimit :Ulimit配置。
  • –squash :将 Dockerfile 中所有的操作压缩为一层。
  • –tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
  • –network: 默认 default。在构建期间设置RUN指令的网络模式

使用当前目录的 Dockerfile 创建镜像,标签为 runoob/ubuntu:v1。

docker build -t runoob/ubuntu:v1 . 

也可以通过 -f Dockerfile 文件的位置:

$ docker build -f /path/to/a/Dockerfile .

go build工作中遇到的常见问题

“docker build” requires exactly 1 argument.

问题描述:
在参照这docker官网教程学习构建镜像的时候。提示错误:“docker build” requires exactly 1 argument.

问题分析:
原因是因为(少了一个 ‘.’ , ‘.’ 代表当前路径):

docker build --tag=xxx .
报错:file not found in build context or excluded by .dockerignore

问题描述:
dockerfile中 执行copy 把主机上的文件往容器中copy时,报错file not found in build context or excluded by .dockerignore

问题分析:
我的dockerfile中 宿主机的文件,写的绝对路径。而 dockerfile 不能获取 父目录。

解决方案:
文件放置在当前路径下,dockerfile中 写成 ./文件名 即可(将文件copy到当前目录)。

构建 Go 应用 docker 镜像

构建 Go 应用 docker 镜像
https://zhuanlan.zhihu.com/p/476921483

之前我们的演示的是 centos:centos8 基础镜像,查看原文,作者使用了 golang:alpine 镜像,非常小。

原作者的 dockerfile demo:

FROM golang:alpine AS builder

LABEL stage=gobuilder

ENV CGO_ENABLED 0
ENV GOPROXY https://goproxy.cn,direct

RUN apk update --no-cache && apk add --no-cache tzdata

WORKDIR /build

ADD go.mod .
ADD go.sum .
RUN go mod download
COPY . .
RUN go build -ldflags="-s -w" -o /app/hello ./hello.go


FROM alpine

RUN apk update --no-cache && apk add --no-cache ca-certificates
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ Asia/Shanghai

WORKDIR /app
COPY --from=builder /app/hello /app/hello

CMD ["./hello"]
  • 默认禁用了 cgo
  • 启用了 GOPROXY 加速 go mod download
  • 去掉了调试信息 -ldflags=“-s -w” 以减小镜像尺寸
  • 安装了 ca-certificates,这样使用 TLS证书就没问题了
  • tzdata 在 builder 镜像安装,并在最终镜像只拷贝了需要的时区
  • 自动设置了本地时区,这样我们在日志里看到的是北京时间了

Dockerfile 构建出的镜像大小和 v1 centos对比结果如下:

$ docker images | grep hello
hello     v4    94ba3ece3071   4 hours ago     6.66MB
hello     v3    f51e1116be11   8 hours ago     6.61MB
hello     v2    0dd53f016c93   8 hours ago     6.61MB
hello     v1    ac0e37173b85   9 hours ago     314MB

docker镜像存储位置

docker info | grep "Docker Root Dir"
root@cka-k8s-master:~/code/mygoapp# cd /data/docker
root@cka-k8s-master:/data/docker# ls
buildkit  containers  image  network  overlay2  plugins  runtimes  swarm  tmp  trust  volumes
root@cka-k8s-master:/data/docker# du -sh * | sort -h
4.0K    runtimes
4.0K    swarm
4.0K    tmp
4.0K    trust
16K     plugins
28K     volumes
80K     network
88K     buildkit
4.6M    image
8.1M    containers
3.1G    overlay2
root@cka-k8s-master:/data/docker#

个人demo:go 项目docker镜像Dockerfile

原始镜像的选择?
centos:centos8作为基础镜像,是因为centos:centos8镜像中包含了基本的排障工具,例如vi、cat、curl、mkdir、cp等工具。

FROM centos:centos8
LABEL maintainer="<[email protected]>"

WORKDIR /opt/mygoapp

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
      echo "Asia/Shanghai" > /etc/timezone && \
      mkdir -p /var/log/mygoapp

COPY file-boom /opt/mygoapp/bin/

ENTRYPOINT ["/opt/mygoapp/bin/file-boom"]
CMD ["-c", "/etc/mygoapp/file-boom.yaml"]
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/inthat/article/details/124060033

智能推荐

服务器发送信息给arduino,arduino通过esp8266模块发送数据到云服务器-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏25次。arduino通过esp8266模块发送数据到云服务器我是代码小白,一个正在做毕设的秃头少年。鄙人拙作,有不当之处,还请指教。最近买了一套arduino设备,打算做一个物联网设备小玩意,可是怎么把数据上传到云服务器可愁坏我了。通过对比实验,我决定用esp8266wifi模块进行通信。云服务器的话,我现在还没写相应的代码,所以先用Onenet平台进行配置。Onenet平台进行配置1.进入Onenet..._esp8266 arduino wifi发送数据

latex h t b p是什么意思_latex htpb-程序员宅基地

文章浏览阅读1.4w次,点赞9次,收藏17次。常用选项[htbp]是浮动格式:『h』当前位置。将图形放置在正文文本中给出该图形环境的地方。如果本页所剩的页面不够,这一参数将不起作用。『t』顶部。将图形放置在页面的顶部。『b』底部。将图形放置在页面的底部。『p』浮动页。将图形放置在一只允许有浮动对象的页面上。在table或者figure 后加 [!htb] 是系统忽略“美学”标准,把表格和图片插入到你的代码中,是动的,但是不加感叹号,它就是按顺序选择h(此处),t(上方),b(下方),所以为了让图片随着你的代码移动,最好加一个[!htb]_latex htpb

【转载】linux下的usb抓包方法-程序员宅基地

文章浏览阅读67次。1 linux下的usb抓包方法1、配置内核使能usb monitor:make menuconfigDevice Drivers --> USB Support --> USB Monitor --> Sel..._linux安装tcpdump 查看usb

计算机组成pc em ir,计算机组成 课程设计报告.doc-程序员宅基地

文章浏览阅读164次。计算机组成 课程设计报告计算机组成原理课程设计报告姓 名:班 级:学 号:指导老师:2016年 6月31日目 录第一章 背景知识与课设任务概述11.1课设目的11.2课设任务11.2111.2211.2321.2421.252第二章 课设内容32.1指令的执行流程32.1.132.1.242.1.352.2存储器62.2.162.3运算器72.3.172.4硬件系统组成122.4..._计算机组成课程设计报告

python青果教务系统抢课_名额不够,技术来凑,利用Python实现教务系统强制性抢课...-程序员宅基地

文章浏览阅读1.3k次。最近一学期一次的抢课大戏又来了,几家欢乐几家愁。O(∩_∩)O哈哈~(l我每次一选就过了hah,我还是有欧的时候滴)。看着他们盯着教务系统就着急,何况我们那教务系统,不想说什么。emmm 想周围的朋友,正好下午利用扩容前一段时间写了个小脚本帮助朋友抢课。(当然抢到了啦,^_^)私信小编001即可获取大量Python学习资料,名额有限因为时间不够,来不及仔细琢磨,我第一想法就是直接提交选课的数据包(..._青果教务系统抢课

windows 加 switchyomega + burp 抓https包-程序员宅基地

文章浏览阅读4.6k次。很简单,下载证书后导入到受信任根目录证书下载,直接在代理状态浏览器访问burp点击CA就可以下载了 设置该证书全部信任,,switchyomega 设置如下即可 就可以抓https的包了 ...

随便推点

2021-07-18-程序员宅基地

文章浏览阅读43次。从零开始实现简易版本SpringIoC&DI&MVCSpring源码进修中,实现一个简易版本的Spring,包含以下主要内容:IoC,DI,MVC,已完成基本的功能。代码量还算一般,特此记录以共勉。首先是整个项目的基本思路。项目实现的功能就是从前端发一个请求,后端根据请求解析到后端的相应方法进行处理,完成后将结果进行返回。代码需要完成请求路径和类+方法的对应。配置阶段配置web.xml:DispatcherServlet设定init-param:contextConfigL

507页XX市应急管理局智慧矿山煤矿数字化矿山技术解决方案_18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案word-程序员宅基地

文章浏览阅读542次。只有在单系统自动化的基础上,通过高速网络接入各单系统,充分数据融合,建立合理的联动机制才能完成从单系统自动化到综合自动化的转变,该部分的转变从投入的资金和实现的容易度相对来讲可实现性和可控性都比较容易,但是从综合自动化向数字化矿山发展,涉及的面比较广,必须由多方共同来推进,一般涉及到“综合自动化”、“空间数字化”及“管理信息化”三大方面,三者缺一不可,通过三者的有机融合,再通过合适的平台例如三维可视化平台进行展示,同时通过科学合理的管理制度和流程加以应用才是真正意义上有血有肉的数字化矿山。_18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案word

Tomcat官网地址-程序员宅基地

文章浏览阅读1.4w次,点赞8次,收藏7次。Tomcat官网地址_tomcat官网

汇编指令长度计算_汇编指令占多少字节-程序员宅基地

文章浏览阅读5.1k次,点赞11次,收藏58次。指令长度与寻址方式有关系,规律或原则如下:一、没有操作数的指令,指令长度为1字节。如es:ds:cbwxlat等。二、操作数只涉及寄存器的指令,指令长度为2字节。如mov al,[si]mov ax,[bx+si]mov ds,ax等。三、操作数涉及内存地址的指令,指令长度为3字节。如mov al,[bx+1]mov ax,[bx+si+3]lea di,[1234]mov [2345],ax等。四、操作数涉及立即数的指令,指令长度为:寄存器类型+2。8位寄存器,寄存器_汇编指令占多少字节

二、RSA加密_ctf rsa 多个n和多个c-程序员宅基地

文章浏览阅读3.4k次。CTF中的RSA及攻击方法笔记1 数论基础1.1 模运算规则2 RSA相关题目2.1 已知 n,e,c 求 m2.2 已知 p,q,e 求 d2.3 已知dp,dq,c,p,q 求m2.4 仅已知c,c特别大 【c = m^e mod n】2.5 已知n1,n2,c1,c2,n 求 m2.6 已知n1,n2,e,c2 求m2.7 已知e,d,N 求p,q1 数论基础参考链接:https://www.freebuf.com/articles/web/257835.html1.1 模运算规则模运算与基_ctf rsa 多个n和多个c

mysql中把bigint类型转换为时间格式,与hive中unix_timestamp、FROM_UNIXTIME两个函数之间的区别_bigint转日期-程序员宅基地

文章浏览阅读2w次,点赞4次,收藏15次。数据库中时间类型是这样的,13位bigInt类型的数据select date_format(FROM_UNIXTIME(列名/1000),'%Y%m%d') from xx表原理就是把13位的时间格式/1000等于时间戳,使用FROM_UNIXTIME把时间戳转换成具体的日期ps:将时间转换为时间戳select unix_timestamp('2018-08-30..._bigint转日期

推荐文章

热门文章

相关标签