Docker容器虚拟化技术—Docker高级实战(DockerFile)
DockerFile是一个文本格式的配置文件,用户可以使用DockerFile来快速创建自定义的镜像。
1. DockerFile基本结构
Dockerfile由一行行命令语句组成,并且支持以#开头的注释行
Dockerfile分为四部分:基础镜像信息、维护者信息、 镜像操作指令和容器启动时执行指令
比如:
# This Dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu:18.04 #基础镜像信息
MAINTAINER docker_user docker_user@email.com # 维护者信息
COPY . /app # 镜像操作指令
RUN make /app #容器指令
CMD python /app/app.py # 在容器里运行次命令
每条指令创建一层:
FROM : 指明所基于的镜像名称,从ubuntu:18.04Docker镜像创建一个图层
MAINTAINER : 维护者信息,docker_user维护(可以不写)
COPY : 从Docker客户端的当前目录添加文件,镜像操作指令
RUN : 构建镜像时执行make命令,每运行一条RUN指令,镜像就添加新的一层,并提交(添加可写层)
CMD : 指定在容器中运行什么命令,用来指定运行容器时的操作命令
Docker镜像由只读层组成,每个只读层代表一个Dockerfile指令
这些层是堆叠的,每个层都是上一层的变化的增量
Docker可以通过读取Dockerfile指令来自动构建镜像
完整的小例子:
#1. 在一个空目录下,新建一个名为 Dockerfile 文件
mkdir /usr/dockerfile -p
#2. 编辑 dockerfile
vim dockerfile-demo
# 3. 编辑dockerfile文件
FROM nginx:latest
# 维护者 可以省略
MAINTAINER dabing dabing@docker.com
#启动容器
RUN mkdir /usr/share/nginx/html/ -p
RUN echo Hello DockerFile! > /usr/share/nginx/html/demo.html
#4. 构建镜像 . : 根据当前上下文环境构建
docker build -f dockerfile-demo -t dabing/nginx:v1 .
#5. 运行
docker run --rm -d -it --network host dabing/nginx:v1
# 6. 访问tomcat进行验证,失败,因为nginx未启动
# 7. 将dockerfile文件中的最后一行CMD 命令删除
# 8.终止容器并删除构建的镜像
# 9.浏览器访问
2. DockerFile指令详解
Dockerfile命令官方文档
https://docs.docker.com/engine/reference/builder/
常见命令详解:
1. FROM
指定所创建镜像的基础镜像,如果本地不存在,则默认会去Docker Hub下载指定镜像。
命令格式如下:
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
平时不用这么复杂的,只需如下即可
语法:
FROM <image>[:<tag>]
例如:
FROM centos:7.6.1810
任何Dockerfile中的第一条指令 必须 为FROM指令。并且,如果在同 一个Dockerfile中创建多个镜像,可以使用多个FROM指令(每个镜像一 次)
2. MAINTAINER
指定维护者信息,格式为MAINTAINER。可以不写
MAINTAINER dabing dabing@docker.com
该信息会写入生成镜像的Author属性域中
3. RUN
运行指定命令
格式为
1、RUN
默认将在shell终端中运行命令,即/bin/sh-c
2、RUN["executable","param1","param2"]。
指令会被解析 为 Json数组 ,因此必须用双引号。
exec执行,不会启动shell环境
指定使用其他终端类型可以通过此方式实现,例如
RUN["/bin/bash","-c","echo hello"]
每条RUN指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。
当命令较长时可以使用\来换行在shell形式中,可以使用\(反斜杠)将一条RUN指令继续到下一行。
例如,考虑以下两行:
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
它们在一起等效于以下这一行:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
要使用’/bin/sh’以外的其他 shell ,请使用 exec 形式传入所需的 shell 。例如:
RUN ["/bin/bash", "-c", "echo hello"]
注意 在JSON格式中,必须转义反斜杠。在Windows中,反斜杠是路径分隔符,这一点尤其
重要。由于无效的JSON,以下行否则将被视为shell形式,并以意外的方式失败:
RUN [“c:\windows\system32\tasklist.exe”]
此示例的正确语法为:
RUN [“c:\windows\system32\tasklist.exe”]
4. CMD
CMD指令用来指定启动容器时默认执行的命令。它支持三种格式:
CMD[“executable”,“param1”,“param2”]使用exec执行,是推荐使用的方式;
CMD [ "sh", "-c", "echo $HOME" ]
CMD command param1 param2在/bin/sh中执行,提供给需要交互的应用;
CMD echo $HOME
CMD[“param1”,“param2”]提供给ENTRYPOINT的默认参数
CMD [ "echo", "$HOME" ]
每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行
如果用户启动容器时手动指定了运行的命令(作为run的参数), 则会覆盖掉CMD指定的命令
docker run --rm centos ping 192.168.1.1
5. COPY
6. ENV
7. ADD
8. ENTRYPOINT
9. VOLUME
10. WORKDIR
11. EXPOSE
3. DockerFile创建镜像
编写完成Dockerfile之后,可以通过docker build命令来创建镜像。
基本的格式为docker build[选项]内容路径,该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下的所有内容发送给Docker服务端,由服务端来创建镜像
如果使用非内容路径下的Dockerfile,可以通过-f选项来指定其路径
$ docker build -f /path/to/a/Dockerfile .
要指定生成镜像的标签信息,可以使用-t选项
docker build -t lagou/ubuntu:v1 .
docker build 最后的 . 号,其实是在指定镜像构建过程中的上下文环境的目录
4. DockerFile模版
编写建议:
- 从适当的基础镜像开始。例如,如果需要JDK镜像基于正式 openjdk 镜像
- 使用多阶段构建。例如,可以使用该 maven 镜像来构建Java应用程序,然后将其重置为该 tomcat镜像并将Java构件复制到正确的位置以部署我们的应用程序,所有这些操作都在同一 Dockerfile中。这意味着的最终镜像不包括构建所引入的所有库和依赖项,而仅包括运行它们所需的工件和环境。
- 通过最小化Dockerfile中 RUN 单独命令的数量来减少镜像中的层数。可以通过将多个命令合并为RUN 一行并使用Shell的机制将它们组合在一起来实现此目的。考虑以下两个片段。第一层在镜像中创建两层,而第二层仅创建一层。
例如:
#在镜像中创建两层
RUN apt-get -y update
RUN apt-get install -y python
#在镜像中创建一层
RUN apt-get -y update && apt-get install -y python
- 如果有多个具有很多共同点的图像,请考虑使用共享的组件创建自己的基础镜像,Docker只需要加载一次公共层,然后将它们缓存。这意味着我们的派生镜像将更有效地使用Docker主机上的内存,并更快地加载
镜像管理
Docker镜像由一系列层组成。每层代表镜像的Dockerfile中的一条指令。除最后一层外的每一层都是 只读 的。考虑以下Dockerfile:
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
该Dockerfile包含四个命令,每个命令创建一个层。
该 FROM语句首先从ubuntu:18.04镜像创建只读层。
该COPY命令从Docker客户端的当前目录添加一些文件。
该RUN命令使用命令构建您的应用程序make。
最后一层指定在容器中运行什么命令。 运行镜像并生成容器时,可以在基础层之上添加一个新的可写层("容器层")。对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此可写容器层。
容器和镜像之间的主要区别是可写顶层。在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中。删除容器后,可写层也会被删除。基础镜像保持不变。
因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础镜像的访问,但具有自己的数据状态。下图显示了多个共享相同Ubuntu 18.04镜像的容器
查看镜像的分层信息
docker history 镜像ID
磁盘上容器大小
要查看正在运行的容器的大致大小,可以使用以下 docker ps -s 命令。有两个不同的列与大小有关。
- size :用于每个容器的可写层的数据量(在磁盘上)。
- virtual size :容器使用的只读图像数据加上容器的可写层使用的数据量size。多个容器可以共享部分或全部只读图像数据。从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层。因此,您不能只对虚拟大小进行总计。这高估了总磁盘使用量,可能是一笔不小的数目。
我们可以通过Docker仓库来传输我们的镜像,也可以通过文件模式
# 将镜像存储成一个tar文件
docker save 镜像ID -o xxxx.tar 或(docker save xxxx > xxxx.tar)
# 加载tar文件,进行镜像的共享操作(或者走私有仓库进行push/pull操作也可以进行共享)
docker load -i xxxx.tar 或docker (docker load < xxxx.tar)
docker diff 容器ID
docker commit 容器ID svendowideit/testimage:version4 # 直接保存容器
docker commit --change='CMD ["apachectl", "-DFOREGROUND"]' -c "EXPOSE 80" 容器ID
svendowideit/testimage:version4 # 将正在运行的容器添加几个层之后再保存】
DockerFile模版
编写建议:
- 从适当的基础镜像开始。例如,如果需要JDK镜像基于正式 openjdk 镜像
- 使用多阶段构建。例如,可以使用该 maven 镜像来构建Java应用程序,然后将其重置为该 tomcat镜像并将Java构件复制到正确的位置以部署我们的应用程序,所有这些操作都在同一 Dockerfile中。这意味着的最终镜像不包括构建所引入的所有库和依赖项,而仅包括运行它们所需的工件和环境。
- 通过最小化Dockerfile中 RUN 单独命令的数量来减少镜像中的层数。可以通过将多个命令合并为RUN 一行并使用Shell的机制将它们组合在一起来实现此目的。考虑以下两个片段。第一层在镜像中创建两层,而第二层仅创建一层。
#在镜像中创建两层
RUN apt-get -y update
RUN apt-get install -y python
#在镜像中创建一层
RUN apt-get -y update && apt-get install -y python
- 如果有多个具有很多共同点的图像,请考虑使用共享的组件创建自己的基础镜像,Docker只需要加载一次公共层,然后将它们缓存。这意味着我们的派生镜像将更有效地使用Docker主机上的内存,并更快地加载。
CentOS镜像模版
得益于Docker镜像的分层技术,我们尽量将不变的构建指令放在Dockerfile较靠前的位置,常变的放在Dockerfile较下层的位置,这样后续重新构建镜像是就可以使用之前构建镜像的只读层版本了 之所以yum分了很多个 RUN 指令,是因为可能后期仍会安装部分功能,而后期安装的 RUN 指令,可以用之前的只读层,这样可以减少后续构建镜像的时间
注意:如果使用虚拟机,不要用NAT网络 要用网桥 (使用NAT装不上)
dockerfile-centos
# 依据哪个镜像创建
From centos:7.6.1810
# 指定容器内部使用语言
ENV LANG="en_US.UTF-8"
ENV LC_ALL="en_US.UTF-8"
# 使用亚洲/上海时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 创建工作目录
RUN mkdir -p /data/apps /data/data /data/logs ; ln -s /data/apps /apps
# 安装字体
RUN yum install -y epel-release
RUN yum groupinstall -y "fonts"
RUN yum install -y kde-l10n-Chinese
# 安装openssl等依赖包
RUN yum install -y openssl openssl-devel
RUN yum install -y crontabs cronolog ntp
# 安装数据库依赖
RUN yum install -y mariadb-libs
RUN ln -s /usr/lib64/mysql/libmysqlclient.so.18 /usr/lib64/libmysqlclient_r.so.16
RUN yum install -y gcc cmake
RUN yum install -y lrzsz telnet net-tools file bind-utils less
RUN yum install -y jq xml2; yum clean all
RUN yum install -y expat-devel apr-devel ghostscript ghostscript-devel
# 运行容器时的默认命令
CMD ["/bin/bash"]
构建命令:
docker build -f dockerfile-centos -t dabing/centos/7.6/centos .
运行镜像
docker run --rm -it dabing/centos/7.6/centos
JDK镜像模版
dockerfile-jdk
FROM dabing/centos/7.6/centos
ENV JAVA_HOME="/apps/jdk"
ENV PATH=${JAVA_HOME}/bin:$PATH
ADD ./jdk-8u251-linux-x64.tar.gz /apps/
RUN ln -s /apps/jdk1.8.0_251 /apps/jdk
ADD ./UnlimitedJCEPolicyJDK8/US_export_policy.jar /apps/jdk/jre/lib/security/
ADD ./UnlimitedJCEPolicyJDK8/local_policy.jar /apps/jdk/jre/lib/security/
ADD ./msyhbd.ttf /apps/jdk/jre/lib/fonts/
ADD ./msyh.ttf /apps/jdk/jre/lib/fonts/
CMD ["/bin/bash"]
构建命令:
因为需要加载本地的资源(jce_policy-8.zip、jdk-8u251-linux-x64.tar.gz、msyh.ttf、msyhbd.ttf)
因此需要在本地先下载好待使用的资源
#下载jce_policy-8.zip并解压到当前目录
wget http://pkgs-linux.cvimer.com/jdk/1.8/jce_policy-8.zip
unzip jce_policy-8.zip
#下载jdk-8u251-linux-x64.tar.gz、msyh.ttf、msyhbd.ttf
wget http://pkgs-linux.cvimer.com/jdk/1.8/jdk-8u251-linux-x64.tar.gz
wget http://pkgs-linux.cvimer.com/fonts/msyh.ttf
wget http://pkgs-linux.cvimer.com/fonts/msyhbd.ttf
#构建
docker build -f dockerfile-jdk -t dabing/centos/7.6/jdk/1.8/jdk .
Tomcat镜像模版
dockerfile-tomcat
FROM dabing/centos/7.6/jdk/1.8/jdk
ENV TOMCAT_HOME="/apps/tomcat"
ENV PATH=${TOMCAT_HOME}/bin:$PATH
RUN yum install -y gcc make expat-devel apr-devel; yum clean all ; rm -rf /var/cache/yum/*
ADD ./apr-1.6.5.tar.gz /root/
RUN cd /root/apr-1.6.5 && ./configure && make && make install && rm -rf /root/apr*
ADD ./apr-util-1.6.1.tar.gz /root/
RUN cd /root/apr-util-1.6.1 && ./configure --with-apr=/usr/local/apr && make && make install && rm -rf /root/apr*
ADD ./apache-tomcat-7.0.93.tar.gz /data/apps/
RUN ln -s /apps/apache-tomcat-7.0.93 /apps/tomcat
RUN cd /apps/tomcat/bin; tar xf tomcat-native.tar.gz; cd /apps/tomcat/bin/tomcat-native-1.2.21-src/native && \
./configure --with-apr=/usr/local/apr && make && make install
CMD ["/bin/bash"]
构建命令:
#下载包
wget http://pkgs-linux.cvimer.com/apr/1.6.5/apr-1.6.5.tar.gz
wget http://pkgs-linux.cvimer.com/apr/1.6.1/apr-util-1.6.1.tar.gz
wget https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.93/bin/apache-tomcat-7.0.93.tar.gz
# 构建
docker build -f dockerfile-tomcat -t dabing/centos/7.6/jdk/1.8/tomcat/7.0.9/tomcat .
# 运行
docker run --rm -it --network host dabing/centos/7.6/jdk/1.8/tomcat/7.0.9/tomcat
# 进入Docker
cd apps/tomcat/bin
# 启动tomcat
./startup.sh
访问