在构建容器化应用时,至关重要的步骤莫过于镜像制做,本文将介绍镜像制做方法以及镜像制做的建议。一般镜像的制做有两种方式:
使用现有的容器使用docker commit 生成镜像使用Dockerfile进行镜像构建
''''
采用docker commit 生成的镜像其实是容器内的文件系统进行修改在进行提交,而运行的容器其实是在镜像的文件系统顶层添加了一层读写层,所都的修改都是基于这一层,当生成镜像时会将这一层数据保存,因此每次使用commit提交镜像时候都会比原来多一层,这样会使得镜像愈来愈大而且不易维护。同时,对于镜像使用者来讲彻底不透明,使用者不清楚该镜像怎么样构建的,是否安全等,这种方式及其不推荐。
''''
而使用Dockerfile构建镜像,对于使用者来讲彻底透明,构建镜像的每个步骤都在Dockerfile文件中描述的清清楚楚,同时当须要对镜像修改时候,只需修改Dockerfile文件中的指令,维护镜像只须要维护一个Dockerfile,这也是镜像构建的最佳方式。固然,要使用Dockerfile就必须明白Dockerfile的语法和各个指令,如下将做详细介绍。
''''
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。能够在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。html
''''
Dockerfile实际上就是一个文本文件,只不过这里的文件内容被Docker Deamon识别从而进行镜像构建。
''''
在构建docker镜像的时候,实际上将当前目录移动到了一个虚拟目录当中,全部的操做路径都是以虚拟路径为准。linux
使用Dockerfile步骤:
1.编写Dockerfile文件,用于描述镜像生成的步骤
2.使用docker build -t name:tag 命令构建镜像nginx
格式:
docker build [参数] [dockerfile的路径]git
docker build参数:
-c : 指定使用CPU大小
-f : 指定dockerfile路径
-t : 指定构建后的镜像名称github
#
号表明注解。1.若要在Dockerfile中引环境变量则使用$variable_name或${variable_name}
2.当变量为空或者变量值未设置可使用${variable_name:-value}来指定变量的默认值golang
docker build 命令用于基于Dockerfile构建镜像,使用语法:
docker build [OPTIONS] PATH | URL | -
其中PATH表明含有Dockfile的目录,固然也能够是URL中含有Dockerfile
经常使用选项:docker
-t, --tag list # 指定生成镜像标签,格式为name:tag -f, --file string # 单独指定Dockerfile文件位置 --build-arg list # 设置构建时的变量 --no-cache # 构建镜像时候不使用缓存
构建一个简单的nginx镜像:shell
# 1.建立一个目录用于存放DockerFile [root@docker ~]# mkdir docker_project [root@docker ~]# cd docker_project/ [root@docker ~/docker_project]# # 2.编辑Dockerfile文件,若是文件名称不是Dockerfile须要用-f指定名称 [root@docker ~/docker_project]# vim Dockerfile # 指定基础镜像为centos:7.9.2009 FROM centos:7.9.2009 # 3.构建镜像 [root@docker ~/docker_project]# docker build -t centos7:v1 . Sending build context to Docker daemon 2.048kB Step 1/1 : FROM centos:7.9.2009 ---> 8652b9f0cb4c Successfully built 8652b9f0cb4c Successfully tagged centos7:v1 # 4.利用制做的镜像启动容器,并查看是否运行成功 [root@docker ~/docker_project]# docker run -d -it --name my_centos71 centos7:v1 dd95eab722bf53fbcafbfad420b87eb5acd7cbdccfdff320a6709faca3e2bd37 [root@docker ~/docker_project]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES dd95eab722bf centos7:v1 "/bin/bash" 3 seconds ago Up 2 seconds my_centos71
''''
FROM指令是最重要且必须为Dockerfile中的第一个非注视指令,用于为构建的镜像指定基础镜像。后续指令运行环境基于该基础镜像,构建镜像时候默认会先从主机上寻找镜像,若不存在时则从Docker HUB上拉取镜像。指定构建镜像的基础镜像(有且只能有一个基础镜像)vim
语法 : FROM <repository> FROM <repository>[:<tag>] FROM <repository>@<digest> FROM [基础镜像]:[镜像版本号] 解释: repository: # 镜像仓库 tag: # 镜像标签,省略就是latest digest: # 镜像哈希码
''''
LABEL用于为镜像提供元数据信息,其数据格式为key=value。windows
语法 : LABEL <key>=<value> <key>=<value> <key>=<value> ... 示例: LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
''''
使用LABEL指定元数据时,一条LABEL指定能够指定一或多条元数据,指定多条元数据时不一样元数据之间经过空格分隔。推荐将全部的元数据经过一条LABEL指令指定,以避免生成过多的中间镜像。
''''
用于提供镜像提供者的信息,能够在Docker任何位置。该语法可能废弃,推荐使用LABEL
语法: MAINTAINER <message> 解释: message:能够是任意文本信息 示例: MAINTAINER "wd <xxx@163.com>"
''''
用于主机中的文件或者复制到镜像中
语法: COPY [--chown=<user>:<group>] <src>... <dest> COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] 解释: src: # 源文件或者目录,支持通配符。若是src是目录,src目录本身不会被复制,复制的是目录中的文件 dest: # 容器中文件系统目录,若是目录不存在自动建立建立。 user: # 复制到容器中的文件所属用户 group:# 复制到容器中的文件所属用户组
注意事项:
''''
ADD指令相似于COPY,可是ADD比COPY更强大,支持TAR文件和URL路径
ADD [--chown=<user>:<group>] <src>... <dest> ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] 解释: src: # 源文件或者目录,支持通配符。若是src是目录,src目录本身不会被复制,复制的是目录中的文件 dest: # 容器中文件系统目录,若是目录不存在自动建立建立。 user: # 复制到容器中的文件所属用户 group:# 复制到容器中的文件所属用户组 示例: ADD hom* /mydir/ ADD hom?.txt /mydir/
注意事项:
''''
用于为Dockerfile中的各个指定设置工做目录,可使用屡次,当使用相对路径时目录是基于前一个WORKDIR指令
语法 : WORKDIR dirpath 示例: WORKDIR /usr/local
''''
用于为镜像定义所需的环境变量,并可被Dockfile中位于其之后的指令所调用,如ADD、COPY、RUN等调用格式为$variable_name或者${variable_name},此外在启动容器时候这些变量也是存在的。
语法: ENV <key> <value> ENV <key>=<value> ... 示例: ENV myName="John Doe” \ myDog=Rex \ myCat=fluffy ENV myCat fluffy
注意事项:
''''
用于在build过程当中运行的程序,能够是任何指令,能够指定多个RUN,RUN指令建立的中间镜像会被缓存,并会在下次构建中使用。若是不想使用这些缓存镜像,能够在构建时指定--no-cache参数,如:docker build --no-cache
首先有个atest shell脚本,里面的内容为
echo $0 echo $1 echo $2 # 执行bash -c “./atest hello world”他的输出以下: ./atest hello world
注意事项:
两种语法: # shell 格式默认linux采用/bin/sh -c,windows采用cmd /S /C RUN <command> [linux命令] # exec可执行程序格式 RUN ["executable", "param1", "param2”] 示例: RUN yum install -y nginx RUN ["/bin/bash", "-c", "echo hello"]
''''
用于为容器暴露端口到外部,用于实现通信,相似于docker run的-p选项
语法: POSE <port> [<port>/<protocol>...] 解释: port: # 端口 protocol: # 协议,能够是udp或tcp,默认tcp 示例: EXPOSE 8080 EXPOSE 8080/udp 8088/tcp
''''
用于为在镜像启动时为容器候提供的默认命令,该指令能够有多个,可是只有最后一个生效。
语法 : # shell格式,含有shell环境 CMD command param1 param2 # 可执行程序格式 CMD ["executable","param1","param2”] # 第三种用于为ENTRYPOINT提供默认参数 CMD ["param1","param2”]
注意:
''''
相似于CMD功能,用于为启动容器指定默认启动命令,与CMD不一样的是ENTRYPOINT命令不会随着docker run 后使用的命令覆盖而会把命令做为参数,除非docker run 参数中指定了—entrypoint
语法 : ENTRYPOINT <command> ENTRYPOINT ["<executable>", "<param1>", "<param2>"]
注意事项:
示例:
["nginx","-g","daemon off"]
''''
用于指定构建镜像时RUN、CMD、ENTRYPOINT等指令使用的用户或UID,默认状况容器运行身份为ROOT
语法 : USER <user>[:<group>] USER <UID>[:<GID>] 示例: USER nginx
注意事项:
''''
将可执行程序运行为shell环境,默认以/bin/sh -c运行
语法: SHELL ["executable", "parameters"] 示例: # 等价于 RUN echo hello SHELL ["echo", “hello"]
''''
该指令用于在build过程当中提供参数,而在命令行使用--build-arg
# 语法: ARG <name>[=<default value>] # 示例Dockerfile: FROM nginx ARG CONF="/tmp/nginx.conf" LABEL Author=xm RUN touch "${CONF}" # 构建镜像: [root@docker ~]# docker build --build-arg CONF='/etc/test.conf' -t nginx:v15.2 ./ Sending build context to Docker daemon 225.6MB Step 1/4 : FROM nginx ---> f09fe80eb0e7 Step 2/4 : ARG CONF="/tmp/nginx.conf" ---> Using cache ---> ac081589c644 Step 3/4 : LABEL Author=xm ---> Using cache ---> 53b9b0ba4460 Step 4/4 : RUN touch "${CONF}" ---> Running in 50debe96f876 Removing intermediate container 50debe96f876 ---> d8680a2433bc Successfully built d8680a2433bc Successfully tagged nginx:v15.2 # 运行容器查看: [root@docker ~]# docker run --rm nginx:v15.2 ls /etc/test.conf -l -rw-r--r-- 1 root root 0 Feb 27 11:18 /etc/test.conf
''''
用于在Dockerfile中定义一个触发器,当制做出来的镜像被别人用于基础镜像时候自动触发。
语法: ONBUILD [INSTRUCTION] 解释: INSTRUCTION: # 指令能够是RUN 、COPY等
注意事项:
''''
用于在image中建立一个挂载目录,以挂载宿主机上的目录
语法: VOLUME <path> VOLUME ["path"] 解释: path:表明容器中的目录,与docker run 不一样, Dockerfile中不能指定宿主机目录,默认使用docker管理的挂载点 示例: VOLUME ["/var/log/“] VOLUME /myvol
''''
在构建镜像过程当中,咱们可能只须要某些镜像的产物,好比在运行一个go程序须要先go程序包编译后才运行,若是在一个镜像里面完成,先要通过安装编译环境,程序编译完再安装运行环境,最后运行程序,这样的镜像体积每每比较大,不利于咱们使用。而真正咱们须要的镜像是只有程序包和运行环境,编译环境的构建在运行容器时候是不须要的,因此Docker提供了一种解决方案就是multi-stage(多阶段构建)。
''''
Docker容许多个镜像的构建可使用同一个Dockerfile,每一个镜像构建过程能够称之为一个stage,简单理解就是一个FROM指令到下一个FROM指令,而每一个stage可以使用上一个stage过程的产物或环境(其实还支持其余镜像的),这样一来,最终所得镜像体积相对较小。不只如此多阶段构建一样能够很方便地将多个彼此依赖的项目经过一个Dockerfile就可轻松构建出指望的容器镜像,而不用担忧镜像太大、项目环境依赖等问题。
''''
经过上述介绍,咱们能够在第一个stage将go程序编译获得编译后程序包,而后在第二个stage中直接拷贝编译好的go程序包到运行环境中,最后的镜像中就只有程序包和运行环境。如下做为示例:
FROM golang:1.7.3 WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=0 /go/src/github.com/alexellis/href-counter/app . CMD ["./app"]
''''
在以上Dockerfile中存在两个FROM指令,也就是两个stage,第一个stage用于构建产物,而在第二个stage中使用COPY --from=0 意思将第一个stage中的/go/src/github.com/alexellis/href-counter/app拷贝到.目录,第二个stage仅仅至关于执行copy就有了构建产物,不用在安装编译环境,镜像会很缩小。
''''
默认状况下,stage未命名,能够经过整数来引用它们,第一个stage表示0,第二个表1以此类推。 可是,当有多个stage时候,这样会显得麻烦,Docker提供AS 语法能够为stage命名:
FROM golang:1.7.3 as builder
而后在另外一个stage中使用:
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
''''
除了可使用Dockerfile中的stage外,构建镜像时候还能够直接使用本地已存在的环境和产物,例如:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
基础镜像尽可能选择比体积较小的镜像,如每一个官方发行的alpine镜像。虽然这版本镜像比较小,可是与之带来的是利用该类镜像运行的容器中排错的命令不多;
使用RUN指令时候,尽可能把多个RUN指令合并为一个,一般作法是使用&&符号;
经过multi-stage方法减小一些没必要要使用的环境来减少镜像;
安装完成软件同时删除一些不须要的文件或目录;