Docker学习3--镜像和仓库
2016-05-08 17:25 Qin奋 阅读(341) 评论(0) 收藏 举报1.Docker的镜像概念
Docker镜像是由文件系统叠加而成,最底端是一个引导文件系统,Docker镜像的第二层是root文件系统,即bootfs,位于引导文件系统之上。
bootfs可以是一种或多种操作系统。这很像典型的Linux/Unix的引导文件系统。(实际上一个容器启动后,它会被移到内存中,而引导文件系统则会被卸载(unmount),以留出更多的内存供initrd磁盘镜像使用。)
在传统的Linux引导过程中,root文件系统会最先以只读的方式加载,当引导结束并完成了完整性检查之后,它才会被切换为读写模式。
但是在Docker里,root文件系统永远只能是只读状态,并且Docker利用联合加载 union mount 技术又会在root文件系统层上加载更多的只读文件系统。
联合加载指的是一次同时加载多个文件系统,但是在外面看起来只能看到一个文件系统。联合加载会将各层文件系统叠加到一起,这样最终的文件系统会
包含所有底层的文件和目录。
Docker将这样的文件系统统称为镜像。一个镜像可以放到另一个镜像的顶部。位于下面的镜像叫父镜像parent image,以此类推直到镜像栈的最底部,最底部镜像成为base image.
当一个镜像启动容器时,Docker会在该镜像的最顶层加载一个读写文件系统。我们在Docker中运行的应用程序就是在这个读写层中执行的。
写时复制(copy on write) 是使Docker如此强大的技术之一。 要想理解Docker的镜像原理,需要对操作系统,和linux内核了解熟悉
2.Docker 镜像相关命令
2.1. docker images
列出镜像
docker images ubuntu 按照名称显示
本地镜像都保存在 宿主机的 /var/lib/docker目录下。每个镜像都保存在Docker所采用的存储驱动目录下面,如aufs或者devicemapper.
所有的容器都保存在/var/lib/docker/container下面
2.2. 镜像仓库 Registry。Docker公司运营的公共Registry服务叫 Docker Hub。镜像是保存在仓库中的,包括镜像,层以及镜像的metadata
一个镜像在仓库中可以有多个版本(同一个镜像名称)。Docker提供tag来标记版本或者代号,我们docker images 能看到同一个image ID 但不同tag
2.2.1.docker pull ubuntu: 按名称(从默认仓库Docker Hub)中拉取镜像。 结果是N个版本的镜像都被拉取下来了。
docker run -i -t --name new_container ubuntu /bin/bash 默认拉取latest版本的镜像
2.2.2.docker pull ubuntu:12.04
docker run -i -t --name new_container ubuntu:12.04 /bin/bash
指定版本拉取镜像
2.2.3.Docker Hub中有两种类型的仓库:用户仓库(user repository)和 顶层仓库(top repository)。用户仓库的镜像都是有Docker用户创建的,而顶层仓库则是由Docker内部的人来管理的
用户仓库的命令由用户名和仓库名两部分组成,如 jack/puppet. 用户名:jack 仓库名:puppet
3.构建Docker镜像
3.1 docker commit
构建镜像 (非主流)
(一般说来,我们不是正在“创建”新镜像,而是基于一个已有的基础镜像,如ubuntu或fedora等,构建新镜像而已。真正从零构建一个全新的镜像 https://docs.docker.com/articles/baseimages/) (案列:
[root@iZ236vrgbw9Z containers]# docker login
Username: jq82953
Password:
Email: qinjiayan@buaa.edu.cn
WARNING: login credentials saved in /root/.docker/config.json
Login Succeeded
root@iZ236vrgbw9Z containers]# docker run -i -t --name container001 ubuntu /bin/bash
root@1ff5efb563c8:/# apt-get -y install apache2
[root@iZ236vrgbw9Z containers]# docker commit 1ff5efb563c8 coconut_repo/apache2
bc343a5dcd089efd61f0ec8b63273e3a569426217e5cc6e7cbecc3d4cfcf5074
[root@iZ236vrgbw9Z containers]#
[root@iZ236vrgbw9Z containers]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
coconut_repo/apache2 latest bc343a5dcd08 3 minutes ago 206.5 MB
job1 latest a3e7568f348d 3 weeks ago 1.113 MB
ubuntu 14.04 41cc538fb83a 3 weeks ago 187.9 MB
[root@iZ236vrgbw9Z
containers]# docker commit -m="A new custom image" --author="Coconut
Man" \1ff5efb563c8 coconut_repo/apache2:webserver
c575324738a5cb5a7535209116e409a3ed2c6ec535d72a03492493ad1d926a67
[root@iZ236vrgbw9Z containers]#
[root@iZ236vrgbw9Z containers]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
coconut_repo/apache2 webserver c575324738a5 31 seconds ago 206.5 MB
coconut_repo/apache2 latest bc343a5dcd08 8 minutes ago 206.5 MB
)
3.2 用Dockerfile 和 docker build来构建镜像
3.2.1 docker build -t="coconut_repo/static_web" .
-t 为新镜像设置了仓库和名称。 仓库名称为coconut_repo,镜像名称为:static_web. "." 表示从当前目录下加载Dockerfile
docker build -t="coconut_repo/static_web:v1" .
为新镜像指定标签
docker build -t="coconut_repo/static_web" \git@github.com:jamtur01/docker-static_web
指定一个Git仓库的原地址作为Dockerfile的位置。这里前提是Docker在这个Git仓库的根目录下存在Dockerfile文件
3.2.2 Dockerfile案列
(# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER Jiayan Qin "qinjiayan@buaa.edu.cn"
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Hi, I am in your container' \>/usr/share/nginx/html/index.html
EXPOSE 80)
FROM指令指定一个已经存在的镜像,后续指令都将基于该镜像进行,该镜像成为base image. 每个Dockerfile的第一条指令都应该是FROM.
MAINTAINER镜像的创建者信息
此后的三条RUN 指令会在当前镜像中运行指定的命令。
EXPOSE告诉Docker 该容器内的应用程序将会使用容器的指定端口。但是出于安全考虑,要能访问容器的这个端口,还要使用docker run创建运行容器时指定打开这些端口。Docker并不会默认打开他们。
3.2.3 docker build 输出案列
([root@iZ236vrgbw9Z static_web]# docker build -t="coconut_repo/static-web:v1" .
Message from syslogd@iZ236vrgbw9Z at May 4 20:41:49 ...
kernel:unregister_netdevice: waiting for lo to become free. Usage count = 2
Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> 41cc538fb83a
Step 1 : MAINTAINER Jiayan Qin "qinjiayan@buaa.edu.cn"
---> Using cache
---> 19400e6a67dc
Step 2 : RUN apt-get install -y nginx
---> Running in 3fb40e0aa47f
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
fontconfig-config fonts-dejavu-core geoip-database libfontconfig1)
Dockerfile中的每条指令都会被顺序执行,而且作为构建过程的最终结果,返回了新镜像的ID。构建的每一步机器对应的指令都会独立运行,并且在输出最终镜像ID之前,Docker会提交每步的构建结果。
3.2.4 Dockerfile 运行中的某一步失败(如 apt-get install -y ngin)
解决方法: Docker run -t -i 容器ID(已经成功的最后一个创建的容器) /bin/bash
然后再在容器中运行一次正确的命令:apt-get install -y nginx
3.2.5 docker build --no-cache -t="repository/image_name"
在build的过程中不缓存,也不用之前的缓存。 默认dockerfile中每一个RUN的结果都会被缓存。
3.3.6 docker run -d -p 80 --name static_web coconut_repo/static-web:v1 \nginx -g "daemon off;"
-d 告诉Docker以分离 detached 的方式在后台运行。这种方式适合运行类似Nginx守护进程这样的需要长时间运行的进程。
\nginx -g "daemon off;" 将以前台运行的方式启动Nginx,来作为Web服务器。
-p 用来控制Docker(容器)在运行时应该公开那些网络端口给外部(宿主机)。
(Docker可以通过两种方法在宿主机上分配端口:1.(Docker自动)在宿主机选择一个49000~49900的一个大端口号来映射到容器的80端口。2.(我们)在Docker宿主机指定一个具体端口映射容器80端口)
3.2.7 docker run -d -p 8080:80 --name static_web coconut_repo/static-web:v1 \nginx -g "daemon off;"
将容器的80端口绑定到宿主机的8080端口
3.2.8 docker run -d -p 127.0.0.0:80:80 --name static_web coconut_repo/static-web:v1 \nginx -g "daemon off;"
将容器的80端口绑定到宿主机的127.0.0.1这个IP的80端口
3.2.9 docker run -d -p 127.0.0.0::80 --name static_web coconut_repo/static-web:v1 \nginx -g "daemon off;"
将容器的80端口绑定到宿主机的127.0.0.1这个IP的一个随机端口
3.2.10 docker run -d -P --name static_web coconut_repo/static-web:v1 \nginx -g "daemon off;"
将容器的80端口对宿主机公开并绑定到一个随机端口。-P同时会将Dockerfile中EXPOSE的其他端口一并公开
关于端口:https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/#network-port-mapping/refresher
3.3 Dockerfile 指令
(Dockerfile定义的要运行的命令放在一个数组结构,运行CMD+参数 的时候其实会被转换为 "/bin/sh -c"+参数)
3.3.1 CMD ["/bin/true"]
CMD 指令用于指定一个容器启动时要运行的命令。作用等价于 docker run -i -t container_name /bin/true
CMD 与 RUN 类似, RUN是指定镜像被构建时要运行的命令
docker run 命令会覆盖CMD指令。也就是前者优先级比较高。
3.3.2 ENTRYPOINT ["/usr/sbin/nginx"]
类似于CMD,也是让容器在启动时执行某个命令。但是不会被docker run覆盖,而且docker run 的参数会被传给ENTRYPOIN。
比如Dockerfile中定义了ENTRYPOINT ["/usr/sbin/nginx"], 创建容器时docker run -it some_container -g "daemon off"。容器启动时则会运行ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off"]
确实需要的话--entrypoint参数可以覆盖ENTRYPOINT
3.3.3 WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp/
ENTRYPOINT [ "RACKUP" ]
WORKDIR指令用来从镜像创建容器时,在容器内部设置一个工作目录,CMD或者ENTRYPOINT指定的程序会在这个目录下执行。
以上的列子,ENTRYPOINT [ "RACKUP" ]最终会在/opt/webapp/中执行。WORKDIR可以重置指定的执行目录。
通过-w能覆盖工作目录。 例:docker run -it -w /var/log ubuntu pwd /var/log 该命令将容器的工作目录社会为/var/log
3.3.4 ENV RVM_PATH /home/rvm/
ENV指令用来在镜像构建过程中设置环境变量。设置的变量可以在后续任何的RUN中使用 RUN gem install unicorn ==>转换成 RVM_PATH =/home/rvm/ gem install unicorn执行(不是很理解??)
其他指令也可以声明使用。 如:1.ENV TARGET_DIR /opt/app/; 2.WORKDIR $ARGET_DIR
-e可以在docker run的时候传递环境变量。如:docker -it -e "TARGET_DIR= /opt/app/" somecontainer
3.3.5 USER nginx; USER user; USER user:group; USER uid; USE user:gid; USE user:group;
USER指令用来指定镜像以什么样的用户去运行
-u 可以在运行容器时 docker run -u 来覆盖
3.3.6 VOLUME ["/opt/project"]; VOLUME ["/opt/project", "/data"]
VOLUME指令用来向基于镜像创建的容器添加卷 一个卷是可以存在一个或者多个容器内的特定的目录,这个目录可以绕过联合文件系统,并提供如下共享数据或者对数据
进行持久化的功能:卷可以在容器间共享和重用; 一个容器可以不是必须和其他容器共享卷; 对卷的修改是即时生效的且痐对更新镜像产生影响; 卷会一直存在直到没有容器再使用它;
卷功能让我们可以将数据(如源代码),数据库或者其他内容添加到镜像中而不是提交到容器中,并且允许我们在多个容器中共享这些内容。我们可以
用此功能来测试容器和内部的应用城西代码,管理日志,或者处理容器内部的数据库
关于卷:https://docs.docker.com/engine/userguide/dockervolumes/
3.3.7 ADD software.lic /opt/application/software.lic
ADD指令用来将构建环境下的文件或者目标复制到镜像中。上面的命令会将构建目录下的software.lic文件复制到镜像中的/opt/application/software.lic.
源目录或者文件必须在构建目录下或者Dockfile上下文中。
ADD http://wordpress.org/latest.zip /root/worldpress.zip
源参数还可以是一个URL
ADD latest.tar.gz /war/www/wordpress
如果源是归档文件(压缩文件),ADD命令会将归档文件latest.tar.gz解压到目标地址/war/www/wordpress下。效果和 tar -x命令一样,如果目标地址下已经有同名文件,不会覆盖。
ADD 指令会让构建缓存变得无效。
3.3.8 COPY conf.d/ /etc/apache2
COPY指令非常类似于ADD,但COPY只复制遇到归档文件不解压。文件源路径必须是一个与当前构建环境相对的文件或者目录,也就是源文件放到Dockerfile同一目录下,不能复制该文件之外的文件。
因为构建环境会上传到Docker守护进程,而COPY动作是在守护进程中进行。所以构建环境之外的东西都不可见。COPY指令的的美乐迪位置必须是容器内部的一个绝对路径。
3.3.9 ONBILD ADD . /app/src
ONBUILD 指令能为镜像添加触发器trigger。当一个镜像被用作凄然镜像的基础镜像时,该镜像中的触发器将会被触发。且是在新镜像的FROM指令之后执行ONBILD指令的内容。隔代继承镜像,基础镜像ONBIULD会触发。
4. 关于卷 VOLUME(p98)
3.3.6提到了卷,卷在Docker里非常重要,也很有用。 卷是在一个或者多个容器内被选定的目录,可以绕过Union File System, 为Docker提供持久数据或者共享数据。
这意味着对卷的修改会直接生效,并绕过镜像。当提交或者创建镜像时,卷不被包含在镜像里。
卷可以在容器间共享。即使容器停止,卷里的内容依旧存在。
卷的应用场景:
希望同时对代码做开发和测试;
代码改动很频繁,不想在开发工程中重构镜像
希望在多个容器中共享代码
docker run -d -p 80 --name website \-v $PWD/website:/var/www/html/website \jiayan_prac01/nginx nginx
-v 这个参数执行了卷的源目录(本地宿主机的目录)和容器里的目录,这两个目录通过:来分隔。如果目的目录不存在,Docker会自己创建一个。
rw 和 ro 参数可以用来指定目的目录的读写状态,如:docker run -d -p 80 --name website \-v $PWD/website:/var/www/html/website:ro \jiayan_prac01/nginx nginx
这将使目的目录:/var/www/html/website 变成只读状态。
浙公网安备 33010602011771号