Docker 学习笔记(二):Dockerfile 定制镜像
镜像的定制实际上就是定制每一层所添加的配置、文件。
如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。
这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
因为每一条指令构建一层,而且每一层构建好后,就不会再变化。为了使镜像尽可能地小而且层次清晰,每一层都应该围绕一个特定的目标进行构建,并且在构建结束前,要清理掉所有缓存和其他无关的东西!
Docker 现在最多只能支持 127 层,尽量让每一条命令都完成一个完整的目标,不要每条 shell 命令都对应一个 RUN,这是相当糟糕的做法。
在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。
一、commit 命令
docker commit 用法类似 git commit,用于将当前容器层的修改,固化成一个新的镜像层。
# 1. 首先启动了一个容器
# 2. 通过 exec 命令登入该容器做一些修改
# 3. 可以使用下列命令查看容器层的具体改动
docker diff <container id> 
# 4. 使用 commit 命令提交容器层的改动
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]  # 模板
docker commit \
    --author "Tao Wang <twang2218@gmail.com>" \
    --message "修改了默认网页" \
    webserver \
    nginx:v2
# 5. 使用 history 命令查看镜像的构建历史
docker history nginx:v2
利用 Dockerfile 定制镜像,其实就是在基础镜像上启动一个临时容器,然后在该容器上一条条地运行 Dockerfile 内的指令。
每跑完一个指令,就将当前的修改固化层一个新的镜像层(这就类似在此时执行 docker commit)。
指令跑完了,一个分层的镜像也就生成了,这时再清除掉构建用的临时容器。
二 、Dockerfile 指令详解
Dockerfile 常用的有十多个指令:
FROM:指定基础镜像LABEL:镜像的一些标签,如 maintainer/licenceUSER:能用普通用户,就不要用 root 来做。建议使用普通用户来运行不需要 root 权限的服务。ENV:设置环境变量,可用于设置 PATH 或者其他环境变量。- 运行容器时,可以通过 
--env XXX=xxx来设置或者修改环境变量。--env可多次重复使用 - 密码/密钥等参数的默认值可设置在这里,在运行时自行修改。
 
- 运行容器时,可以通过 
 ARG:构建时的参数,只在构建期有用。(而 ENV 就相当于运行期参数)- 可通过
docker build -build-arg <varname>=<value> xxx来修改构建参数。 - 一般用于设置一些依赖的版本号、镜像源的地址等。构建时根据这些参数从镜像源下载对应的依赖。
 
- 可通过
 WORKDIR:用于制定下一个镜像层的工作目录(容器内部的),类比cd xxx。- 可多次使用,这样每一个镜像层都可以用不同的工作目录。
 - 如果路径不存在,会直接创建该路径
 
ADD/COPY:都是添加文件的命令,更推荐使用 COPY,ADD 最好只用在tar.gz/tar.xz等文件的添加上(会自动解压)。- 需要下载的文件,建议使用 curl/wget
 - COPY 可以用于从别的镜像复制文件(常用于多阶段构建)
 
RUN:最常用的构建指令,会创建新的镜像层,所以最好让每条 RUN 命令都完成一个目标的构建,减少层数。VOLUME:指定数据层挂载点。- 常用:
VOLUME ["/data", "/var/log/"] 
- 常用:
 EXPOSE:暴露端口。- 该指令只制定了容器需要暴露的端口。在 run 时还需要用 
-p xx:xx做端口映射,才能和本机的端口绑定! 
- 该指令只制定了容器需要暴露的端口。在 run 时还需要用 
 ENTRYPOINT:镜像的“入口”,也就是启动镜像时会执行的命令。- 格式:
ENTRYPOINT ["executable", "param1", "param2"] docker run命令的所有其他参数,都会被当作 "入口"命令的参数传入!
- 格式:
 CMD:在不使用ENTRYPOINT的情况下,它就是镜像的默认命令。- 格式:
CMD ["executable","param1","param2"] - 在使用 
ENTRYPOINT的情况下,CMD建议设置为CMD ["--help"],并且紧跟在ENTRYPOINT命令之后。 
- 格式:
 ONBUILD:该指令适合用在基础镜像的构建中。- 如果 
FROM一个使用了ONBUILD指令的镜像,会先执行该指令,然后才执行 Dockerfile 里面的指令。 
- 如果 
 
需要注意的是,现在只有 RUN/ADD/COPY 这三条指令,才会创建新的镜像层。其他的指令只会在构建过程中创建临时镜像层,它们不会出现在最终的镜像中。
1. FROM 指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。
在 Docker Hub 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node、openjdk、python、ruby、golang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntu、debian、centos、fedora、alpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
FROM scratch
...
如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm、coreos/etcd。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
镜像构建上下文
docker build --tag <image name>:tag . 中的 . 并不仅仅指 Dockerfile 的路径!
build 命令的最后一个参数,是镜像构建上下文的路径,这个路径可以是文件夹路径,可以是一个 tar 压缩包,也可以是一个 url,甚至 git 仓库地址也是支持的。
Docker 是 Client/Server 模式的程序,build 命令会将该 [文件夹/tar 压缩包/url] 的内容发送给 Server 端(Docker 引擎)用于构建,
因此后面构建中的 COPY/ADD 指令,只能使用上下文里面的内容,更不支持 ../xxx 这样的路径。
三、多阶段构建
多阶段构建中,不同的阶段使用不同的基础镜像(因此有多个 FROM),前面的阶段大都是为了生成一些需要的文件(前后端编译等)。
在最后一个阶段,使用  COPY 将需要的文件从前几个阶段生成的镜像中 COPY 过来,这样就得到了一个只包含运行时的镜像。
前端编译基于前端相关的镜像,后端用后端的编译镜像,最后放到只包含运行时的镜像里。
FROM golang:1.9-alpine as builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]
附
一些 Jenkins 构建,可供参考/使用的 Dockerfile:
- Slave: Jenkins Slave
 - Python 3.7
