docker 的简单使用
docker 简介
Flux7 Docker 系列教程(一):Docker 简介 主要看英文版的好.
Docker — 从入门到实践
层叠的只读文件系统, 联合加载.
Docker 3 个组件/3 个元素
3 个组件是 Docker Client, Docker Daemon, Docker index/Registry.
3 个元素是 Docker Container, Docker Image, DockerFile.
他们如何交互呢?



- Docker Client:用户和 Docker 守护进程进行通信的接口,也就是 docker 命令。
- Docker Daemon 守护进程:宿主机上用于用户应答用户请求的服务。
- Docker Index:用户进行用户的私有、公有 Docker 容器镜像托管,也就是 Docker 仓库。
- Docker constainer 容器:用于运行应用程序的容器,包含操作系统、用户文件和元数据。
- Docker image 镜像:只读的 Docker container 容器模板,简言之就是系统镜像文件。
- DockerFile:进行镜像创建的指令文件。
在学习 Docker 组件之前,先来看一下 Docker 底层到底是由什么组成的(暂时别看)
- Namespace:隔离技术的第一层,确保 Docker 容器内的进程看不到也影响不到 Docker 外部的进程。
- Control Groups:LXC 技术的关键组件,用于进行运行时的资源限制。
- UnionFS(文件系统):容器的构件块,创建抽象层,从而实现 Docker 的轻量级和运行快速的特性。
如何运行咯
运行任何应用都必须按照以下两个步骤来: 先有镜像再有容器, container 就是一个 image 的实例 instance
- 创建一个 image 镜像文件
- 运行 container 容器
1. 创建一个镜像文件
Docker image相当于一个只读的模板文件,保存着运行container所需要的所有的配置、文件;每次启动,都会以基础的 Docker image为模板,按照 Dockerfile 的指令,建立一个新的适用于你自己的 Dokcer image:实际上是在这个基础镜像上建立了一个新的应用层。
自己创建的 Docker image可以推送到 Docker Index 中心,然后提供给他人使用。
2. 运行容器
container被运行后,会在原有的image上创建一个可读写的层,容器设置完毕网络之后便可以运行应用了。
docker 本身的启动, 本身就可以看做是一个守护进程 service docker start/stop/restart,在/etc/default/docker配置
查看 docker 运行没, ps -ed | grep docker 或 status docker
重点看 3 个概念 image container registry
在说下概念
Docker 镜像
我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
Docker 容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
Docker Registry
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
镜像
获取, 查看, 创建, 删除
- 获取镜像
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]- 不给用户名会默认指定是 library
- 使用
docker run -it --rm ubuntu:18.04 bash进入后可以用exit或ctrl+d退出并停止容器.- 这里主要是配合容器看
- 列出顶层
docker image ls [option] [名[:tag]]或docker images有个 s 和没有 s 的区别- 看到
<none>的是 虚悬镜像(dangling image) - 用 -f 指定过滤
docker image ls -f dangling=true删除这些没用的虚悬镜像docker image prune - 列出中间(所有镜像)
docker image ls -a - 只列出 id
docker image ls -q常配合删除用 - 自定义 ls 出来的表格格式
docker image ls --format ""
- 看到
- 删除
docker image rm [选项] <镜像1> [<镜像2> ...]- 镜像可以是 镜像短 ID、镜像长 ID、镜像名 或者 镜像摘要, 查看摘要
docker image ls --digests - 删除行为分为两类,一类是 Untagged,另一类是 Deleted. 镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签. 无标签指向, 无镜像依赖和无容器依赖时就是 deleted
- 批量删除, 配合 ls 一起用
docker image rm $(docker image ls -q redis)
- 镜像可以是 镜像短 ID、镜像长 ID、镜像名 或者 镜像摘要, 查看摘要
创建的 dockerfile 后面说, docker commint 不用
容器
查看, 启动和停止, 删除
- 启动: 有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动
docker run -t -i ubuntu:18.04 /bin/bashdocker container start将一个已经终止的容器启动运行
- 后台运行
docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"加个 -d 参数docker container ls看容器信息- 获取容器的输出信息
docker container logs [container ID or NAMES]和docker logs一样
- 终止
docker container stop来终止一个运行中的容器. 此外,当 Docker 容器中指定的应用终结时,容器也自动终止。- 只启动了一个终端的容器,用户通过
exit命令或Ctrl+d来退出终端时,所创建的容器立刻终止. 所以加参数 -d 和进入后再推出是有区别的 docker container ls -a可以看终止状态的docker container restart命令会将一个运行态的容器终止,然后再重新启动它
- 只启动了一个终端的容器,用户通过
- 进入容器: 在使用 -d 参数时,容器启动后会进入后台。
- 使用
docker attach命令或docker exec命令 - attach 的 如果用 exit,会导致容器的停止
docker exec -it 69d1 bash用 exit,不会导致容器的停止
- 使用
- 删除
docker container rm trusting_newtondocker container ls -a命令可以查看所有已经创建的包括终止状态的容器,docker container prune清理掉所有处于终止状态的容器
- 导入导出:
docker export和docker importdocker load这个是镜像存储文件, 前面的 容器快照文件将丢弃所有的历史记录和元数据信息
仓库
仓库(Repository)是集中存放镜像的地方, 一个容易混淆的概念是注册服务器(Registry)
docker login [地址]和docker logoutdocker search centos和docker pull
- 打完 tag 后 push
docker tag ubuntu:18.04 username/ubuntu:18.04,docker push username/ubuntu:18.04
自动构建
数据和网络
数据就是 volume, 数据卷和主机目录
- 查看
docker volume lsdocker volume inspect my-vol- 在容器中
docker inspect web的"Mounts"字段
- 创建
docker volume create my-vol - 删除
docker volume rm my-voldocker volume prune数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令
- 在创建容器时挂上卷 -v
--mount source=my-vol,target=/webapp缩写-v my-vol:/wepapp
上面是创建了数据卷挂上, 还有是不用创建, 就是挂主机目录
--mount type=bind,source=/src/webapp,target=/opt/webapp简写-v /src/webapp:/opt/webapp- 默认是读写的, 可以指定只读
-v /src/webapp:/opt/webapp:ro
- 默认是读写的, 可以指定只读
所以只需要用 -v 就好, 也不用指定 type 了
网络就是参数 -p
Docker 的 14 个命令,注意区分 image 和 container

