Docker
一、Docker作用与特征
docker中文官网传送门:https://www.docker-cn.com (自己看去)
- 速度飞快以及优雅的隔离框架
- 物美价廉
- CPU/内存的低消耗
- 快速开/关机
- 跨云计算基础构架
二、Docker组件与元素
三个组件分别是:
Docker Client(Docker客户端)是用户界面,它支持用户与Docker Daemon之间通信。Docker Daemon(Docker服务端)运行于主机上,处理服务请求。Docker Index(Docker仓库)是中央registry,支持拥有公有与私有访问权限的Docker容器镜像的备份。
三个基本要素分别是:
Docker Containers(Docker容器)负责应用程序的运行,包括操作系统、用户添加的文件以及元数据。Docker Images(Docker镜像)是一个只读模板,用来运行Docker容器。DockerFile(生成镜像的Docker文件)是文件指令集,用来说明如何自动创建Docker镜像。

三、Docker安装
docker下载与安装教程传送门:http://get.daocloud.io (windows10安装完后记得去BIOS界面开启cpu虚拟化)
四、Docker容器生命周期

- docker create: 容器进入初建成状态,此时只是建成了容器并没有启动
- docker start: 容器进行启动状态
- docker rm : 容器销毁进入deleted状态,若没记录则状态不可见。见上图,容器在运行时不可直接remove
- docker run : docker create+ docker start
- docker stop: 容器进入停止状态。当容器OOM(内存溢出)、流程执行结束会根据策略进行重启或者stop
- docker kill: 强制杀死容器,容器进行停止状态
- docker pause: 容器进入暂停状态,此时容器将暂停运行
- docker unpause: 容器从暂停状态转入运行状态
五、Docker网络模型

