Doker 镜像是由文件系统叠加而成
当Docker第一次启动一个容器时,初始的读写层是空的。当文件系统发生变化时,这些变化都会应用到这一层上。如果,你修改一个文件,这个文件首先会从该读写层下面的只读层复制到该读写层。该文件的只读版本依然存在,但是已经被读写层中该文件副本所隐藏。
这种机制被称为写时复制。每个只读镜像层都是只读的,并且以后永远不会变化。每当创建一个新容器时,Docker会构建出一个镜像栈,并栈的最顶端添加一个读写层。这个读写层再加上其下面的镜像层以及一些配置数据,就构成了一个容器。
列出容器
docker images
docker pull ubuntu // 下载或获取ubuntu仓库下所有内容
docker pull centos:5.11 //下载指定版本的centos
镜像都是从仓库里下载下来的
Docker Hub 仓库有两种类型,用户仓库(个人管理)和顶层仓库(由Docker内部人来管理)
查找镜像
docker search centos
构建镜像
有两种方式:
- 使用docker commit 命令(不推荐使用)
- 使用docker build命令和Dockerfile文件(功能强大且更灵活)
一般来说,我们并不是真正「创建镜像」,而是基于一个已经有基础镜像,如ubuntu或fedora等,构建新镜像而己
先在Docker Hub上注册个账号
dcoker login
使用commit命令创建镜像
我们先创建一个容器,并在容器内修改,然后提交为一个新镜像
docker run -i -t centos /bin/bash
yum -y update //更新源
yum install httpd //安装 apache
exit //退出容器
ps -l -q 查到最新容器的id
docker commit 99289abe5d1a keithfu/apache //提交镜像
docker images keithfu/apache //查看已经创建的容器
docker commit只会提交差异的部分。
提交的时候,可以添加更多的参数
docker commit -m="说明" --author="作者" 99289abe5d1a keithfu/apache:webserver
docker inspect keithfu/apache:webserver
docker push //提交到hub上
用Dockerfile构建镜像
创建Dockerfile文件如下:
touch Dockerfile
#version 0.0.1
FROM ubuntu:14.04
MAINTAINER Keith Fu "18612352157@163.com"
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
执行build命令
docker build -t "keithfu/static_web" .
-t用来设置 新镜像的仓库和名称,也可添加标签
docker build -t "keithfu/static_web:v1" .
最后的.是告诉Docker 去当前目录去找Dockerfile文件
docker build -t "keithfu/static_web:v1" git@git.oschina.net:studay/css-exercise.git
Dockerfile由一系列指定和参数组成,每个指令都必须 为大写且后面紧跟一个参数,这些指定会安顺序执行。
每条指令都会创建一个新的镜像层对镜像提交
开表示注释
每个Dockerfile的第一条指令都应该是FROM,指定一个已经在存在的镜像,称之为基础镜像
MAINTAINER 指令,会告诉Docker该镜像的作者是谁,以及作者的电子邮件地址。
EXPOSE 告诉Docker该容器内的应用程序将会使用容器的指定端口,但Docker并不会自动打开该端口,需要在run运行容器时来指定需要打开哪些端口。
每一步都产生一个镜像,且有ID
还是很兴奋,push成功了,网速还是可以的
从新镜像启动容器
docker run -d -p 80 --name static_web keithfu/static_web nginx -g "daemon off"
上面 -d选项告诉Docker以分离的方式在后台运行。这种方式非常适合类似Nginx守护进程这样需要长期运行的里程。同时我们也指定了需要在容器中运行的命令:nginx -g "daemon off"
。这将以前台运行的方式启动Nginx,来用作web服务器。
-p标志,用来控制Docker在运行时应该公开哪些网络端口给外部的宿主机。运行一个容器时,Docker可以通用两种方式来在宿主机上分配端口:
- Docker可以在宿主机上随机选择一个19000~49900的一个比较大的端口号来映射到容器中的80端口上
- 可以在Docker宿主机中指定一个具体的端口号来映射到容器中的80端口上。
我们上面的例子是在随机打一个端口,这个端口会连接到80端口上,可以使用docker ps
命令来看一下容器的端口分配情况:
我们也可以使用docker poort
来查看容器的端口映射情况。
docker port keithfu/static_web
docker port keithfu/static_web 80 //查看具体的端口的绑定情况
docker run -d -p 127.0.0.1:80:80 --name static_web keithfu/static_web nginx -g "daemon off;"
按上面的方式,我们可以具体指明宿主的端口号,但这样,如果多个容器运行,只能有一个能成功。
如果 -p 不指定任何,则会公开Dockerfile中的EXPOSE指指令中设置的所有端口
成功运行
Dockerfile 和构建缓存
每一步 的构建过程都会将结果提交为镜像,所以Docker的构建镜像过程就显得非常聪明,它会将之前的镜像层看做缓存。如上例子,如会只 在4步发生了变化 ,那么前3步不会重新构建 ,而是直接进行第四步。那么说第三步中的apt-get update ,不会在刷新APT包的缓存。但有的时候,你f却不想这样,需要略过缓存。可以使用 docker build --no-cache
标起
基于构建缓存的Dockerfile模板
看如下:
FROM ubuntu:14.04
MAINTAINER KeithFu
ENV REFRESHED_AT 2016-12-07
RUN apt-get -qq update
我们用ENV设置环境变量,如果刷新一个构建,只需要修改ENV指令中的日期,这样后续指令而无须依赖缓存。