docker info: 检查 Docker 是否安装, 显示 Docker 信息docker versiondocker images: 检查本机有多少个 docker image list, top level 的.-a看完整的, 包括中间层 images,-q只看 iddocker inspect看 top level layer 的 image 详情 MD 信息,也可以看 container, 如 IP,port, 配置信息docker search <imageName>: 在 docker index 搜索镜像-sstardocker pull <image name>: 下载镜像docker run busybox /bin/echo Hello worrld: 运行 container,(-d -p) 是docker create和docker start结合, 创建 container 并运行 containerdocker create:是为指定 image 添加一个可读写层,构成一个新的 container. 从 image => container.docker start: 是为 container 文件系统创建了一个进程隔离空间, 每个容器只有一个. 里面跑不跑进程随意.docker ps: 列出运行中的 container, -a 是所有不运行的也列出, 就是只看start不看create的咯和docker container ls,docker container ls --alldocker logs <ID/>name>: 查询输出,docker stop <ID/>name>: 停止 container,优雅退出,回收进程空间 start 和 stop 可以多个docker kill <ID/>name>直接杀.docker container stop这么写太繁琐ctl+p ctl+q: 将容器后台, 也就是变为守护, attach,exit后ctl+d: 退出容器docker start/restart <ID/>name>: 运行/重启 containerdocker attach重新进入-d 的守护容器docker exec: 在运行中的 container 中(有了进程空间的)运行 command, 执行进程docker rm <ID/>name>: 删除构成 container 的可读写层,要先 stop.docker rmi <ID/>name>是删 top level layer image,docker rmi $(docker images -q): 删了所有层 . 就是docker container rm和docker image rm简写docker commit <ID/>name> <newImageName>: 保存 contaner 为新 image(将可读写层变为只读层, 无论容器运不运行, 注意层的 id 会变),docker build: 从一个 DockerFile 中建立 image(里面是 4 步,先 FROM 获取到 image, 然后重复地 1.docker run新建一层读写,然后分配进程空间, 再 2.RUM执行命令 RUN 其实就会新建一个读写层的 , 3.docker commit保存这层为只读层), 两种方式都可以构建镜像.-t是打 tag. 类似git每一个修改都是一个commit, 保存记录.docker history <imageName>: 获取镜像历史(只能是本地)docker push <user>/<repo_name>: 推送镜像.docker login-u -p和docker logout:注意登录 id 不是邮箱, 然后这个 id 就算你注册的时候填了大写的, 注册成功后还都是小的, 看一下.(登不上就直接去 UI 桌面中登录好了)docker help和docker <command> --help: 帮助docker update: 更新 contain 而配置docker rename重命名 container,也可以在docker run的时候指定 namedocker top: 列出一个 container 中的运行的进程.docker pause暂停一个或多个 container 中的所有进程docker unpause, 保留进程空间docker port: 列出 container 的端口映射docker tag image username/repository:tag: 给 image 打 tag,保留原始镜像,docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]就是打一个 image 名字是username/repositorytag 是tag
暂时不看的命令
docker import和docker export:export会把容器打tar,但会移除不必要的 MD 和 image 层, 只输出一个层,很好用. 只能看到一个 image, save 能看到历史镜像docker save和docker load: 使用load从stdin导入一个tar格式的镜像或者仓库,然后用save将tar镜像输出到stdout。save会把一个镜像,只能是镜像,打tar并保留每一层的 MD 信息. 两个命令就是方便在别的机器上用docker cp: 从 container 中复制进去文件docker deploy和docker diff: 看 container 中文件改动docker eventsdocker statsdocker wait