- Miniconda3,Linux 通用,省心
 - docker-alpine-python3,超小版本
 
 - Android 构建环境:Docker Android Build Box
 - 大合集:mritd/dockerfile
 
参考
Docker 学习笔记(二):Dockerfile 定制镜像的更多相关文章
- docker学习笔记二:常用命令
		
docker学习笔记二:常用命令 查看docker常用命令 docker --help 返回结果如下: 其中常用的命令如下: 1.image相关操作 展示所有的image: 删除image: rmi ...
 - InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移
		
系列目录 InterSystems Ensemble学习笔记(一) Ensemble介绍及安装InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移 一 ...
 - Docker学习笔记二(linux下安装Docker)
		
Docker学习笔记二(linux下安装Docker) 1.在线安装linux Docker 这种方式首先要保证linux 环境下可以上网,当然,小编是在自己的电脑上安装了虚拟机,在虚拟机上安装了,l ...
 - docker学习笔记(3)- 镜像
		
简介 在docker学习笔记(1)- 架构概述一节中可以看到镜像是docker三大组件之一,可以将Docker镜像类比为虚拟机的模版. 镜像由多个层组成,每层叠加之后从外部看就像一个独立的对象,镜像的 ...
 - Docker学习笔记三 Dockerfile 指令 定制镜像
		
本文地址:https://www.cnblogs.com/veinyin/p/10412079.html 镜像是分层存储的,每一层都是独立存在的,修改当前层并不会修改其依赖的上一层,删除某一层也只是 ...
 - Docker学习笔记二 使用镜像
		
本文地址:https://www.cnblogs.com/veinyin/p/10408363.html Docker运行容器前,需本地存在对应镜像,若没有则Docker从镜像仓库下载该镜像. 镜 ...
 - Docker学习笔记 - 创建私有的镜像仓库
		
一.查找镜像仓库 https://hub.docker.com/ 二.下载镜像仓库 docker pull registry:2.6.2 三.安装镜像仓库 docker run -d -p 6000: ...
 - Docker学习笔记二:Docker常用命令及提升拉取镜像的速度
		
一.Docker命令: 1.docker images //命令用来查看docker中所包含的镜像信息 2.docker ps -a //命令用来查看docker中所包含所有容器信息(运行状 ...
 - docker学习之使用 DockerFile 构建镜像并搭建 swarm+compose 集群
		
题目要求 (1)将springboot应用程序打成jar包:Hot.jar (2)利用dockerfile将Hot.jar构建成镜像 (3)构建 Swarm 集群 (4)在 Swarm 集群中使用 c ...
 - docker学习笔记二
		
知识点: 1)手动构建镜像 2)Dockerfile快速构建镜像 阿里云yum源https://opsx.alibaba.com/mirror 镜像制作nginx镜像实例 创建并运行centos容器 ...
 
随机推荐
- Linux下通过nmap扫描局域网内设备,获取ip地址和mac地址
			
安装nmap sudo apt-get install nmap 扫描 sudo nmap -sP -PI -PT
 - 【转】禁用chrome firefox 的 WebRTC功能防止真实IP泄漏
			
无论是使用VPN还是其它代理方式,很多时候我们不希望暴露自己的真实IP,且一直以来我们认为VPN是安全的,所有流量都会走VPN. 但最近暴露出一个WebRTC特性,会暴露我们的真实IP.适用浏览器:c ...
 - Qualcomm Audio HAL 音频通路设置【转】
			
本文转载自:https://blog.csdn.net/azloong/article/details/79383323 1. 音频框图概述| Front End PCMs | SoC DSP | B ...
 - 阿里云ECS,Ubuntu Server 16.04安装图形界面远程控制
			
最近阿里云有新用户免费体验6个月的活动,虽说是免费体验,但是还是要买个它们的产品才行,我就花9.9买了个最便宜的,然后就获得了一个乞丐版的ECS服务器,配置是1核内存1G.系统装的是Ubuntu Se ...
 - Leetcode: Longest Common Subsequence
			
Given two strings text1 and text2, return the length of their longest common subsequence. A subseque ...
 - JS实现动态添加和删除div
			
实现方式一:只在最后一个数据中动态添加或者删除 | 背景需要做一个页面,页面可以输入参数,点击确认按钮可以发请求给某接口.但是接口的某个字段是数组类型,所以在页面上需要实现添加或者删除元素的功能. | ...
 - 先查询再插入,改为存储过程,java部分入参出参、mybatisxml【我】
			
先查询再插入,改为存储过程 create or replace procedure PRO_REVENUE_SI(l_p_cd in Varchar2, l_c_cd in Varchar2, l_p ...
 - spring boot集成Websocket
			
websocket实现后台像前端主动推送消息的模式,可以减去前端的请求获取数据的模式.而后台主动推送消息一般都是要求消息回馈比较及时,同时减少前端ajax轮询请求,减少资源开销. spring boo ...
 - Spring cloud微服务安全实战-3-14章节小结
			
认证:一个httpBasic 一个是用户名密码的认证 授权:数据库内判断的r就是读 w就是写 ,ACL 获取用户信息的这段逻辑,实际上着也是安全机制的一种.防止越权,当前只能看到自己的信息. Spri ...
 - PAT 甲级 1144 The Missing Number (20 分)(简单,最后一个测试点没过由于开的数组没必要大于N)
			
1144 The Missing Number (20 分) Given N integers, you are supposed to find the smallest positive in ...