Docker

Docker是用于开发,部署和运行应用程序的流行平台,它通过容器化技术将软件以及依赖项打包在一个独立的标准化单元中,这能够解决"这个问题在我的机子上不是这样的啊.jpg"的问题。

Docker是要通过对应用组件的封装,发布,部署,运行等生命周期的管理来达到“一次封装,到处运行”的目的,这个组件可以是应用,服务甚至于一个完整的操作系统。

Docker与虚拟机的区别

Docker与虚拟机的区别
\Docker容器虚拟机
启动速度秒级别分钟级别
计算能力损耗几乎无百分之五十左右
性能接近原生弱于
系统支持量上千个几十个
隔离性通过命名空间实现隔离,通过cgroup实现限制资源完全隔离
内核所有容器共享宿主内核每个虚拟机各自独立

可以发现虽然Docker容器和虚拟机都是分出一块地方独立起来做自己的事情,但是他们有着极大的区别,Docker容器就如同它的核心“Go语言”一样极具轻量,几乎完全舍弃冗余部分的设计,以及系统资源的复用使它拥有极高的可用性,开启一个或多个容器几乎不会带来性能上的损耗,而虚拟机则更倾向于”完全的独立“,它从硬件到操作系统全部都是独立于主系统的,这带来了极高的安全保障和可操作性,但这也导致了它的笨重,以及性能上的损耗,这使得它通常并不能启动多个虚拟机。

因为Docker不需要去实现虚拟机的硬件虚拟化,可以直接使用物理机的硬件资源,所以在CPU,内存利用率上也会有显著优势

Docker利用的是宿主机的内核,不需要加载操作系统的OS内核,这也是Docker和虚拟机的一大区分点

Docker的基本运行流程

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上,然后通过Socket连接从客户端进行访问,守护进程从客户端接受命令并管理运行在主机上的容器。容器,是一个运行时环境,也就是Docker图标上的集装箱,Docker的运行流程如下:

  1. 用户使用Docker Client与Docker Daemon建立通讯,并发送请求给后者

  2. Docker Daemon作为Docker框架中的主体部分,提供Docker Server的功能使其可以接受 Docker Client的请求

  3. Docker Engine执行 Docker内部的一系列工作,每一项工作都以一个Job形式存在

  4. Job运行过程中,需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动Graph Driver将下载镜像以Graph的形式存储

  5. 当需要为Docker创建网络环境时, 通过网络管理驱动Network driver创建并配置Docker容器的网络环境

  6. 当需要 限制Docker容器运行资源或执行用户指令等操作时,则通过Exec Driver来完成

  7. Libcontainer是一项独立的容器管理包,Network driver以及Exec Driver都是通过Libcontainer来实现具体对容器进行的操作

Docker的常用组成

镜像(image)

镜像是一个只读的模板,一个镜像可以创建多个容器,Docker镜像就像是一个类模板,从这个模板,我们可以new出很多实例对象,这个实例对象就是我们的容器

容器(container)

容器类似于虚拟化环境中创建出来的实例,Docker利用容器来独立运行一个或一组应用。容器为镜像提供了一个标准且隔离的运行环境,可以被启动,开始,停止和删除。每个容器之间相互隔离

仓库(repository)

仓库是集中存放镜像文件的场所,分为公开仓库和私有仓库,最大的docker仓库是docker hub,但是网络通常难以链接,所以国内一般都是用的阿里云和网易云。

Docker的基本命令

search

docker search ubuntu #搜索名为ubuntu的镜像

search可以从仓库中搜索相符的镜像并展示,可以用来查询镜像名称,下载次数,镜像描述以及其是否官方。

pull

拉取镜像

docker pull redis:latest #拉取最新版本的redis镜像

将镜像从线上拉取到本地,之后就可以通过该镜像启动容器

images

查看当前Docker的镜像

docker images

通过该代码可以看到我们已经拉取到的所有镜像:

ps

查看当前Docker的容器

docker ps

该代码可以让我们看到当前已经启动的所有容器,以及容器的各项信息

docker ps -a  #可以看到所有容器,包括未启动的容器

而加上了参数-a之后就可以看到包含未启动容器在内的所有容器

run

从镜像中启动交互式容器

docker run [OPTIONS] IMAGE [COMMAND][ARG...]