自动化 DockerFile
Docker 提供的 Dockerfile 是一个类似 Makefile 的工具,主要用来自动化构建镜像。既然能自动化创建镜像,那么我们何必去手动创建镜像呢。
格式
Dockerfile 中所有的命令都是以下格式:INSTRUCTION argument
指令(INSTRUCTION)不分大小写,但是推荐大写。
FROM 命令
FROM <imageName>,例如 FROM ubuntu
所有的 Dockerfile 都用该以 FROM 开头,FROM 命令指明 Dockerfile 所创建的 image 文件以什么镜像为基础,FROM 以后的所有指令都会在 FROM 的基础上进行创建镜像;可以在同一个 Dockerfile 中多次使用 FROM 命令用于创建多个 image。
MAINTAINER 命令
MAINTAINER <authorName> 用于指定镜像创建者和联系方式。
例如:
1 | MAINTAINER Victor Coisne victor.coisne@dotcloud.com |
RUN 命令
RUN <command> 用于 container 内部执行命令。每个 RUN 命令相当于在原有的 image 基础上添加了一个改动层,原有的镜像不会有变化, 运行完后再提交成只读层. 即类似先docker run再docker commit
ADD 命令
ADD <src> <dst> 用于从将 <src> 文件复制到 <dst> 文件:<src> 是相对被构建的 DockerFile 的相对路径,可以是文件或目录的路径,也可以是一个远程的文件 url,<dst> 是 container 中的绝对路径。
COPY 命令
只能讲和 Dockerfile 文件同目录中的内容复制到 container 中的指定目录,通常是WORKDIR指定的工作目录,
如: Copy the current directory contents into the container at /app
1 | COPY . /app |
暂时只要这个命令是改动 1 个层, 不会有 Running 的那个层
/var/lib/docker/tmp/docker-builderXXXXXXX/… no such file or directory #1922
WORKDIR只是在 image 内设置路径
1 | docker build -t hello-demo-app . |
which sets the current directory as the context, let’s say you wanted the parent directory as the context, just use:
1 | docker build -t hello-demo-app .. |
CMD 命令
CMD 命令有 3 种格式:
CMD ["executable","param1","param2"]:推荐使用的 exec 形式。CMD ["param1","param2"]:无可执行程序形式CMD command param1 param2:shell 形式。
CMD 命令用于启动 container时默认执行的命令,即docker run时的默认命令, 但如果docker run加了命令, 会覆盖CMD的. 而ENTRYPOINT不会被覆盖, 要覆盖需要docker run --entry加参数
,CMD 命令可以包含可执行文件,也可以不包含可执行文件:不包含可执行文件的情况下就要用 ENTRYPOINT 指定一个,然后 CMD 命令的参数就会作为ENTRYPOINT的参数。
一个
Dockerfile中只能有一个CMD,如果有多个,则最后一个生效。CMD的 shell 形式默认调用/bin/sh -c执行命令。CMD命令会被Docker命令行传入的参数覆盖:docker run busybox /bin/echo Hello Docker会把CMD里的命令覆盖。
ENTRYPOINT 命令
ENTRYPOINT 命令的字面意思是进入点,而功能也恰如其意:他可以让你的 container 表现得像一个可执行程序一样。
ENTRYPOINT 命令也有 2 种格式:
ENTRYPOINT ["executable", "param1", "param2"]:推荐使用的 exec 形式ENTRYPOINT command param1 param2:shell 形式
一个 Dockerfile 中只能有一个
ENTRYPOINT,如果有多个,则最后一个生效。
关于 CMD 和 ENTRYPOINT 的联系请看下面的例子
仅仅使用 ENTRYPOINT:
1 | FROM ubuntu |
执行 docker run 306cd7e8408b /etc/fstab 和 docker run 306cd7e8408b 结果并不会有什么差别:
1 | 命令 # docker run 306cd7e8408b /etc/fstab |
但是我们通常使用 ENTRYPOINT 作为 container 的入口,使用 CMD 给 ENTRYPOINT 增加默认选项:
1 | FROM ubuntu |
然后执行这个 constainer:
不加参数便会默认有 -l参数:
1 | 命令 # docker run 89dc7e6d0ac1 |
加了 /etc/fstab 参数便会覆盖原有的 -l 参数:
1 | 命令 # docker run 89dc7e6d0ac1 /etc/fstab |
EXPOSE 命令
EXPOSE <port> [<port>...] 命令用来指定 container 对外开放的端口。
例如 EXPOSE 80 3306,开放 80 和 3306 端口, 但在docker run的时候还得在 host 上指定端口的
WORKDIR 命令
WORKDIR /path/to/work/dir 配合 RUN,CMD,ENTRYPOINT 命令设置指令执行时的工作目录。
可以设置多次,如果是相对路径,则相对前一个 WORKDIR 命令。默认路径为/。
会分 2 步变动层
例如:
1 | FROM ubuntu |
docker run ID 得到的结果为:/usr/lib
USER 命令
USER <UID/Username> 为 container 内指定 CMD RUN ENTRYPOINT 命令运行时的用户名或 UID。
VLOUME 命令
VOLUME ['/data'] 允许 container 访问容器的目录、允许 container 之间互相访问目录。VOLUME 仅仅是允许将某一个目录暴露在外面,更多的操作还需要依赖 Docker 命令实现。
更多的内容可以参考 深入理解 Docker Volume(一)
ENV 命令: Define environment variable
参考 export 的用法咧:ENV LC_ALL en_US.UTF-8
最佳实践
所有应用都会有个最佳的方式,Dockerfile 也不例外,下面是我们总结出的最佳实现方式:
- 把维护者和更新系统的命令依次写在最上方
- 使用标签管理 Dockerfile
- 避免映射公共端口
- 使用类似 array 形式的 CMD 和 ENTRYPOINT
注:映射端口并不属于 Dockerfile 的工作范围。
概念: 10 张图带你深入理解 Docker 容器和镜像
Image Definition
Image 就是一堆只读层(read-only layer)的统一视角
Container Definition
容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
container 只是在 image 上增加一层读写层, 并没有提及容器是否在运行.
要点:容器 = 镜像 + 读写层。并且容器的定义并没有提及是否要运行容器。
Running Container Definition
一个运行态容器(running container)被定义为一个可读写的统一文件系统加上隔离的进程空间和包含其中的进程。
正是文件系统隔离技术使得 Docker 成为了一个前途无量的技术。一个容器中的进程可能会对文件进行修改、删除、创建,这些改变都将作用于可读写层(read-write layer)。
Image Layer Definition
为了将零星的数据整合起来,我们提出了镜像层(image layer)这个概念。下面的这张图描述了一个镜像层,通过图片我们能够发现一个层并不仅仅包含文件系统的改变,它还能包含了其他重要信息。
元数据(metadata)就是关于这个层的额外信息,它不仅能够让 Docker 获取运行和构建时的信息,还包括父层的层次信息。需要注意,只读层和读写层都包含元数据。
除此之外,每一层都包括了一个指向父层的指针。如果一个层没有这个指针,说明它处于最底层。
区别点
docker run与docker start的区别
docker run 与 docker start 的区别,为容器命名docker run 只在第一次运行时使用,将镜像放到容器中,以后再次启动这个容器时,只需要使用命令docker start 即可。docker run相当于执行了两步操作:将镜像放入容器中(docker create),然后将容器启动,使之变成运行时容器(docker start)。
而docker start的作用是,重新启动已存在的镜像。也就是说,如果使用这个命令,我们必须事先知道这个容器的ID 或名字,我们可以使用docker ps找到这个容器的信息。
docker run和docker exec的区别
“docker run”VS“docker exec”,这两个命令有区别吗?docker run通常是在新创建的容器中所使用的命令. 它适用于在没有其他容器运行的情况下,您想要创建一个容器,并且要启动它,然后在其上运行一个进程。如果镜像已经在容器中了, 用docker start或docker restartdocker exec适用于在现有容器中运行命令的情况。如果已经拥有了一个正在运行的容器,并希望更改该容器或从中获取某些内容,那么使用docker exec命令就非常合适了。
docker ps和docker images
一个看 containers, 一个看 imagesdocker ps 命令会列出所有运行中的 container。这隐藏了非运行态容器的存在,如果想要找出这些容器,我们需要使用docker ps -adocker images命令会列出了所有顶层(top-level)image。实际上,在这里我们没有办法区分一个镜像和一个只读层,所以我们提出了top-level镜像。只有创建容器时使用的镜像或者是直接 pull 下来的镜像能被称为顶层(top-level)镜像,并且每一个顶层镜像下面都隐藏了多个镜像层。
registry和repository
registry 是 docker 提供的线上的那个 index,包含 repository, repository 是本地中所有 image,类似 git 的库,github 是 registry
RUN和CMD
RUN是构建 image 时, 那一层 image 执行什么命令,而CMD是构建完 image 后,成为一个 container 后的命令
CMD和ENTRYPOINT
CMD命令用于启动 container时默认执行的命令,即docker run时的默认命令, 但如果docker run加了命令, 会覆盖CMD的. 而ENTRYPOINT不会被覆盖, 要覆盖需要docker run --entry加参数
- 远程访问
就是 docker client 和 docker deamon 不在同一个 host 上, 平时通过 docker 命令访问 docker client 然后 docker client 会访问 deamon. 现在你可以直接通过远程 REST API 访问 docker deamon
- detach 和 attach 区别
ctl+d退出, attach再回到运行的容器中
- run 中 link 和 name 的区别
—name 只是别名, —link 是指定链接到那个容器
参考
10 张图带你深入理解 Docker 容器和镜像
Flux7 Docker 系列教程(一):Docker 简介
Docker — 从入门到实践 666
Docker 联合文件系统(Union Filesystem)