镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。
Union(联合)文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。
Docker 目前支持的联合文件系统包括 OverlayFS , AUFS , Btrfs , VFS ,ZFS 和 Device Mapper。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件体统包含所有底层的文件和目录
docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system) 主要包含bootloader和kernel,bootloader 主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就存在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
roorfs (root file system),在bootfs之上。包含的就是典型Linux系统中的 /dev ,/proc,/bin ,/etx 等标准的目录和文件。rootfs就是各种不同的操作系统发行版。比如Ubuntu,Centos等等。
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host(宿主机)的kernel,自己只需要提供rootfs就行了,由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载
# 下载镜像 docker pull 镜像名[:tag]
[@bjyf_50_20 ~]# docker pull mysql
Using default tag: latest #如果不写 tag,默认就是 latest
latest: Pulling from library/mysql
6ec7b7d162b2: Pull complete # 分层下载,docker image的核心 联合文件系统
fedd960d3481: Pull complete
7ab947313861: Pull complete
64f92f19e638: Pull complete
3e80b17bff96: Pull complete
014e976799f9: Pull complete
59ae84fee1b3: Pull complete
ffe10de703ea: Pull complete
657af6d90c83: Pull complete
98bfb480322c: Pull complete
9f2c4202ac29: Pull complete
a369b92bfc99: Pull complete
Digest: sha256:365e891b22abd3336d65baefc475b4a9a1e29a01a7b6b5be04367fcc9f373bb7 #签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest #真实地址
# 等价于
docker pull mysql
docker pull docker.io/library/mysql:latest
# 指定版本下载
docker pull mysql:5.7
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境的软件,包含了运行某个软件所需要的所有内容
基于UnionFS联合文件系统,采用分层结构,一层一层的堆叠起来,像一个同心圆,但,从外面来说,只能看到最外层的文件系统
分层结构好处:共享资源,便于复用(许多镜像都是从相同的Base基础镜像构建而来的,基础镜像只需要保存一份)
镜像都是只读的,而由镜像生成的容器是可以修改的
容器=镜像+读写层
为什么Docker镜像要采用这种分层的结构呢?
采用这种分层结构最大的一个好处就是共享资源,比如有多个镜像都从相同的base镜像构建而来,那么宿主机只需要在磁盘上保存一份base镜像,
同时内存中也只需要加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作 “容器层” ,“容器层” 之下的都叫镜像层。
查看镜像分层的方式可以通过 docker image inspect 命令
docker image inspect redis:latest
Docker镜像含有启动容器所需要的文件系统及其内容,因此,其用于创建并启动docker容器
采用分层构建机制,最底层为bootfs,其上为rootfs
bootfs:用于系统引导的文件系统,包括bootloader和kernel,容器启动完成后会被卸载以节约内存资源
rootfs:位于bootfs之上,表现为docker容器的根文件系统
传统模式中,系统启动之时,内核挂载rootfs时会首先将其挂载为“只读”模式,完整性自检完成后将其重新挂载为读写模式
docker中,rootfs由内核挂载为“只读”模式,而后通过“联合挂载”技术额外挂载一个“可写”层
位于下层的镜像称为父镜像(parent image),最底层的称为基础镜像(base image)
最上层为“可读写”层,其下的均为“只读”层
启动容器时,docker daemon会试图从本地获取相关的镜像,本地镜像不存在时,其将从Registry中下载该镜像并保存到本地
由特定的docker镜像的所有迭代版本组成的镜像仓库
一个Registry中可以存在多个Repository
每个仓库可以包含多个Tag(标签),每个标签对应一个镜像
维护用户账户、镜像的校验以及公共命名空间的信息
相当于为Registry提供了一个完成用户认证等功能的检索接口
基于已有镜像的更新、基于本地模板导入、基于Dockerfile构建
该方法是使用 docker commit
命令,其命令格式为:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
主要参数选项包括:
-a -author=”” 作者信息
-m -message=”” 提交信息
-p -pause=true 提交时暂停容器运行
示例
# 先使用基础镜像创建一个容器,然后对容器进行修改,最后使用commit命令提交为一个新镜像
# 步骤
# 1.根据基础镜像,创建容器
docker run --name mytomcat -p 8080:8080 -d tomcat
# 2.修改容器
docker exec -it mytomcat /bin/bash
/# cd /usr/local/tomcat/webapps/ROOT
/# rm -f index.jsp
/# echo welcome to tomcat > index.html
/# exit
# 3.提交为新的镜像,语法:docker commit -m="描述信息" -a="作者" 容器ID或容器名 镜像名:tag
docker ps -a
docker commit -m="修改默认索引页" -a="admin" 镜像ID harbor.adtech.com/os/tomcat:v1
docker inspect 镜像ID
# 4.使用新创建的harbor.adtech.com/os/tomat:v1镜像,生成一台容器实例:
docker run --name tomcat_v1 -p 8080:8080 -d harbor.adtech.com/os/tomcat:v1
docker exec -it tomcat_v1 /bin/bash
示例
docker run --name b1 -it busybox
mkdir -p /data/html
vi /data/html/index.html
Busybox welcome.
# 提交镜像,修改默认运行的命令(上面的容器不能关闭,需另开一个窗口制作镜像,新镜像默认打开html页面,而不是/bin/sh)
docker commit -a "Song " -c 'CMD ["/bin/httpd","-f","-h","/data/html"]' -p b1 Song/httpd:v0.1
docker image ls
# 可以打多个标签
docker tag Song/httpd:v0.1 Song/httpd:latest
# 打开自定义的容器
docker run --name t1 Song/httpd:v0.1
# 删除标签
docker image rm song/httpd:latest
ls /data/html
cat /data/html/index.html
wget http://download.openvz.org/template/precreated/ubuntu-18.04-x86_64-minimal.tar.gz
sudo cat ubuntu–18.04–x86_64–minimal.tar.gz | docker import -ubuntu:18.04
docker images
Dockerfile是一个包含创建镜像所有命令的文本文件,使用docker build命令可以根据Dockerfile的内容创建镜像
Dockerfile
是用来构建 Docker
镜像的文件,是由一系列命令和参数构成的脚本
Dockerfile
从 FROM
命令开始,紧接着各种命令、参数等,最终会生成一个新的镜像
使用 docker build
创建镜像时,需要使用 Dockerfile
文件自动化制作 image
镜像
Dockerfile
有点像源码编译时 ./configure
后产生的 Makefile
dockerfile
文件docker build
构建镜像docker run
运行镜像docker push
发布镜像(DockerHub、阿里云镜像仓库)FROM
,指定 Base image
基础镜像#
表示注释指定基础镜像,即构建新镜像是基于哪个镜像
FROM harbor.adtech.com/os/centos:7.4
指定作者姓名或邮箱地址(已弃用)
MAINTAINER [email protected]
设置镜像的标签
指定构建过程中要运行的 shell 命令 RUN
或 RUN ["executable","param1","param2"]
RUN rm -rf /etc/yum.repos.d/*
RUN ["/bin/bash","-c","echo hello"]
设置环境变量
ENV PATH $PATH:/usr/local/nginx/sbin
指定默认的工作目录,即进入容器后默认进入的目录,对于RUN,CMD,ENTRYPOINT、COPY、ADD
生效
WORKDIR /usr/local/nginx
创建挂载点,也称容器数据卷,用于数据共享和持久化
创建挂载点,无法指定宿主机上对应的目录,宿主机上的目录是自动生成的, /data1
和 /data2
表示容器中的两个目录VOLUME ["/data1","/data2"]
指定容器启动时要运行的命令,与RUN不同的是,这些命令不是在镜像构建过程中执行的支持三种格式
# 使用exec执行,推荐方式;
CMD ["executable","param1","param2"]
# 提供给ENTRYPOINT的默认参数
CMD ["param1","param2"]
# 在 /bin/sh 中执行,提供给需要交互的应用
CMD command param1 param2
注:指定启动容器时执行的命令,每个Dockerfile只能有一条CMD命令,如果指定了多条命令,只有最后一条会被执行
指定容器启动时要运行的命令,与 CMD 有区别
container 启动时执行的命令,但是一个 Dockerfile 只能有一条 ENTRYPOINT 命令,如果多条,则只执行最后一条,ENTRYPOINT 没有 CMD 的可替换特性
拷贝文件/目录到镜像中
格式为 COPY
。复制本地主机的
(为Dockerfile所在的目录的相对路径)到容器中的
。当使用本地目录为源目录时,推荐使用 COPY
拷贝文件到镜像中,且会自动解压缩
将文件
拷贝到 container 的文件系统对应的路径
。所有拷贝到 container 中的文件和文件夹权限为 0755, uid 和 gid 为 0
如果要 ADD 本地文件,则本地文件必须在 docker build
,指定的
目录下
如果要 ADD 远程文件,则远程文件必须在 docker build
,指定的
目录下 docker build github.com/creack/docker-firefox
ADD
只有在 build 镜像的时候运行一次,后面的 container
的时候不会在重新加载
声明容器运行的服务端口,即,指定对外暴漏的端口
EXPOSE 8022
Container 内部服务开启的端口,主机上要用还需在启动 container 时,做 host-container 的端口映射
docker run -d -p 127.0.0.1:8022:22 centos7-ssh
为 RUN、CMD、ENTRYPOINT 执行命令指定运行用户
容器中服务健康检查
HEALTHCHECK CMD /healthcheck.sh
设置编译镜像时加入的参数
当构建一个被继承的 Dockerfile 的时候就会运行 ONBUILD 指令,ONBUILD 是一个触发指令
设置容器的退出信号量
示例:自定义centos镜像
# 1.编写Dockerfile文件
vim Dockerfile2
FROM centos
ENV MYPATH /usr/local/centos
RUN mkdir -p $MYAPTH
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install wget
RUN yum -y install net-tools
EXPOSE 80
#创建挂载点,无法指定宿主机上对应的目录,宿主机上的目录是自动生成的,/data1 和 /data2表示容器中的两个目录
VOLUME ["/data1","/data2"]
CMD echo $MYPATH
CMD echo "---end---"
CMD ["/bin/bash"]
# 2.使用 docker build 构建镜像(注意后面的点号)
docker build -f Dockerfile2 -t harbor.adtech.com/os/centos:v1 .
# 3.使用 docker run 运行容器
docker run -it harbor.adtech.com/os/centos:v1
# 4.查看镜像的变更历史和构建过程
docker history 镜像ID或镜像名
# 5.验证挂载点(mounts)
docker inspect 镜像ID
示例:自定义 httpd 镜像
Dockerfile是一个包含创建镜像所有命令的文本文件,使用docker build命令可以根据DockerFile的内容创建镜像
# 1.创建Dockerfile文件
vim Dockerfile
# FROM:基于哪个(基础)镜像
FROM centos
# RUN:运行命令时使用,RUN 或 RUN ["executable","param1","param2"],
# 例如:RUN ["/bin/bash","-c","echo hello"]
RUN yum -y install httpd
#ADD 将文件拷贝到新产生的镜像的文件系统对应的路径。所有拷贝到新镜像中的文件和文件夹权限为0755,uid和gid为0
ADD start.sh /usr/local/bin/start.sh
ADD index.html /var/www/html/index.html
# container启动时执行的命令或启动服务,但是一个Dockerfile中只能有一条CMD命令,多条则只执行最后一条CMD.
CMD /usr/local/bin/start.sh
# 2.创建start.sh脚本启动httpd服务和apache默认首页index.html文件
echo "/usr/sbin/httpd -DFOREGROUND" > /usr/local/bin/start.sh
chmod a+x /usr/local/bin/start.sh
echo "docker image build test" > index.html
# 3.构建新的镜像,语法:docker built -f Dockerfile文件的路径 -t 镜像名:tag 命令执行的上下文
docker build -f Dockerfile -t harbor.adtech.com/os/tomcat:v2 .
# 注:Dockerfile构建镜像,在指定新镜像的名称和tag后面的点并不是Dockerfile的路径,这是指定镜像的上下文空间,
# 也就是当COPY和ADD路径使用时候的相对路径,Dockerfile默认为当前路径,可以使用-f参数另外指定。
# 4.使用新镜像运行容器
docker run -p 8099:8080 -d harbor.adtech.com/os/tomcat:v2
示例:操作系统 CentOS7 基础镜像制作
cat Dockerfile
FROM harbor.test.com/os/centos:7.4
MAINTAINER [email protected]
RUN rm -rf /etc/yum.repos.d/*
COPY yum.repos.d /etc/yum.repos.d/
RUN yum makecache \
yum -y install vim rsync cronie which sudo krb5-workstation \
yum -y install wget bzip2 openssh-server systemd net-tools iproute openssl098e \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENTRYPOINT ["/usr/lib/systemd/systemd"]
示例:操作系统 CentOS6 基础镜像制作
# 编写Dockerfile
cat Dockerfile
FROM centos:6.9
MAINTAINER [email protected]
RUN rm -rf /etc/yum.repos.d/*
COPY yum.repos.d /etc/yum.repos.d/
RUN yum makecache \
yum install -y vim rsync cronie which sudo krb5-workstation wget bzip2 openssh-server upstart openssl098e \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENTRYPOINT ["/sbin/init"]
#构建镜像
docker build -f Dockerfile -t harbor.test.com/os/centos:6.9 .
#上传镜像到私有仓库
docker push harbor.test.com/os/centos:6.9
#从私有仓库下载镜像
docker pull harbor.test.com/os/centos:6.9
#运行镜像
docker run --name lbs -d harbor.test.com/os/centos:6.9
#向容器中拷贝文件
docker cp /var/spool/cron/ lbs:/var/spool/cron/
#进入容器
docker exec -it lbs /bin/bash
共同点
都可以指定 shell
或 exec
函数调用的方式执行命令
当在 ``dockerfile 中存在多个 CMD
或 ENTRYPOINT
指令时,只有最后一条指令生效,所以一般只有一条 CMD
或 ENTRYPOINT
指令
不同点
CMD
指令可以被 docker run
之后的参数覆盖。而 ENTRYPOINT
指令指定的命令不会被覆盖,而是将 docker run
指定的参数当作 ENTRYPOINT
指定命令的参数
CMD
指令可以为 ENTRYPOINT
指令设置默认参数,而且可以被 docker run
指定的参数覆盖
验证1
当存在多个CMD或ENTRYPOINT指令时,只有最后一条生效
# 编写 dockerfile 文件
vim cmdfile
FROM centos
CMD ["/bin/ls","-a"]
CMD ["/bin/bash"]
# 构建镜像
docker build -f cmdfile -t harbor.adtech.com/test/cmdfile:v1 .
# 运行容器查看只执行了最后一条CMD,其他的CMD并未执行
docker run -it harbor.adtech.com/test/cmdfile:v1
验证2
CMD 指令可以被 docker run 之后的参数(/bin/pwd)覆盖,不再运行 /bin/bash
docker run -it harbor.adtech.com/test/cmdfile:v1 /bin/pwd
验证3
docker run 之后的参数(-l)会被作为 ENTRYPOINT 指令的参数,组合成新的命令(ls /usr/local -l)
# 编写 dockerfile 文件
vim entrypointfile
FROM centos
ENTRYPOINT ["/bin/ls","/usr/local"]
# 构建镜像
docker build -f entrypointfile -t harbor.adtech.com/test/entrypointfile:v1 .
# 运行容器
docker run -it harbor.adtech.com/test/entrypointfile:v1 -l
docker login -u Song
docker push Song/httpd
docker save -o myimages.gz Song/httpd:latest Song/httpd:v0.1
rsync -aP myimages.gz 10.134.40.86::root/tmp
docker load -i myimages.gz
Docker导入镜像
cat centos.tar | docker import - centos6
Docker导出镜像
docker export 容器id号 > cenos6.tar
在docker容器中运行hello world!
docker run centos:latest echo "hello world"
docker run 镜像名 echo "hello word"
在容器中安装ntpdate的程序
docker run centos yum install ntpdate