Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
Docker入门
初识Docker
什么是Docker
Docker开源项目
Docker是基于Go语言实现的云开源项目,诞生于2013年初,最初发起者为dotCloud公司。Docker项目目前已加入Linux基金会,遵循Apache2.0协议,全部开源代码均在https://github.com/docker/docker上进行维护。
Docker的主要目标是”Build,ship and Run Any App,Anywhere“,即通过对应用组件的封装、分发、部署、运行等生命周期的管理,达到应用级别的”一次封装,到处运行“。这里的应用组件,可以是一个Web应用,也可以是一套数据库服务,甚至是一个操作系统或编译器。
Linux容器技术
Docker引擎的基础是Linux容器(Linux Containers,LXC)技术(容器有效第将由单个操作系统管理的资源划分到孤立的组中,以便更好第在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,这样既不需要指令级模拟,也不需要即时编译。容器可以在核心CPU本地运行指令,儿不需要任何专门的解释机制。此外,也避免了准虚拟化和系统调用替换中的复杂性。)
从Linux容器到Docker
在Linux容器的基础上,Docker进一步优化了容器的使用体验。Docker提供了各种容器管理工具(如分发、版本、移植等)让用户无需关注底层的操作,可以简单明了地管理和使用容器。
可以简单地将Docker容器理解为一种沙盒。每个容器内运行一个应用,不同的容器相互隔离,容器之间也可以建立通信机制。容器的创建和停止都十分快速,容器自身对资源的需求也十分有限,远远低于虚拟机。很多时候,甚至直接把容器当作应用本身也没有任何问题。
为什么使用Docke
Docker容器虚拟化的好处
高效地构建应用
Docker在开发和运维中的优势
- 更快的交付和部署。使用Docker,开发人员可以使用镜像来快速构建一套标准的开发环境;测试和运维人员可以直接使用相同的开发环境来部署代码。Docker可以快速创建和删除容器,实现快速迭代,大量节约开发、测试、部署的时间。并且各个步骤都有明确的配置和操作,整个过程全程课件,使团队更容易理解应用的创建和工作过程。
- 更高效的资源利用。Docker容器的运行不需要额外的虚拟化管理程序支持,它是内核级的虚拟化,可以实现更高的性能,同时对资源的额外需求很低。
- 更轻松的迁移和扩展。Docker容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。
- 更简单的更新管理。使用Dockerfile,只需要小小的配置修改,就可以替代以往大量的更新工作。并且所有修改都可以以增量的方式进行分发和更新,从而实现自动化并且高效的容器管理。
Dcoker与虚拟机比较
- Docker容器很快,启动和停止可以在秒级实现。
- Docker容器对系统资源需求很少,一台主机可以同时运行数千个Docker容器。
- Docker通过类似Git的操作来方便用户获取、分发和更新应用镜像,指令简明,学习成本较低。
- Docker通过Dockerfile配置文件来支持灵活的自动化创建和部署机制,提高工作效率。
特性 | 容器 | 虚拟机 |
---|---|---|
启动速度 | 秒级 | 分钟级 |
硬盘使用 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
隔离性 | 安全隔离 | 完全隔离 |
虚拟化与Docker
在计算领域,一般指的是计算虚拟化,或通常说的服务器虚拟化。(维基百科:在计算机技术中,虚拟化是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以用比原本的组态更好的方式来应用这些资源。)
虚拟化技术分类
- 基于硬件的虚拟化(不常见)
- 基于软件的虚拟化
- 应用虚拟化
- 平台虚拟化
- 完全虚拟化。虚拟机模拟完整的底层硬件环境和特权指令的执行过程,客户操作系统无需进行修改。
- 硬件辅助虚拟化:利用硬件辅助支持处理敏感指令来实现完全虚拟化的功能,客户操作系统无需进行修改。
- 部分虚拟化:只针对部分硬件资源进行虚拟化,客户操作系统需要进行修改。
- 超虚拟化:部分硬件接口以软件的形式提供给客户机操作系统,客户操作系统需要进行修改。
- 操作系统及虚拟化:内核通过创建多个虚拟的操作系统实例来隔离不同的进行。容器相关技术属于这个范畴。
Docker和传统虚拟机方式的不同之处
Docker的核心概念和安装
三大核心概念:镜像、容器、仓库
核心概念
Docker镜像
Docker镜像类似于虚拟机镜像,可以理解为一个面向Docker引擎的只读模板,包含了文件系统。
镜像是创建Docker容器的基础。通过版本管理和增量的文件系统,Docker提供了一套简单的机制来创建和更新现有的镜像,用户设置可以从网上下载一个已经做好的应用镜像,并通过简单的命令就可以直接使用。
Docker容器
Docker容器类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用。容器是从镜像创建的应用运行实例,可以将其启动、开始、停止、删除,而这些容器都是互相隔离、不可见的。
容器可以看做一个简易版的Linux系统环境(包括root用户权限、进程空间、用户空间和网络空间等),以及运行在其中的应用程序打包成应用盒子。
镜像自身是只读的。容器从镜像启动的时候,Docker会在镜像的最上层创建一个可写层,镜像本身保持不变。
Docker仓库
注册服务器是存放仓库的地方,不能将Docker仓库和注册服务器(Registry)混为一谈。
Docker仓库类似于代码仓库,是Docker集中存放镜像文件的场所。每个仓库放着某一类镜像,往往包括多个镜像文件,通过不同的标签(tag)来区分。
根据所存储的镜像公开与否,Docker仓库可以分为公开仓库和私有仓库两种形式。目前最大的公开仓库是Docker Hub,存放了数量庞大的镜像供用户下载。国内的公开仓库包括Docker Pool等,可以提供稳定的国内访问。Docker也支持用户在本地网络创建一个只能自己访问的私有仓库。
安装Docker
- Ubuntu
- CentOS
- Windows
- Mac OS
1 | brew install docker |
镜像
镜像是Docker的三大核心概念之一。Docker运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker会尝试先从默认镜像仓库下载,用户也可以通过配置,使用自定义的镜像仓库。
获取镜像
镜像是Docker运行容器的前提。使用docker pull
命令从网络上下载镜像。
1 | docker pull NAME[:TAG] |
若不显式地指定TAG,则默认会选择latest标签,即下载仓库的最新版本的镜像。
1 | docker pull ubuntu |
下载过程可以看出,镜像文件一般由若干层组成,行首的c64513b74145代表了各层的ID。下载过程中会获取病输出镜像的各层信息。层其实是AUFS(Advanced Union File System,一种联合文件系统)中的重要概念,是实现增量保存与更新的基础。
两条安装命令相当于:docker pull registry.hub.docker.com/ubuntu:latest
,即从默认的注册服务器registry.hub.docker.com中的ubuntu仓库下载标记为latest的镜像。
指定完整的仓库注册服务器地址。例如从DockerPool社区的镜像源下载
1 | docker pull dl.dockerpool.com:5000/ubuntu |
使用镜像创建容器并运行bash应用
1 | docker run -t -i ubuntu /bin/bash |
查看镜像信息
1 | # 列出本地主机上已有的镜像 |
TAG信息用来标记来自同一个仓库的不同镜像。仓库中有多个镜像,通过TAG信息来区分发行版本。
1 | # 使用docker tag为本地镜像添加新的标签 |
不同标签的镜像的ID完全一致的,说明它们实际上指向了同一个镜像文件,只是别名不同。标签起到了引用或快捷方式的作用。
docker inspect
可以获取镜像的详细信息,docker inspect
命令返回的是一个JSON格式的消息,若只要其中一项内容,可以使用-f参数指定。
1 | docker inspect 2cb0d9787c4d |
搜寻镜像
使用docker search
命令可以搜索远端仓库中的共享的镜像,默认搜索Docker Hub官方仓库中的镜像。
支持的参数:
- –automated=false 仅显示自动创建的镜像
- –no-trunc=false 输出信息不截断显示
- -s,–starts=0 指定仅显示评价为指定星级以上的镜像
删除镜像
使用docker rmi
可以删除镜像
1 | # IMAGE可以为标签或ID |
当一个镜像拥有多个标签时,docker rmi
只是删除了该镜像多个标签中的指定标签而已,并不影响镜像文件。但当只剩下一个标签时,执行docker rmi
命令会删除这个镜像文件的所有AUFS层。
当使用docker rmi
命令后面跟上镜像ID(也可以是ID能进行区分的部分前缀串)时,会先尝试删除所有指定该镜像的标签,然后删除镜像文件本身。
当有镜像创建的容器存在时,镜像文件默认是无法被删除的1
2
3
4# 删除依赖该镜像的所有容器
docker rm ubuntu
# 然后再删除镜像
docker rmi ubuntu
创建镜像
三种方法:基于已有镜像的容器创建、基于本地模板导入、基于Dockerfile创建
基于已有镜像的容器创建
使用docker commit
命令,格式:
1 | docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] |
主要选项包括:
- -a,–author=””作者信息
- -m,–message=””提交信息
- -p,–pause=true提交时暂停容器运行
1 | # 演示 |
基于本地模板导入
使用OpenVZ提供的模板创建。OPENVZ模板的下载地址为https://download.openvz.org/template/precreated/
导入命令
1 | cat ubuntu-14.04-x86_64-minimal.tar.gz | docker import - ubuntu:14.04 |
存出和载入镜像
可以使用docker save
和docker load
命令来存出和载入镜像
存出镜像
1 | docker save -o ubuntu_14.04.tar ubuntu:14.04 |
载入镜像
1 | docker load --input ubuntu_14.04.tar |
上传镜像
使用docker push
命令上传镜像到仓库,默认上传到DockerHub官方仓库(需要登录),命令格式为docker push NAME[:NAME]
,第一次使用时,会提示输入登录信息或进行注册。
容器
容器时Docker的另一个核心概念,容器就是镜像的一个运行实例,不同的是,它带有额外的可写文件层。
创建容器
新建容器
使用docker create
命令新建一个容器
1 | docker create -it ubuntu:latest |
使用docker create
新建的容器处于停止状态,可以使用docker start
启动。
新建并启动容器
启动容器的方式有两种
- 基于镜像新建一个容器并启动
- 将在终止状态的容器重新启动
docker run
= docker create
+ docker start
1 | # 使用如下命令输出一个‘hello world’,之后容器自动终止 |
当利用docker run
创建并启动容器时,Docker在后台运行的标准操作:
- 检查本地是否存在指定的镜像,不存在就从共有仓库下载。
- 利用镜像创建并启动一个容器。
- 分配一个文件系统,并在只读的镜像层外挂载一层可读写层。
- 从宿主主机配置的网桥接口中桥接一个虚拟接口道容器中去。
- 从地址池配置一个IP地址给容器。
- 执行用户指定的应用程序。
- 执行完毕后容器被终止。
下面命令启动一个bash终端,允许用户进行交互
1 | docker run -t -i ubuntu:latest /bin/bash |
-t:让Docker分配一个伪终端并绑定到容器的标准输出上
-i:让容器的标准输入保持打开
1 | docker run -ti ubuntu /bin/bash |
守护态运行
-d
参数可以让Docker容器在后台以守护态形式运行
1 | ➜ ~ docker run -d ubuntu /bin/bash -c "while true;do echo hello world;sleep 1;done" |
终止容器
docker stop
可以用来终止一个运行中的容器,命令格式docker stop [-t|--time[=10]]
它会首先向容器发送SIGTERM信号,等待一段时间后(默认为10s),再发送SIGKILL信号终止容器,当Docker容器中指定的应用终结时,容器也自动终止。
处于终止状态的容器,可以通过docker start
重新启动
docker restart
可以重启一个运行中的容器。
进入容器
attach命令
1 | ➜ ~ docker run -idt ubuntu |
但是使用attach命令有时候并不方便。当多个窗口同时attach到同一个容器时,所有的窗口都会同步显示。当某个命令因命令阻塞时,其他窗口也无法执行操作。
exec命令
Docker自1.3版本起,提供了exec工具,可以直接在容器内运行命令。
1 | docker exec -ti a9e625 /bin/bash |
nsenter工具
nsenter工具在util-linux包2.33版本后包含。
删除容器
docker rm
可以删除处于终止状态的容器,命令格式为docker rm [OPTION] CONTAINER [CONTAINER...]
支持的选项包括:
- -f,–force=false强行终止并删除一个运行中的容器
- -l,–link=false删除容器的连接,但保留容器
- -v,–volume=false删除容器挂载的数据卷
导入和导出容器
导出容器
导出容器是指导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态,可以使用docker export
命令,命令格式为docker export CONTAINER
。
1 | docker export ce4 > test_for_run.tar |
导入容器
docker import
可以导入文件成为镜像
1 | cat test_for_run.tar | docker import - test/ubuntu:v1.0 |
与docker load的区别:
docker load命令导入镜像存储文件到本地的镜像库
docker import命令导入一个容器快照到本地镜像库
容器快照文件将丢弃所有的历史记录和元数据信息(仅保留容器当时的快照状态),而镜像存储文件保留完整记录,体积也更大。此外,从容器快照文件导入可以重新指定标签等元数据信息。
仓库
仓库是集中存放镜像的地方,注册服务器是存放仓库的具体服务器,每个服务器上可以有多个仓库,每个仓库下面有多个镜像。仓库地址dl.dockerpool.com/ubuntu,dl.dockerpool.com是注册服务器,ubuntu是仓库名。
Docker Hub
基础操作
- docker pull 下载镜像到本地
- docker search 搜索公共仓库镜像
- docker push 将本地奖项推动到Docker Hub
镜像资源分为两类
- 类似ubuntu这样的基础镜像,成为基础或根镜像。这些镜像由Docker公司创建、验证、支持、提供,这样的镜像往往使用单个单词作为名字
- 类似user/ubuntu这样的镜像,它是由DockerHub的用户user闯将并维护的,带有用户名称前缀,
查找时可以通过-s N参数指定仅显示评价为N星以上的镜像。
自动创建
自动创建使用户通过Docker Hub指定跟踪一个目标网站(目前支持GitHub或BitBucket)上的项目,一旦项目发现新的提交,则自动执行创建。
步骤:
- 创建并登陆Docker Hub,以及目标网站;*在目标网站中连接账户到Docker Hub
- 在Docker Hub中配置一个自动创建
- 选取一个目标网站中的项目(需要含Dockerfile)和分支
- 指定Dockerfile的位置,并提交创建。
之后可以在Docker Hub的“自动创建”页面中跟踪每次创建的动态。
创建和使用私有仓库
使用registry镜像创建私有仓库
通过官方的registry镜像简单搭建一套本地私有仓库环境:
1 | docker run -d -p 5000:5000 registry |
数据管理
用户在使用Docker的过程中,需要能查看容器内应用产生的数据,或者需要把容器内的数据进行备份,甚至多个容器之间进行数据共享,必然涉及到容器内的数据管理操作。容器中管理数据的方式主要有两种方式:1.数据卷 2.数据卷容器
数据卷
数据卷是一个可供容器使用的特殊目录,它绕过文件系统,提供很多有用的特性:
- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新,不会影响镜像
- 卷会一致存在,知道没有容器使用
数据卷的使用用,类似于Linux下对目录或文件进行mmount操作。
在容器内创建一个数据卷
使用docker run
命令的时候,使用-v编辑可以在容器内创建一个数据卷。多次会用-v标记可以创建多个数据卷。
使用training/webapp镜像创建一个Web容器,并创建一个数据卷挂载到容器的/web目录:
1 | docker run -d -P --name web -v /webapp training/webapp python app.py |
-P是允许外部访问容器需要暴露的端口
挂载一个本地主机文件作为数据卷
-v标记也可以从主机上挂载单个文件到容器中作为数据卷:
1 | docker run --rm -it -v ~/.zshrc ubuntu /bin/bash |
网络基础配置
端口映射实现访问
从外部访问容器应用
可以通过-P
或-p
参数来指定端口映射。当使用-P
标记时,Docker会随机映射一个49000~49900的端口至容器内部开放的网络端口。-p
则可以指定要映射的端口,并且在一个指定端口上只可以绑定一个容器。支持的格式有:ip:hostport:containerPort | ip::containerPort | hostPort:containerPort
1 | docker run -d -P training/webapp python app.py |
映射所有的接口地址
将本地的5000端口映射到容器的5000端口
1 | docker run -d -p 5000:5000 training/webapp python app.py |
亦可以多次使用-p
标记绑定多个端口
1 | docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py |
映射到指定地址的指定端口
1 | docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py |
映射到指定地址的任意端口
使用ip::containerPort
绑定localhost的任意端口到容器的5000端口,本地主机会自动分配一个端口:
1 | docker run -d -p 127.0.0.1::5000 training/webapp python app.py |
查看映射端口配置
使用docker port
查看当前映射的端口配置,也可以查看到绑定的地址
1 | docker port DOCKERNAME containerPort |
容器有自己的内部网络和IP地址(
docker inspert + 容器ID
可以获取所有的变量值)。
容器互联实现容器间通信
容器的连接系统是除了端口映射外另一种可以与容器中应用进行交互的方式。它会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。
连接系统根据容器的名称来执行,因此需要先自定义一个好记的容器命名。使用--name
标记可以为容器自定义命名
1 | docker run -d -P --name web training/webapp python app.py |
八、使用Dockerfile创建镜像
Dockerfile是一个文本格式的配置文件,用户可以使用Dockerfile快速创建自定义的镜像。
基本结构
Docker由一行行命令语句组成,并且支持以#开头的注释行。一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动执行指令。
1 | # This dockerfile uses the ubuntu image |
- Example1
1 | From ubuntu |
- Example2
基于ubuntu,安装firfox和vnc软件,启动后,用户可以通过5900端口通过vnc方式使用firefox
1 | FROM ubuntu |
指令
指令的一般格式为INSTRUCTION arguments,指令包括FROM、MAINTAINER、RUN等
FROM
第一条必须为FROM指令。并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个FROM指令。
格式:FROM <image>或FROM <image>:<tag>
MAINTAINER
指定维护者的信息
格式:MAINTAINER <name>
RUN
在shell终端中运行命令,即/bin/sh -c
格式:RUN <command>
使用exec执行命令,指定其他终端可以用这种方式实现
格式:RUN ["/bin/bash","-c","echo hello"]
每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以用\来换行。
CMD
支持三种形式
CMD [”executable","param1","param2"]
使用exec执行,推荐方式CMD command param1 param2
在/bin/bash
中执行,CMD ["param1","param2"]
提供给ENTRYPOINT的默认参数
指定启动容器时执行的命令,每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行。如果用户启动容器时制定了运行的命令,则会覆盖掉CMD指定的命令。
EXPOSE
告诉Docker服务端容器暴露的端口号,供互联网使用。在启动容器时需要通过-P,Docker主机会自动分配一个端口转发到指定的端口;使用-p,则可以指定哪个本地端口映射过来。
格式:EXPOSE <port> [<port>...]
ENV
指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持。
格式:ENV <key> <value>
1
2
3
4ENV PG_MAJOR 9.3
ENV PG_VERSIOIN 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && ...
ENV PATH /usr/local/postgress-$PG_MAJOR/bin:$PATH
ADD
将复制指定的<src>
到容器中的<dest>
。其中<src>
可以是Dockerfile所在目录的一个相对露冷静(文件或目录);也可以是一个URL;还可以是一个tar文件(自动解压为目录)。
格式:ADD <src> <dest>
COPY
复制指定的<src>
(为Dockerfile所在目录的相对路径,文件或目录)为容器中的<dest>
。目标路径不存在,会自动创建。使用本地目录为源目录时,推荐使用COPY
格式:COPY <src> <dest>
ENTRYPOINT
配置容器启动后执行的命令,并且不可被docker run提供的参数覆盖。每个Dockerfile中只能有一个ENTRYPOINT,当指定多个ENTRYPOINT时,只有最后一个生效。
格式:ENTRYPOINT ["executable","param1","param2"]
ENTRYPOINT command param1 param2(shell中执行)
VOLUME
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。
格式:VOLUME ["/data"]
USER
指定运行容器时的用户名或UID,后续的RUN也会使用指定用户。当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres
。要临时获取管理员权限可以使用gosu,而不推荐sudo。
格式:USER daemon
WORKDIR
为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
格式:WORKDIR /path/to/workdir
1 | WORDDIR /a |
ONBUILD
配置当所创建的镜像作为其他新创建镜像的基础镜像时所操作的指令。
格式:ONBUILD [INSTRUCTION]
Dockerfile创建镜像
编写完成Dockerfile后,通过docker build
命令来创建镜像。
格式:docker build [option] path
该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下所有内容发给Docker服务端,由服务端来创建镜像。因此一般建议放置Dockerfile的目录为空目录。
通过.dockerignore文件(每行添加一条匹配模式)来让Docker忽略路径下的目录和文件。
通过-t
选项可以指定镜像的标签信息。