build 构建一个镜像 读取配置文件构建image(文件名默认Dockerfile,也可以自定义): 1)”-“表示将从STDIN读入数据,即将Dockerfile中的数据通过STDIN传入build中。1
Docker build - < Dockerfile
2)PATH参数表示Dockerfile全路径,即将Dockerfile放置到$(HOME)/Users目录下。1
2
Docker build $(HOME)/Users/Dockerfile
Docker build $(HOME)/Users/otherfile 名字可变
3)使用URL参数,URL必须指向一个git地址。Docker会把指定的git仓库克隆到本地,然后把仓库中的Dockerfile内容传给Docker Daemon。1
Docker build https://github.com/wszhi/Docker-image.git#container:Docker
以上命令通知Docker Daemon加载https://github.com/wszhi/Docker-image.git 仓库,切换到container分支,在Docker目录下的Dockerfile文件来构建image。 注意: “:”后面的是Dockerfile文件的路径,此方式不支持自定义命名。若为master分支#后为空 若当前目录有Dockerfile文件可以省略路径使用1
Docker build --rm=false .
–rm=false表示不删除临时镜像,当经常构建且变化不大的时候建议使用。
build 构建镜像三要素 build命令构建镜像是用Dockerfile文件中的指令来构建的,三个要素为:Dockerfile文件、所涉及文件和镜像参数。1
Docker build -t image_name .
默认执行Dockerfile文件。
build命令执行的主要逻辑 1)获取Dockerfile文件——客户端 2)加载Dockerfile文件所在目录下的所有文件——-客户端 3)解析Dockerfile文件指令 4)执行文件内指令,判断是否存在有效缓存镜像 5)执行指令
其中获取Dockerfile文件的方式有三种: 1.通过标准输入传入,参数”-“; 2.通过远程方式获取,git; 3.通过指定路径方式获取。
build命令构建镜像原理 客户端将Dockerfile所需要的文件和镜像参数传递给Daemon。 然后Daemon处理客户端传来的各项参数(为后续的解压操作和文件I/O操作做准备)。 当参数处理完成之后,Daemon开始解压文件,抽取文件。 最后Daemon解析Dockerfile,生成指令语法树。 之后Daemon使用解析后的指令从已经初始化的指令——函数对应表中找到对应的处理函数,最后执行函数。
流程: (客户端) 加载Dockerfile—>压缩构建目录—>添加镜像参数——发送构建请求—-> (服务端) —->生成构建参数—>获取压缩包—>解压文件—>解析Dockerfile生成语法树—>初始化指令处理对照表—>循环执行指令—>删除解压文件—>构建完成
Dockerfile Dockerfile 文件记录着用户”创建”镜像过程中需要执行的所有命令,Docker可以读懂的脚本文件。 镜像指的是一组特定的文件层,镜像的构建过程就是Docker执行Dockerfile所定义命令而形成这组文件层的过程。
Dockerfile 内置命令 FROM 指明基础镜像名称,必填
FROM <image>
FROM <image>:<tag>
FROM <iamge>@<digest>
当同时构建多个image时,可以出现多次FROM,但是只会返回最后一个镜像的ID,前几个会被标记为:
MAINTAINER 备注信息,包括作者、版本等,可选填
MAINTAINER 作者
RUN 用于执行后面的命令,当RUN执行完毕后,将产生一个新的文件层,可选填
# 当镜像中有/bin/sh
RUN <command>
# 当镜像中没有/bin/sh,使用以下方案,可以执行基础镜像中任意一个二进制程序,注意只能用双引号
RUN ["/bin/bash","-c","echo hello"]
镜像最多只能保存126个文件层,包括基础镜像的文件层;执行一次RUN就会产生一个文件层。
CMD 指定此镜像启动时默认执行命令,可选填
#推荐用法,其设定的命令将作为容器启动时的默认执行命令
CMD ["executable","param1","param2"]
#param 将作为ENTERPOINT的默认参数使用
CMD ["param1","param2"]
#将后面的命令作为shell命令,依靠/bin/sh -C 来执行
CMD command param1 param2
example: CMD echo "This is a test" | wc -
一个Dockerfile可以有多个CMD命令,可是只有最后一个CMD命令生效,CMD中也只能出现双引号,如果使用环境变量使用sh -C。
LABEL 在镜像中添加元数据。例如版本号、构建日期等,可选填
LABEL <key>=<value> <key>=<value> <key>=<value> ...
使用键值对的形式来向镜像文件中添加元数据的命令 如果键值对中存在空格,则需要使用双引号来回避错误 一个LABEL命令也会产生新的文件层
LABEL "description"="just for test" "version"="1.0"
EXPOSE 指定需要暴露的网络端口号,可选填
EXPOSE 8080
CMD ["catalina.sh","run"]
当容器运行时,来通知Docker这个容器中哪些端口是应用程序用来监听的,这些端口不会被外部网络访问到,只能被主机的其他容器访问。
ENV 在镜像中添加环境变量,可选填
# 第一个字符为key,后面所有字符为value
ENV <key> <value>
左边为key,右边为value,value有空格时,需要"\"转义或者使用双引号
ENV <key>=<value> ....
ADD 向镜像添加新文件或者新目录,可选填 将src标记的文件,添加到容器中的dest所标记的路径中去。src标记的文件可以是本地文件,可以是本地目录,甚至可以是URL链接。 src标记的是本地文件或者目录时,其相对路径应该是相对于Dockerfile所在目录的路径,而dest则应该指向容器中的目录。如果这个目录不存在,当ADD命令执行时,将会在容器中自动创建此目录。 在src标记的路径中,允许使用通配符。而dest路径不能使用通配符,必须是绝对路径,或者相对于WORKDIR的相对路径。
#添加所有以hom开头的文件
ADD hom* /mydir/
# ? 号可以被任意单个字符所代替
ADD hom?.txt /mydir/
规则: 1.src指定路径必须存在于Dockerfile所在目录。因为在Dockerfile执行时,Docker daemon会读取Dockerfile所在目录的所有数据。如果ADD命令使用的文件在此目录中不存在,那么daemon将找不到指定文件。 2.如果src指定的是URL,并且dest所指定的路径没有以”/“结尾,那么URL下载的数据将直接覆盖dest所给定的文件。 3.如果src指定的是URL,并且dest所指定的路径以”/“结尾,那么URL下载后的数据将直接写入dest所指定的目录中。 4.如果src指向一个目录,那么ADD指令将包括元数据在内的所有数据复制到容器中dest指定的文件中,但src所指定的目录本身不会被复制进去,只会复制此目录下的文件。 5.如果src指向的是一个已知格式的压缩文件。当添加到容器之后会自动执行解压缩动作。从而从URL中下载的压缩文件则不会执行解压缩。 6.如果src使用通配符指定了多个文件,那么此时dest必须是一个以”/“结尾的目录。 7.如果dest指向的路径没有以”/“结尾,那么这个路径指向的文件将会被src指定的文件覆盖。 8.如果dest指向的路径不存在,那么此路径中所涉及的父级目录都将会被创建。 9.当src指向的URL没有下载权限时,首先需要使用RUN wget或者RUN curl获取文件。 10.当ADD命令所标记的文件发生变化时,从变化的那个ADD命令开始,保存在缓存中的镜像将会实效,同时RUN命令产生的镜像也会失效。COPY 从主句镜像复制文件,可选填 向容器中指定的路径下添加文件。
#添加所有以hom开头的文件
COPY hom* /mydir/
# ? 号可以被任意单个字符所代替
COPY hom?.txt /mydir/
1-1 2-4 3-6 4-7 5-8 6如果使用STDIN输入Dockerfile内容,那么Docker命令将会失效。
ENTRYPOINT 在镜像中设定默认执行的二进制程序,可选填 ENTRYPOINT用来设定容器运行时默认执行程序的命令。
#第一种,推荐使用,可以自行设定需要执行的二进制程序和参数。
ENTRYPOINT ["executable","param1","param2"]
#第二种,将所设定的二进制程序限制在/bin/sh -C 下执行。
ENTRYPOINT command param1 param2
ENTRYPOINT可以出现多次,只有最后一次起作用。 CMD指定的值将作为参数附加到ENTRYPOINT里的命令之后。如果run命令后面添加了其他参数,此时的CMD指定的参数将会失效。 第二种用法,ENTRYPOINT 命令设定的二进制程序将会忽略所有来自于CMD和RUN命令后面所添加的所有参数,只会运行ENTRYPOINT命令所设定的二进制程序。 为了确保容器可以正确处理stop命令发来的SIG信号,Docker建议使用exec来启动二进制程序。
VOLUME 向镜像挂载一个卷组,可选填
VOLUME ["/data"]
VOLUME可以在容器内部创建一个指定名称的挂载点。 如果以及在Dockerfile里声明了某个挂载点,那么以后对此挂载点种文件的操作将不会生效。一般在Dockerfile结尾处声明挂载点。
FROM nginx
COPY content /usr/share/nginx/html
COPY conf /etc/nginx
VOLUME /usr/share/nginx/html
VOLUME /etc/nginx
构建镜像
Docker build -t mynginximage .
创建容器
Docker run --name mynginx -P -d mynginximage
Docker run -it --volumes-from mynginx --name mynginxfiles debian /bin/bash
–volumes-from,从mynginx继承了配置文件信息,此时在mynginxfiles当中只能看到继承文件的信息而无法看到其他文件。在这个容器中可以任意编写配置文件而不会影响其他文件和容器。USER 在镜像构建过程中,生成或者切换到另外一个用户,可选填
USER daemon
切换用户身份,当执行完USER命令后,其后面所有的命令都将以新用户的身份来执行。
WORKDITR 设定此镜像后续操作的默认工作目录,可选填
WORKDIR /path/to/workdir
WORKDIR是用来切换当前工作目录的指令。切换后的路径影响后续的指令中的路径。 WORKDIR可以在Dockerfile出现多次,但最终生效的路径是所有WORKDIR指定的路径的叠加。 如果需要切换到其他的工作目录,那么应该使用全路径进行切换。如果使用相对路径,默认在当前目录中切换。 在WORKDIR中只可以使用ENV设定的环境变量值
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
结果为/path/$DIRNAME
ONBUILD 配置构建触发指令集,可选填 由ONBUILD创建的触发命令集在当前Dockerfile执行过程中不会执行,而当此镜像被其他镜像当作其他镜像当作基础镜像使用时,将会被执行。
Dockerfile1
FROM tomcat:7.0.72-jre7
MAINTAINER WSZ
ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]
LABEL "description"="just for test" "version"="1.0"
ENV myName="Shengzhi Wang" mySoft="Docker image build"
RUN mkdir /myvolume
RUN echo "hello world" > /myvolume/greeting
VOLUME /myvolume
CMD echo "This is a test" | wc -
EXPOSE 8080
CMD["catalina.sh","run"]
Dockerfile2
FROM tomcat:7.0.72-jre7
MAINTAINER WSZ
ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]
WORKDIR /usr/local/tomcat/webapps
RUN rm -rf ROOT
WORKDIR /usr/local/tomcat/conf/Catalina/localhost
RUN echo "<?xml version='1.0' encoding='utf-8'?>" > ROOT.xml
RUN echo "<Context path='/show_books' docBase='/build_image_share/show_books_base_on_angularJS_Underscore_master' debug='0' privileged='true' reloadable='true'/> " >> ROOT.xml
# RUN echo "<Context path='/shoppingweb' docBase='/build_image_share/shoppingweb' debug='0' privileged='true' reloadable='true'/> " >> ROOT.xml
LABEL "description"="just for test" "version"="1.0"
ENV myName="Shengzhi Wang" mySoft="Docker image build"
CMD /usr/local/tomcat/bin/catalina.sh run
build_image_share里面有一个命名为show_books_base_on_angularJS_Underscore_master的war包,只有网页没有集成数据库。 然后在同目录下运行docker build -t showbooks . 生成镜像后运行docker run -d -p 8888:8080 showbooks 在本机访问localhost:8080查看结果
Dockerfile3
FROM tomcat:7.0.72-jre7
MAINTAINER WSZ
ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]
WORKDIR /usr/local/tomcat/webapps
RUN cp /build_image_share/shoppingweb.war shoppingweb.war
WORKDIR /usr/local/tomcat/conf
RUN sed -i~ '/<\/Host/i <Context path="/" docBase="shoppingweb.war" debug="0" privileged="true" reloadable="true"/>' server.xml
CMD /usr/local/tomcat/bin/catalina.sh run
Dockerfile 优化方案 1.容器要尽可能短小精悍,在写Dockerfile之前确定容器要提供的服务,要可以快速的启动和停止,同时用最少的步骤来配置容器服务。 2.多使用.Dockerignore文件,当加载文件时过滤某些文件。 当Docker加载文件时,如果发现目录存在.Dockerignore文件,那么此目录中符合.Dockerignore文件中定义规则的文件将会被过滤掉,不会被加载到缓存中。 ./Dockerfile文件必须放到Dockerfile所在目录下面。./Dockerfile文件中的路径是以./Dockerfile所在目录为起点的。 3.不要安装非必需的软件包 4.一个容器尽量只运行一种服务 为了达到容器可以快速部署的目的,应该尽量每个容器只对外提供一种服务。当容器需要其他服务时,通过link的方式从其他容器获取服务,而不是在当前容器再启动另外一种服务。 5.控制文件层数量 文件层过多时,创建容器将会耗费更多的时间。 可以将相似的命令放到同一个RUN指令中运行,以减少文件层数量。但是命令合并的太厉害,会影响到Dockerfile中RUN指令的可读性,导致Dockerfile的维护成本很高。 要在文件层数量和可维护性之间找到平衡。 6.对命令中的参数进行排序
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
subversion
对参数排序以后,可以避免下载重复的软件包,同时增添软件包时更为容易和控制,提高Dockerfile的可读性和可维护性。 默认规则,每行最后加空格和\ 7.尽可能多使用缓存中的数据 在Dockerfile执行过程中,Docker会讲某些命令的执行结果保存成临时镜像,保存在缓存中。在下次执行Dockerfile时,Docker会在执行每一条指令之前检测此条指令缓存值是否有效。只有当缓存没有或者缓存失效时,才会再次执行此条指令。 若是不希望使用缓存中的结果,通过–no-cache=true来关闭此项功能。 Docker加载完基础镜像之后,会对比由此基础镜像所派生出来的所有子镜像。对比规则就是看这些子镜像是否由Dockerfile后面的命令所创建的,如果后面的命令发生了变化,那么缓存自动失效。 单纯的比较指令是否发生变化来判定缓存是否有效。ADD和COPY命令会检查其所涉及的文件是否发生了变更,标准是计算文件的校验和,而且只是校验镜像中的数据,不校验容器中的数据。 一旦缓存的镜像失效,那么此缓存镜像以后的所有镜像都自动失效。
某些命令 Delete all stopped containers docker rm $( docker ps -q -f status=exited)Delete all dangling (unused) images docker rmi $( docker images -q -f dangling=true)安装mysql server RUN apt-get update \ && apt-get install -y mysql-servermysql server启动 docker run –name my-container-name -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql/mysql-server:tag重启mysql service service mysql restart mysql -u root -p
在容器外进入mysql docker exec -it mysql mysql -uroot -p加一个目录存数据 docker run –name mysql2 -v /Users/szwang/DockerForMe/build_image/init_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql/mysql-server
例子 基于原先做的Shoppingweb项目,数据库是Mysql 修改root-context.xml相关的数据库的内容
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://mysql:3306/shoppingdb?useUnicode=true&characterEncoding=UTF-8" />
<property name="username" value="shop" />
<property name="password" value="123456" />
</bean>
Dockerfile文件的内容如下:
FROM tomcat:7.0.72-jre7
MAINTAINER WSZ
ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]
WORKDIR /usr/local/tomcat/webapps
RUN cp /build_image_share/shoppingweb.war shoppingweb.war
WORKDIR /usr/local/tomcat/conf
RUN sed -i~ '/<\/Host/i <Context path="/" docBase="shoppingweb.war" debug="0" privileged="true" reloadable="true"/>' server.xml
CMD /usr/local/tomcat/bin/catalina.sh run
template_envfile的内容如下:
MYSQL_ROOT_PASSWORD=123456
MYSQL_ROOT_HOST=mysql
MYSQL_DATABASE=shoppingdb
MYSQL_USER=shop
MYSQL_PASSWORD=123456
在命令行里跑1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
docker pull mysql/mysql-server
docker run --name mysql2 --env-file ./template_envfile -d mysql/mysql-server
docker exec -it mysql mysql -uroot -p
docker run --name shoppingwithlinkmysqlserver -d -p 8080:8080 --env-file ./template_envfile --link mysql:mysql shoppingweb:2.0
参考书籍 《Docker 全攻略》张涛著 该本书有很长的篇幅介绍docker的命令《Docker 官方文档》 mysql/mysql-server in Docker Using Docker to spin up databases for development Creating a MySQL Docker Container