OPTION:

  • --name="容器名称" 为容器指定一个名称

  • -d:后台运行容器并返回容器ID,即启动守护式容器(后台运行)

  • -i:以交互模式运行容器,通常与-t同时使用

  • -t:为容器重新分配一个伪输入终端,通常与-i同时使用,也就是启动交互式容器

  • 注:-i和-t同时使用可以写成-it

  • -P:随机端口映射

  • -p:指定端口映射

exec

执行命令

docker exec -it <container_id或name> <command>

在正在运行的容器中执行命令,例如:

docker exec -it Redis redis-cli

就是在名为Redis的容器中执行redis-cli的指令

commit

我们可以用commit命令来将一个容器提交为镜像:

docker commit [OPTION] <容器名称或ID> [新镜像名:标签]

OPTION:

  • -m:-m="添加vim编辑器",为镜像添加描述信息

  • -a:-a="你的名字",为镜像声明作者

  • --pause:--pause=true,提交时暂停容器,默认为true

  • -c:-c="ENV DEBUG=true",提交时应用一些Dockerfile指令

Docker的分层

联合文件系统

UnioFS(联合文件系统):Union文件系统是一种分层,轻量级且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。该文件系统是Docker镜像的基础,镜像可以通过分层来继承,基于基础镜像就可以制作出各种具体的应用镜像

Union文件系统的特性为:一次同时加载多个文件系统,但是从外面看只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统就会包含所有底层的文件和目录

镜像分层的好处就是可以共享资源,方便复制迁移,为资源的复用做好准备。

就比方说如果有多个镜像都从一个相同的base镜像构建而来,那么Docker Host就只需要在磁盘上保存一份base镜像,内存中也只需要加载一份base镜像就可以为所有容器服务。且容器每一层都可以被共享

当容器启动时,一个新的可写层就会被加载到镜像的顶部,这一层通常被叫做容器层。容器层之下的都叫做镜像层。所有对容器的改动都只会发生在容器层中,只有容器层可写,容器层下的所有镜像层都是只读的

对于Docker来说,可以用commit来实现“加层”的操作

docker commit -m="添加了vim编辑器" -a="TechGuide" 50dba2e06051 my-ubuntu-vim:1.0

这就是基于镜像的分层系统实现的多层封装,在获取到最初的base镜像后,我们可以通过“加一层”的方式来完善这个镜像的功能。

registry

我们可以通过docker pull registry来拉取registry的镜像,它能帮我们完成私有库的构建

docker run -d -p 端口映射:端口映射 -v /挂载数据卷 --privileged=true registry

启动registry,相当于本地有一个私有的docker hub

然后我们可以通过curl来验证私服库上所有的镜像:

curl -XGET http://你的IP地址:端口号/v2/_catalog

然后我们需要将自己要推送的镜像修改为符合私有库规范的tag。公式为:

docker tag 镜像:Tag Host:Port/Repository:Tag    #Tag为我们要修改的Tag,docker tag是指令前缀

在此之后我们还需要设置安全配置,我们要声明我们的私有库是安全的,否则请求会被拦截。在镜像源中配置

"insecure-registries":["192.168.111.162:5000"]   #此为示例,应将端口和IP均换成自己的

然后就可以将对应的镜像push到私有库中:

docker push 192.168.111.162:5000/images:1.0   #此为示例,应将端口和IP均换成自己的

数据卷

数据卷挂载

当我们的Docker挂载主机目录访问如果出现cannot open directory.:Permission denied时,可以在挂载目录后加一个--priviledged=true的参数。

我们可以通过数据卷来将Docker容器中的数据映射并持久化到本地目录中

docker run -it --privileged=true -v/宿主机绝对路径目录:/容器内目录 镜像名

数据卷的特性:

  • 数据卷可以在容器之间共享或重用数据

  • 卷中的更改可以实时生效

  • 数据卷中的更改不会包含在镜像的更新中

  • 数据卷的生命周期一直持续到没有容器使用它为止

数据卷的权限默认是读写(rw):

docker run -it --privileged=true -v/宿主机绝对路径目录:/容器内目录:rw 镜像名

在容器内目录后添加rw即表示读写权限,而如果我们想要让容器内部只能读取,不能写入,就可以以只读的形式挂载数据卷:

docker run -it --privileged=true -v/宿主机绝对路径目录:/容器内目录:ro 镜像名

我们可以通过

docker inspect 容器ID

来获取该容器的信息,其中就包括该容器的数据卷绑定情况(Mounts:挂载)

容器卷继承

容器卷之间可以继承

docker run -it --privileged=true --volumes-from 父类 --name Name

个人认为容器卷的继承实际上是一种内存上的共享。

更多推荐