这周分享的内容是关于 Docker 的基础,大概的内容分为下面的两个部分,另外还做了个视频,其实这个视频仅仅用来娱乐娱乐而已
前言
第一趴---Docker容器圈简介
Docker容器圈简介
第二趴---Docker基本操作
Docker基本操作
容器圈
容器这个新生事物,现在还可以说是新生事物吗?对于我们学生而言,我觉得没毛病,你说呢?
容器技术可说重塑了整个云计算市场的形态,带动了一批年轻有为的容器技术儿,不过「
容器
」这个概念是
Docker
公司发明的么,不是,它只是众多
Pass
项目中的最底层,没人关注的那一部分而已。
什么是Pass项目?
Pass
项目之所会被很多公司所接受,自然是因为解放了部分开发人员的劳动力,尽快干玩活儿早点下班。其依赖的就是「
应用托管
」的能力,在电脑上斗过地主的应该知道,托管了以后就会自动出牌,同样的道理,为了尽量的弥补本地和云上的环境差异,就出现了
Pass
开源项目。
举个例子来说,运维人员小仙云上部署一个
Cloud Foundry
项目,开发人员只需要简单的一行代码就可以实现将本地的应用部署到云上
就这样一行代码就实现了将本地应用上传到云上,属实很轻松。
那么这个命令执行的基本原理是怎样的?
实际上,我们可以将其最核心的组件理解为一套应用的
打包
和
分发机制
。云上部署的
Cloud Foundry
会为大部分编程语言定义一种
打包
的格式,当开发人员执行命令的时候,实际上是将
可执行文件
和
启动脚本
打包上传到云上的
Coulud Foudry
中,然后
Cloud Foundry
通过相应的
调度器
选择一个虚拟机的
Agent
将压缩包下载后启动
那如何区分虚拟机中的不同应用呢?
虚拟机一般不可能只跑一个应用,因为这样确实也太浪费资源了,我们可以想想,现在手上的电脑,可以用
Vmvare
导入几个虚拟机,所以诸如
Cloud Foundry
通过引入操作系统的
Cgroups
和
Namespace
等机制,从而来为每个应用单独创建一个叫做「
沙盒
」的隔离环境,然后在这些「
沙盒
」中启动应用,通过这样的方法就让虚拟机中应用各自互不干扰,让其自由翱翔,至于
Cgroups
和 **Namespace **的实现原理,后续我们再共同的探讨
这里所谓的隔离环境就是「
容器
」。
那 Docker 和这 Pass 项目的 Cloud Foundry 的容器有啥不一样?
自然不一样,不然现在我们一旦提到容器,想到的不会是
Docker
,而是
Cloud Foundry
了吧。
Cloud Foundry
的首席产品经理就觉得没什么,毕竟自己放的屁都是香的!
不一样,而且当时还发了一份报告,报告中还写到:“
Docker
不就是使用了
Cgroups
和
Namespace
实现的「沙盒」而已,不用过于关注”。
没想到的是,随后短短的几个月,
Docker
项目迅速起飞以至于其他所有
Paas
社区都还没来及反应过来,就已经宣告出局
什么魔力让 Docker 一发不可收拾?
就是提出了
镜像
的概念。上面我们说过,
Paas
提供的一套应用打包的功能,看起很轻松省事,但是一旦使用了
Paas
,你就要终身服务于它,毕竟他是提供商,是「爸爸」,用户需要为每个版本,每种语言去维护一个包,这个打包的过程是需要多次的尝试,多次试错后,才能摸清本地应用和远端
Paas
的脾气,从而顺利部署。
而
Docker 镜像
恰恰就是解决了
打包
这一根本问题。
什么是Docker镜像?
Docker
镜像也是一个压缩包,只是这个压缩包不只是可执行文件,环境部署脚本,它还包含了
完整的操作系统
。因为大部分的镜像都是基于某个操作系统来构建,所以很轻松的就可以构建本地和远端一样的环境。
这就很牛皮了,如果我们的应用是在
Centos7
上部署,我们只需要将项目环境部署在基于
Centos7
的环境中,然后无论在哪里去解压这个压缩包,都可以保证环境的一致性。在整个过程中,我们根本不需要进行任何的配置,因为这个压缩包可以保证:
本地的环境和云端是一致的
,这也是
Docker
镜像的精髓
开发者体验到了
Docker
的便利,从而很快宣布
Paas
时代的结束,不过对于大规模应用的部署,
Docker
能否实现在当时还是个问号
就在 2014 年的
DockerCon
上,紧接着发布了自研的「
Docker swarm
」,
Docker
就这样 一度奔向高潮,即将就到达了自己梦想之巅。
为什么会推出Docker Swarm?
虽然
Docker
通过「
容器
」完成了对
Paas
的「
降维打击
」,但是
Docker
的目的是:如何让更多的开发者将项目部署在自己的项目上,从技术,商业,市场多方位的争取开发者的群体,为此形成自家的
Paas
平台做铺垫
Docker
项目虽然很受欢迎,就目前看来只是一个创建和启动容器的小工具。需要应该清楚的一点是,用户最终部署的还是他们的网站,服务甚至云计算业务。所以推出一个完整的整体对外提供集群管理功能的
Swarm
势在必行,这个项目中的最大亮点即直接使用了
Docker
原生的
API
来完成对集群的管理。
对于
单机
项目,只需要执行下面一条语句即可实现容器
对于
多机
的项目,只需要执行
你看,从单机切换到多机,使用的方法也就参数不同而已,所以这样一个原生的「
Docker容器集群管理
」一发布就受到大家的青睐。随着自身生态的逐渐完善,借助这一波浪潮并通过各种并购来强大自己的平层能力
要说最成功的案例,非
Fig
项目莫属。之所以这么屌,是因为作者提出了「
容器编排
」的概念。
什么是容器编排?
其实这也不是什么新鲜内容,比如在
Linux
中的
Makefile
和常见的
SHELL
脚本,它是一种通过工具或者配置来完成一组虚拟机或者关联资源的定义、配置、创建等工具。
容器的编排是怎么样的呢
我们先以 Fig 为例,假设开发人员小黑,现在要部署一个项目,其中包含了应用容器
A
,数据库容器
B
,负载容器
C
,这个时候
Fig
只需要将三个容器定义在一个配置文件,然后指定他们的关联关系,执行下面的命令即可
当然也可以在
Fig
的配置文件中配置各种容器的副本,然后加上
Swarm
的集群管理功能,这样不就是
Paas
了么。只是这个项目被收购以后,修改名字为
compose
了,后续也会的
compose
进行详细的阐述
就这样一个以「
鲸鱼
」为商标的
Docker
,火遍全球,因为它秉持将「
开发者
」群体放在食物链的顶端。一分钟实现网站的部署,三分钟搭建集群,这么多年以来,很多后端开发者很少将眼光放在
Linux
技术上,开发者们为了深入的了解
Docker
的技术原理,终于将眼光放入诸如
Cgroups
和
Namespace
技术中。
就在这一时之间,后端及云计算领域的大佬都汇聚于这个「
小鲸鱼
」的身边。随后到了考虑集群的方案,论集群的管理调度能力,还不得不提
Berkeley
的
Mesos
,专注于大数据领域,更加关注的是计算密集型的业务。凭借着它天生的两层调度机制,让它很快发布了一个叫做
Marathon
的项目,这个项目随即成为了
Swarm
的有力竞争对手。
这还没完,说了这么久,还没有提到在基础设施领域翘楚的
Google
公司,是的,同在这一年,宣告了一个叫做
Kubernetes
项目的诞生。
随着
Docker
生态的建立,
Docker swarm
,
Docker compose
,
Machine
形成了三件套,此时大量围绕
Docker
项目的网络,存储,监控都涌现。在令人兴奋的背后,是对它更多的担忧,这主要来源于对
Docker
商业化战略的顾虑,
Docker
公司对于
Docker
着绝对的权威并在多个场合直接和谷歌,微软对干
其实在
Docker
兴起的时候,谷歌也开源了一个
Linux
容器:
Imctfy
,在当时这个项目在
Docker
面前真是弟弟,所以向
Docker
公司表示想合作的意愿,
Docker
显然不同意,且在之后的不久自己发布了一个容器运行时的库
Libcontainer
,可能由于太急躁,其代码可读性极差,不稳定和频繁的变更,让社区叫苦不迭
为了切割
Docker
项目的话语权,决定成立一个中立的基金会。所以于 2015 年将这个
Libcontainer
捐出,并修改名称为
Runc
,然后依据
RunC
项目,制定了一套容器和镜像的标准和规范----
OCI
什么是OCI
为了让
Docker
不能太嚣张,其他玩家构建自身平台的时候不依赖于
Docker
项目,提出一个标准和规范----
OCI
。这一标准并没有改变 Docker 在容器领域一家独大的现状。Google 坐不住了,必须得搞点大招
**Google **给
RedHat
等伙伴打了电话,说咱们共同牵头发起一个基金会-----
CNCF
。目的很简单,以
kubernetes
为基础,建立一个以由开源基础设置主导,按照独立基金会方式运营的平台级社区,来对抗
Docker
公司为核心的容器商业生态
为了做好这个事儿,CNCF 必须完成两件事儿
CNCF 如何解决第一个问题----编排能力
Swarm
的无缝集成以及
Mesos
的大规模集群的调度管理能力,很明显,如果继续往这两个方向发展,后面的路不一定好走。所以,
kubernetes
选择的方式是
Borg
,其基础特性是 Google 在容器化基础设施多年来实践的经验,这也正是项目从一开始就避免了和
Swarm
,mesos 社区同质化的重要手段
看似很有技巧,怎么落地?
RedHat
正好擅长这玩意呀,它能真正的理解开源社区运作和项目研发真谛的合作伙伴。作为
Docker
一方,主要不管的强调「
Docker native
」,但是由于
kubernetes
没有跟
Swarm
展开同质化的竞争,所以这个「
Docker Native
」的说法并没有什么杀伤力。反而其独特的设计理念和号召力,让其构建了一个完全与众不同的容器编排管理生态。
就这样很快就把
Swarm
甩在了身后。随机开始探讨第二个问题,CNCF 添加了一系列容器工具和项目,面对这样的压迫,
Docker
在2016年决定放弃现有的
Swarm
项目,而是将容器编排等全部内置到
Docker
项目中。
而
kubunetes
的应对方案也蛮有意思,开启「
民主化架构
」,
kubernetes
为开发者暴露可以扩展的插件机制,让用户可以随意的通过植入代码的方式介入到
kubernetes
的每一个阶段,很快,整个容器圈出现了优秀的作品:火热的微服务治理项目
lstio
等
面对
kubernetes
的 强力攻击,
Docker
公司不得不面对失败的事实,只好放弃开源社区专注于商业化转型,所以于2017年将容器运行时部分
containerd
捐赠给了
CNCF
,从而将
Docker
项目改名为
Moby
,然后交给社区维护,于 2017 年,**Docker **公司宣布将在企业版内置
kubernetes
项目,这也标志了
kubernetes
「编排之争」的结束
Docker能做什么
Docker
是一个用于开发,发布,运行应用的程序于一体的开放平台。如果我们需要将货物整齐的摆放在船上且互不影响,那么一种可行的方案即通过集装箱进行标准化,我们将各种货品通过集装箱打包,然后统一的放在船上进行运输,Docker其实就是这样一个将各种软件进行打包成集装箱,然后分发。
Docker的安装
Docker
是一个跨平台的解决方案,支持各大平台比如
Centos
,
Ubuntu
等
Linux
发行版。下面讲述的是在
Centos
中的使用,安装
运行上述命令,
Docker
首先会检查本地是否有
hello-world
这个镜像,如果发现本地没有这个镜像,
Docker
就会去
Docker Hub
官方仓库下载此镜像,然后运行它。最后我们看到该镜像输出 "Hello from Docker!" 并退出。
Docker核心概念
Docker
的操作主要围绕
镜像
,
容器
,
仓库
三大核心概念
什么是镜像?
一句话说即镜像是
Docker
容器启动的先决条件,因为镜像会提供容器运行的一些基础文件和配置文件,是容器启动的基础。说白了,要启动容器,需要镜像来提供一些基础环境。
使用的镜像的方式有哪些?
什么是容器?
容器是镜像的运行实体。镜像是静态的
只读文件
,可是容器是要
运行
的,需要可写文件层。所以容器运行着真正的应用进程,所以自然会有创建,运行,停止,暂停和删除五种状态
既然容器是直接运行的运行程序,那它是有自己的
命名空间
嘛?
容器有自己独立的命名空间和资源限制,意味着在容器内部,你无法看到主机上面的进程,环境变量等信息,这就是容器和物理机上的进程本质区别
什么是仓库?
镜像仓库类似于代码仓库,用来分发和存储镜像,分为
公共镜像
和
私有镜像
。
Docker hub
是
Docker
的官方公开镜像仓库,很多的官方镜像都可以在上面找到,但是访问很慢,所以可以找国内的镜像源,当然后面我们也会自己搭建一个私有镜像仓库
三者的关系是怎么样的?
上图清晰的展现了镜像是容器的基石,容器是在镜像的基础上创建的。一个镜像可以创建多个容器,仓库用来存放和分发镜像
Docker架构
容器技术的发展可说突飞猛进了,市面上除了
Docker
容器还有
coreos
的 rkt,lxc 等,这么多种容器,是不是需要一个标准呢,不然就太容易乱套了
你可能会说直接把
Docker
作为标准不就好了,但是有这么多相关的容器技术,谁不想吃个好瓜,除此之外,当时的编排的技术也竞争火爆,当时的三主力分别是
Docker Swarm
,
kubernetes
以及
mesos
。作为原生的
Docker Swarm
自然优势明显,但是
kubernetes
不同意啊,它们觉得
调度
形式太单一了
因此爆发了容器大战,
OCI
也就在此出现。
OCI
是开放的容器标准,轻量级开放的治理结构,目前主要有两个标准,分别是容器运行时标准和容器镜像标准
在如此竞争激烈下面,
Docker
的架构成为了下面这个样子
Docker
的整体架构为
CS
架构,客户端和服务端两部分组成,客户端发送命令,服务端接受处理指令,其通信的方式有多种,即可以通过
Unix
套接字通信,也可以网络链接远程通信
Docker客户端
我们平时通常使用
Docker
命令行的方式和服务端打交道,其实还可以通过 **REST API **的方式和
Docker
服务端交互,甚至使用各种预言的
sdk
和
Docker
的服务端交互,美滋滋
Docker服务端
Docker
服务端是 Docker 后台服务的总称。其中
Dockerd
是一个非常重要的后台进程,它负责响应并处理
Docker
客户端的请求,然后转化为
Docker
的具体操作
Docker 重要的组件
我们去 Docker 默认安装路径先看看有哪些组件
这里主要说明下
runc
和
contained
组件
通过上图,可以看到,
dockerd
通过
gRPC
与
containerd
通信,由于
dockerd
与真正的容器运行时,
runC
中间有了
containerd
这一
OCI
标准层,使得
dockerd
可以确保接口向下兼容。
gRPC
是一种远程服务调用。
containerd-shim
的意思是垫片,类似于拧螺丝时夹在螺丝和螺母之间的垫片。
containerd-shim
的主要作用是将
containerd
和真正的容器进程解耦,使用
containerd-shim
作为容器进程的父进程,从而实现重启
dockerd
不影响已经启动的容器进程。
docker 各个组件之间的关系
此时发现其 pid 为 4428,随后我们查看进程之间的关系
注意,docker19 就看不到两者是父子关系了
可以先使用 ps aux | grep contained ,然后使用
pstree
查看 contained 的 pid ,实际上,Docker 启动的时候,contained 就启动了,dockerd 和 contained 一直就存在。当执行了docker run以后,contained 就会创建 contained-shim 充当垫片进程,然后启动容器的真正进程 sleep 3600,这和架构图一致
075528566666
Docker相关组件
对于我们最直观的即
Docker
命令,作为
Docker
客户端的完整实现,通过
Docker
命令来实现所有的
Docker
客户与服务端的通信
Docker 客户端于服务端的交互过程是怎么样的呢
Docker
组件向服务端发送请求后,服务端根据请求执行具体的动作并将结果返回给
Docker
,
Docker
解析服务端的返回结果,并将结果通过命令行标准输出展示给用户。这样一次完整的客户端服务端请求就完成了
dockerd 为
Docker
服务端后台的常驻进程,负责接收客户端的请求,处理具体的任务并将结果返回客户端
那么 Docke r客户端采用哪几种方式发送请求
第一种方式:通过
unix
套接字与服务端通信,配置的格式为:
unix://socket_path
。默认的
dockerd
生成的 socket文件存放在
/var/run/docker.sock
,此文件只能是
root
用户才能访问,这也是为什么刚安装完
Docker
后只能root 来进行访问操作
第二种方式:采用
TCP
的方式与服务端通信,配置格式为:
tcp://host:por
,为了保证安全,通常还需要使用TLS认证
第三种方式:通过
fd
文件描述符的方式,配置格式为:fd://这种格式一般用于
systemd
管理的系统中。
在
Linux
中,有一个叫做
init
的进程,是所有进程的父进程,用来回收那些没有回收的进程,同样的道理,在容器内部,可以通过加上参数
--init
的方式,让 1 号进程管理所有的子进程,例如回收僵尸进程
举个例子示范,以镜像 busybox 为例
此时的 1 号进程为为 sh 进程,如果加上 --init
你会发现,此时的 1 号进程为
docker-init
,而不是
sh
了
docker-proxy 用来将容器启动的端口映射到主机,方便主机的访问。
假设目前启动一个
nginx
容器并将容器的
80
端口映射到主机的
8080
端口
查看容器 IP
此时使用 ps 查看主机是否有 docker-proxy 进程
可以发现当进行端口映射的时候,
docker
为我们创建了一个
docker-proxy
进程,并且通过参数将容器的
IP
和端口传递给
docker-proxy
,然后
proxy
通过
iptables
完成
nat
的转发
从最后一句可以看出,当我们主机访问 8080 端口的时候,
iptable
将流量会转发给
172.17.0.2
的 80 ,从而实现主机上直接访问容器的业务
使用
curl
访问一下
nginx
容器
contained组件
contained主要负责容器的生命周期管理,同时还会负责一些其他的功能
主要负责那些功能?
镜像的管理
接收dockerd的请求
管理存储相关资源
管理网络资源
containerd-shim
的意思是垫片,类似于拧螺丝时夹在螺丝和螺母之间的垫片。
containerd-shim
的主要作用是将
containerd
和真正的容器进程解耦,使用
containerd-shim
作为容器进程的父进程,从而实现重启
containerd
不影响已经启动的容器进程。
ctr
实际上是
containerd-ctr
,它是
containerd
的客户端,主要用来开发和调试,在没有
dockerd
的环境中,
ctr
可以充当
docker
客户端的部分角色,直接向
containerd
守护进程发送操作容器的请求。
Docker镜像使用
来,继续,我们看看镜像是什么。镜像是一个只读的镜像模版且包含了启动容器所需要的文件结构内容。镜像不包含动态数据,构建完成将不会改变
对于镜像都有哪些操作?
对于镜像的操作分为:
-
拉取镜像:通过
docker pull
拉取远程仓库的镜像
-
重命名镜像:通过
docker tag
重命名镜像
-
查看镜像:通过
docker image ls
查看本地已经存在的镜像
-
删除镜像:通过
docekr rmi
删除没有用的镜像
-
构建镜像
-
第一种是通过
docker build
命令基于
dockerfile
构建镜像,推荐
-
第二种是通过
docker commit
基于运行的容器提交为镜像
拉取镜像
拉取镜像直接使用 docker pull 命令即可,命令的格式如下
现在举个例子,这里有个镜像叫做
busybox
,这个镜像集成了上百个常用的
Linux
命令,可以通过这个镜像方便快捷的查找生产环境中的问题,下面我们一起操作一波
首先会在本地镜像库查找,如果不存在本地库则直接去官网拉取镜像。拉取完镜像后随即查看镜像
如果要查看指定的镜像,则使用docker image ls命令进一步查询
我们仔细观察这两个镜像,就会发现这两个镜像的 IMAGE ID其实是一样的,这是什么原因呢
实际上他们都是指向的同一个镜像文件,只不过其别名不一样而已,如果此时不想要mybox镜像,想删除这个镜像
此时再次使用 docker images 查看确实删除了
如何自己构建自己镜像呢
之前说过,有两种方式,一种是通过
docker commit
的方式,一种是
docker build
的方式。首先看看使用容器提交镜像的方式
此时启动了一个busybox容器并进入到容器,并在容器中创建一个文件,并写入内容
此时就在当前目录下创建了一个hello.txt文件并写入了内容。现在新建另外一个窗口,然后提交为一个镜像
然后使用 docker image ls 查看发现确实生成了镜像
然后我们再看看使用 dockerfile 的方式
先看看都有哪些命令
这么多,不存在的,我们先看一个dockerfile就知道如何用了
-
首先第一行表示基于什么镜像构建
-
第二行是拷贝文件
nginx
。
repo
到容器内的
/etc/yum.repos.d
-
第三行为容器中运行
yum install
命令,安装
nginx
命令到容器
-
第四行为生命容器使用 80 端口对外开放
-
第五行定义容器启动时的环境变量
HOST=mynginx
,容器启动后可以获取到环境变量
HOST
的值为
mynginx
。
-
第六行定义容器的启动命令,命令格式为
json
数组。这里设置了容器的启动命令为
nginx
,并且添加了
nginx
的启动参数
-g 'daemon off;'
,使得
nginx
以前台的方式启动。
基本操作已经会了,现在我们看看镜像的实现原理
为了清楚的看见镜像的存储结构,通过
docker build
构建镜像
因为我的
docker
使用的是
overlay2
文件驱动,所以进入到
/var/lib/docker/overlay2
,使用
tree
查看
可以清楚的看到,
dockerfile
的每一行命令都会生成一个镜像层
Docker容器操作
我们通过一个镜像可以轻松的创建一个容器,一个镜像可以有多个容器,在运行容器的时候,实际上是在容器内部创建了这个文件系统的读写副本,如下图所示
容器的生命周期是怎么样的?
容器的生命周期一共有五个状态分别为