docker容器的网络有五种模式:
-
bridge模式,–net=bridge(默认)
这是dokcer网络的默认设置,为容器创建独立的网络命名空间,容器具有独立的网卡等所有单独的网络栈,是最常用的使用方式。 在docker run启动容器的时候,如果不加–net参数,就默认采用这种网络模式。安装完docker,系统会自动添加一个供docker使用的网桥docker0,我们创建一个新的容器时, 容器通过DHCP获取一个与docker0同网段的IP地址,并默认连接到docker0网桥,以此实现容器与宿主机的网络互通。
-
host模式,–net=host
这个模式下创建出来的容器,直接使用容器宿主机的网络命名空间。 将不拥有自己独立的Network Namespace,即没有独立的网络环境。它使用宿主机的ip和端口。
-
none模式,–net=none
为容器创建独立网络命名空间,但不为它做任何网络配置,容器中只有lo,用户可以在此基础上,对容器网络做任意定制。 这个模式下,dokcer不为容器进行任何网络配置。需要我们自己为容器添加网卡,配置IP。 因此,若想使用pipework配置docker容器的ip地址,必须要在none模式下才可以。
-
其他容器模式(即container模式),–net=container:NAME_or_ID
与host模式类似,只是容器将与指定的容器共享网络命名空间。 这个模式就是指定一个已有的容器,共享该容器的IP和端口。除了网络方面两个容器共享,其他的如文件系统,进程等还是隔离开的。
-
用户自定义:docker 1.9版本以后新增的特性,允许容器使用第三方的网络实现或者创建单独的bridge网络,提供网络隔离能力。
六、Docker Volume(数据卷)
Docker中的数据可以存储在类似于虚拟机磁盘的介质中,在Docker中称为数据卷(Data Volume)。数据卷可以用来存储Docker应用的数据,也可以用来在Docker容器间进行数据共享。
数据卷呈现给Docker容器的形式就是一个目录,支持多个容器间共享,修改也不会影响镜像。使用Docker的数据卷,类似在系统中使用 mount挂载一个文件系统。
-
一个数据卷是一个特别指定的目录,该目录利用容器的UFS文件系统可以为容器提供一些稳定的特性或者数据共享。数据卷可以在多个容器之间共享。
-
创建数据卷,只要在docker run命令后面跟上-
v参数即可创建一个数据卷,当然也可以跟多个-v参数来创建多个数据卷,当创建好带有数据卷的容器后,就可以在其他容器中通过–volumes-froms参数来挂载该数据卷了,而不管该容器是否运行。也可以在Dockerfile中通过VOLUME指令来增加一个或者多个数据卷。 -
如果有一些数据想在多个容器间共享,或者想在一些临时性的容器中使用该数据,那么最好的方案就是你创建一个数据卷容器,然后从该临时性的容器中挂载该数据卷容器的数据。这样,即使删除了刚开始的第一个数据卷容器或者中间层的数据卷容器,只要有其他容器使用数据卷,数据卷都不会被删除的。
-
不能使用docker
export、save、cp等命令来备份数据卷的内容,因为数据卷是存在于镜像之外的。备份的方法可以是创建一个新容器,挂载数据卷容器,同时挂载一个本地目录,然后把远程数据卷容器的数据卷通过备份命令备份到映射的本地目录里面。如下:docker run -rm --volumes-from DATA -v $(pwd):/backup busybox tar cvf /backup/backup.tar /dat -
也可以把一个本地主机的目录当做数据卷挂载在容器上,同样是在docker run后面跟- v参数,不过-v后面跟的不再是单独的目录了,它是[host-dir]:[container-dir]:[rwro]这样格式的, host-dir是一个绝对路径的地址,如果host-dir不存在,则docker会创建一个新的数据卷,如果host-dir存在,但是指向的是一个不存在的目录,则docker也会创建该目录,然后使用该目录做数据源。
Docker Volume数据卷可以实现:
-
绕过“拷贝写”系统,以达到本地磁盘IO的性能,(比如运行一个容器,在容器中对数据卷修改内容,会直接改变宿主机上的数据卷中的内容,所以是本地磁盘IO的性能,而不是先在容器中写一份,最后还要将容器中的修改的内容拷贝出来进行同步。)
-
绕过“拷贝写”系统,有些文件不需要在docker commit打包进镜像文件。
-
数据卷可以在容器间共享和重用数据
-
数据卷可以在宿主和容器间共享数据
-
数据卷数据改变是直接修改的
-
数据卷是持续性的,直到没有容器使用它们。即便是初始的数据卷容器或中间层的数据卷容器删除了,只要还有其他的容器使用数据卷,那么里面的数据都不会丢失。
Docker数据持久化:
容器在运行期间产生的数据是不会写在镜像里面的,重新用此镜像启动新的容器就会初始化镜像,会加一个全新的读写入层来保存数据。
如果想做到数据持久化,Docker提供数据卷(Data volume)或者数据容器卷来解决问题,另外还可以通过commit提交一个新的镜像来保存产生的数据。
七、Dockerfile
Dockerfile是由一系列指令和参数构成的脚本,这些指令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大的简化了部署工作。Dockerfile从FROM命令开始,紧接着跟随者各种方法,指令和参数。其产出为一个新的可以用于创建容器的镜像 。
Dockerfile常用指令
FROM指定基础镜像
放在第一行,其格式为:
#语法:
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>
LABEL镜像元数据
可以设置镜像的任何元数据,例如作者、版本等信息,格式为:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
ENV设置环境变量
主要就是设置环境变量,之后的命令都可以用此变量进行赋值,格式如下:
ENV <key> <value>
ENV <key>=<value> ...
VOLUME定义匿名卷
VOLUME用于创建挂载点,即向基于所构建镜像创始的容器添加卷:
VOLUME ["/data"]
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
VOLUME 让我们可以将源代码、数据或其它内容添加到镜像中,而又不并提交到镜像中,并使我们可以多个容器间共享这些内容 .
COPY复制文件
主要就是构建镜像时,进行拷贝文件到镜像的指定路径下,格式为:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
ADD更高级的复制文件
ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。比如<源路径>可以是一个 URL,这种情况下,Docker引擎会试图去下载这个链接的文件放到<目标路径>去。
EXPOSE设置监听端口
为镜像设置监听端口,容器运行时会监听改端口,格式为:
EXPOSE <port> [<port>/<protocol>...]
如,nginx镜像,监听了80端口
EXPOSE 80
同时,也能指定协议名,如:
EXPOSE 80/udp
ARG设置构建参数
该命令用于设置构建参数,该参数在容器运行时是获取不到的,只有在构建时才能获取。这也是其和ENV的区别。
ARG <name>[=<default value>]
使用举例:
arg author=yiuman
# 构建时,也可以替换了
# docker build --build-arg <varname>=<value>
docker build --build-arg author=xiaoming
RUN执行命令
在镜像的构建过程中执行特定的命令,并生成一个中间镜像。格式:
RUN <command>
或者
RUN ["executable", "param1", "param2"]
这也是很常用的一个功能了。 第一种后边直接跟shell命令 - 在linux操作系统上默认 /bin/sh -c - 在windows操作系统上默认 cmd /S /C
第二种是类似于函数调用。
可将executable理解成为可执行文件,后面就是两个参数。
两种写法比对:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME
RUN ["/bin/bash", "-c", "echo hello"]
CMD启动时命令
功能为容器启动时要运行的命令, 语法有三种写法
1. CMD ["executable","param1","param2"]
2. CMD ["param1","param2"]
3. CMD command param1 param2
就时shell这种执行方式和写法,第一种和第二种其实都是可执行文件加上参数的形式:
举例说明两种写法:
CMD [ "sh", "-c", "echo $HOME" ]
CMD [ "echo", "$HOME" ]
补充细节:这里边包括参数的一定要用双引号,就是双引号”,不能是单引号。千万不能写成单引号。原因是参数传递后,docker解析的是一个JSON array。
ENTRYPOINT启动默认命令
ENTRYPOINT 用于给容器配置一个可执行程序。也就是说,每次使用镜像创建容器时,通过 ENTRYPOINT 指定的程序都会被设置为默认程序。ENTRYPOINT 有以下两种形式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
ENTRYPOINT 与 CMD 非常类似,不同的是通过docker run执行的命令不会覆盖 ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile 中只允许有一个 ENTRYPOINT 命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT 指令 。
docker run运行容器时指定的参数都会被传递给ENTRYPOINT,且会覆盖 CMD 命令指定的参数。如,执行docker run <image> -d时,-d 参数将被传递给入口点 。
也可以通过docker run --entrypoint重写 ENTRYPOINT 入口点。如:可以像下面这样指定一个容器执行程序:
ENTRYPOINT ["/usr/bin/nginx"]
WORKDIR指定工作目录
用于在容器内设置一个工作目录:
WORKDIR /opt/docker/workdir
通过WORKDIR设置工作目录后,Dockerfile 中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。
USER指定当前用户
用于指定运行镜像所使用的用户:
USER yiuman
使用USER指定用户后,Dockerfile 中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。
nginx Dockerfile Demo:
# 注意:非注释第一行 必须以FROM 开头。
# FROM 指定基础镜像,即以此镜像作为基础
FROM nginx
# 设置元数据,利用 docker inspect [镜像名称|镜像ID],即可查看。
LABEL author="作者:yiuman"
LABEL version="版本:v0.1"
LABEL desc="说明:修改nginx首页提示"
# 操作执行,这里直接修改了nginx的html的首页内容,/usr/share/nginx/html
RUN echo 'hello,World' > /usr/share/nginx/html/index.html
# 启动命令 不写时 会直接使用基础镜像的启动命令
CMD ["nginx", "-g", "daemon off;"]
各容器的dockerfile实例的传送门:https://github.com/docker-library
八、Docker常用命令
- 容器生命周期管理 —
docker [run|start|stop|restart|kill|rm|pause|unpause] - 容器操作运维 —
docker [ps|inspect|top|attach|events|logs|wait|export|port] - 容器rootfs命令 —
docker [commit|cp|diff] - 镜像仓库 —
docker [login|pull|push|search] - 本地镜像管理 —
docker [images|rmi|tag|build|history|save|import] - 其他命令 —
docker [info|version] - 教练我要学—
docker --help
九、踩过的坑
刚开始用docker的时候,把docker for Mac 下载下来后,按照教程、随便看看文章就直接用上了。之所以用docker是因为公司需要弄微服务架构,运维部署这块选择了K8S与docker。然后自己就喜欢上了docker,本地跑下mysql,redis之类的。后来在微服务网络通信这块发现了容器内网络并不能通宿主机(即本地),折腾了大半天,终于在官网上找到了答案,如下:<传送门>

大致的意思是,macOS系统中存在网络限制,macOS没有docker0桥,因为这个接口是在虚拟机中的,宿主机无法ping通容器,从macOS主机无法访问docker (Linux)桥接网络。如果要从容器连接到主机,那么需要连接到host.docker.internal特殊的DNS,它将解析为主机使用的内部IP地址。网关可以用gateway.docker.internal
windows与linux下的docker是不存在这个问题的。