docker简介
docker是什么
问题:为什么会有docker出现
一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的迭代之后,不同版本环境的兼容,对运维人员都是考验
Docker之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案。
环境配置如此麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。开发人员利用 Docker 可以消除协作编码时“在我的机器上可正常工作”的问题。
之前在服务器配置一个应用的运行环境,要安装各种软件,就拿尚硅谷电商项目的环境来说吧,Java/Tomcat/MySQL/JDBC驱动包等。安装和配置这些东西有多麻烦就不说了,它还不能跨平台。假如我们是在 Windows 上安装的这些环境,到了 Linux 又得重新装。况且就算不跨操作系统,换另一台同样操作系统的服务器,要移植应用也是非常麻烦的。
传统上认为,软件编码开发/测试结束后,所产出的成果即是程序或是能够编译执行的二进制字节码等(java为例)。而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让维运团队得以部署应用程式,开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。Docker镜像的设计,使得Docker得以打破过去「程序即应用」的观念。透过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由下而上打包,达到应用程式跨平台间的无缝接轨运作。
docker理念
Docker是基于Go语言实现的云开源项目。
Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。
Linux 容器技术的出现就解决了这样一个问题,而 Docker 就是在它的基础上发展过来的。将应用运行在 Docker 容器上面,而 Docker 容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作
一句话
解决了运行环境和配置问题,方便做持续集成并有助于整体发布的容器虚拟化技术。
docker能干嘛
之前的虚拟机技术
虚拟机(virtual machine)就是带环境安装的一种解决方案。
它可以在一种操作系统里面运行另一种操作系统,比如在Windows 系统里面运行Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。
虚拟机的缺点:
1 资源占用多 2 冗余步骤多 3 启动慢
容器虚拟化技术
由于前面虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
比较了 Docker 和传统虚拟化方式的不同之处:
*传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
*而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
* 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。
开发/运维(DevOps)
一次构建、到处运行
更快速的应用交付和部署
传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂的配置才能正常运行。Docker化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间。
更便捷的升级和扩缩容
随着微服务架构和Docker的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个Docker容器将变成一块“积木”,应用的升级将变得非常容易。当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒级。
更简单的系统运维
应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复。
更高效的计算资源利用
Docker是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的Hypervisor支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的CPU和内存的利用率。
应用docker的企业
- 新浪
- 美团
- 蘑菇街
- 。。。等等
docker在哪下载
- 1.官网
- docker官网:http://www.docker.com
- docker中文网站:https://www.docker-cn.com/
- 2.仓库
- Docker Hub官网: https://hub.docker.com/
docker安装
前提说明
CentOS Docker 安装
Docker支持以下的CentOS版本:
CentOS 7 (64-bit)
CentOS 6.5 (64-bit) 或更高的版本
前提条件
目前,CentOS 仅发行版本中的内核支持 Docker。
Docker 运行在 CentOS 7 上,要求系统为64位、系统内核版本为 3.10 以上。
Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位、系统内核版本为 2.6.32-431 或者更高版本。
查看自己的内核
uname -r或者cat /etc/redhat-release 可以查看当前系统相关信息(内核版本号、硬件架构、主机名称和操作系统类型等)。
查看已安装的CentOS版本信息(CentOS6.8有,CentOS7无该命令)
docker的基本组成
镜像(image)
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。
容器(container)
Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。
它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
仓库(repository)
仓库(Repository)是集中存放镜像文件的场所。
仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub(https://hub.docker.com/),
存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云 等
小总结
需要正确的理解仓储/镜像/容器这几个概念:
Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就似乎 image镜像文件。只有通过这个镜像文件才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。
* image 文件生成的容器实例,本身也是一个文件。
* 一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器
* 至于仓储,就是放了一堆镜像的地方,我们可以把镜像发布到仓储中,需要的时候从仓储中拉下来就可以了。
docker架构图
安装步骤
1.centos6.8安装docker
由于我用的不是centos6.8,因此只简单介绍一下如何安装和启动
yum -y install epel-release
Docker使用EPEL发布,RHEL系的OS首先要确保已经持有EPEL仓库,否则先检查OS的版本,然后安装相应的EPEL包。
yum -y install docker-io
安装后的配置文件:/etc/sysconfig/docker
启动docker后台服务:service docker start
docker version验证
2.centos7安装docker
我用的centos7,后续都是以centos7为例
yum -y install docker即可
启动docker服务,systemctl start docker
测试
docker version 查看docker版本 docker run hello-world 运行镜像hello-world
配置镜像加速
默认拉取镜像是从国外的网站进行拉取,不连vpn会灰常灰常的慢。那么如何配置,从国内的网站拉取镜像呢? vim /etc/docker/daemon.json 编辑这个文件 输入下面内容 { "registry-mirrors": ["https://iwpfe8zv.mirror.aliyuncs.com"] ##加入自己的镜像加速器 } 表示从阿里云上面拉取镜像 然后重启docker systemctl restart docker
如何卸载docker
systemctl stop docker 停止docker服务 yum remove -y docker 卸载docker rm -rf /var/lib/docker 删除docker相关的残留文件
永远的hello-world
阿里云镜像加速
是什么
https://dev.aliyun.com/search.html
注册一个属于自己的阿里云账户(可复用淘宝账号)
获得加速器地址链接
- 登陆阿里云开发者平台
- 获取加速器地址
配置本机docker运行镜像加速器
鉴于国内网络问题,后续拉取 Docker 镜像十分缓慢,我们可以需要配置加速器来解决, 我使用的是阿里云的本人自己账号的镜像地址(需要自己注册有一个属于你自己的): https://xxxx.mirror.aliyuncs.com * vim /etc/docker/daemon.json 将获得的自己账户下的阿里云加速地址配置进 { "registry-mirror":["https://你自己的账号加速信息.mirror.aliyuncs.com"] }
重新启动docker后台服务:systemctl restart docker
网易云加速
基本同阿里云
启动docker后台容器(测试运行hello-world)
docker run hello-world
输出这段提示以后,hello world就会停止运行,容器自动终止。
run干了什么
根据镜像生成一个容器实例
底层原理
docker是怎么工作的
Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。我们输入命令,docker通过client将我们的命令传给server,然后守护进程来管理docker所创建的容器,比如删除、重启等等 。
所以docker的logo很形象,一个鲸鱼飘在大海里,上面背着很多的集装箱。这个大海就是我们的宿主机,直接使用宿主机的资源,大鲸鱼就相当于是docker,鲸鱼上面的集装箱就是一个个的容器,里面运行着各种服务,而且每个集装箱都是相互隔离的,不会对其他的集装箱造成污染。
为什么docker比虚拟机快
(1)docker有着比虚拟机更少的抽象层。由亍docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
(2)docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。仍而避免引寻、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载Guest OS,返个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了返个过程,因此新建一个docker容器只需要几秒钟。
docker镜像
镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
UnionFS(联合文件系统)
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
docker镜像加载原理
Docker镜像加载原理:
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
。
平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M??
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。
分层的镜像
以我们的pull为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载
为什么docker镜像要采用这种分层结构呢
最大的一个好处就是 - 共享资源
比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,
同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
镜像的特点
Docker镜像都是只读的
当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
镜像的常用命令
帮助命令
docker version
docker info
docker --help
镜像命令
查看镜像
docker images:列出本地主机上的所有镜像
各个选项说明:
REPOSITORY:表示镜像的仓库源
TAG:镜像的标签
IMAGE ID:镜像ID
CREATED:镜像创建时间
SIZE:镜像大小
同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。
如果你不指定一个镜像的版本标签,例如你只使用 ubuntu,docker 将默认使用 ubuntu:latest 镜像
比如docker pull mysql:5.6表示安装5.6版本的mysql,如果不指定,则默认安装最新的mysql
options说明
-a:列出本地所有的镜像(含中间镜像层)
-q:只显示镜像的id
--digests:显示镜像的摘要信息
--no-trunc:显示完成的镜像信息
搜索镜像
命令:
docker search [options] 镜像名称
其中stars类似于GitHub上面的star,越多表示越受欢迎
options说明
--no-trunc:显示完整的镜像描述
-s:列出stars数不少于指定值的镜像
这里提示我们-s 3000这种方法已被废弃了,请使用--filter=stars=3000这种方式
--automated:只列出automated build类型的镜像
下载镜像
docker pull 镜像名称[:TAG] 下载(对应版本的)镜像
会什么会有这么多,因为镜像是分层的
删除镜像
删除单个镜像:docker rmi -f 镜像id
这里的-f表示强制删除,如果没有使用该镜像创建容器的话,那么加不加-f是没有区别的,但是一旦该使用该镜像创建了容器并且启动的话,那么要删除必须加上-f,否则报错。值得一提的是,即便删除了镜像,已经创建的容器也不会消失,并且仍然可以正常运行,因为这个容器已经被创建出来了。
为什么会删除这么多,之前也说过,镜像是分层的,镜像里面还有镜像。但是对外显示的只有一层,至于为什么设计成分层,上面也说了,这是docker镜像的设计原理。但是我们使用就当只有一层就行。
删除多个镜像:docker rmi -f 镜像名1:TAG 镜像名2:TAG······
删除全部镜像:docker rmi -f $(docker images -qa)或者 docker images -qa|xargs docker rmi -f
思考
结合学习git的新的,猜猜是否docker commit和docker push
docker容器
容器是什么?
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
容器特点
Docker镜像都是只读的
当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
容器常用命令
新建并启动容器
docker run [options] 镜像名
options说明
OPTIONS说明(常用):有些是一个减号,有些是两个减号 --name="容器新名字": 为容器指定一个名称; -d: 后台运行容器,并返回容器ID,也即启动守护式容器; -i:以交互模式运行容器,通常与 -t 同时使用; -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用; -P: 随机端口映射; -p: 指定端口映射,有以下四种格式 ip:hostPort:containerPort ip::containerPort hostPort:containerPort containerPort
启动交互式容器
此时我便进入了通过docker启动的centos,这个centos保留了最核心的部分,使用的资源都是宿主机的资源
列出当前所有正在运行的容器
docker ps[options]
OPTIONS说明(常用):
不加选项:列出当前正在运行的容器
-a :列出当前所有正在运行的容器+历史上运行过的
-l :显示最近创建的容器。
-n:显示最近n个创建的容器。
-q :静默模式,只显示容器编号。
--no-trunc :不截断输出。
container id:容器的id
image:容器是由哪个镜像创建的
created:创建时间
status:容器状态,是在运行啊,还是在多长时间之前退出
names:容器的名字
退出容器
exit:容器停止、退出
exit之后,使用docker ps显示正在运行的容器,发现没有正在运行的容器
ctrl+p+q:容器不停止、退出
按下ctrl+p+q之后,使用docker ps发现之前的容器还在运行
启动容器
docker start 容器id或者容器名
启动之前创建的容器
一开始是没有正在运行的容器的,但是这个容器确实被创建出来了,只不过退出了。我们通过最近创建的容器找到id或者name,然后docker start 容器id进行启动。顺便一提这个id可能有点长,我们也可以只输入前几位,比如6位。
重启容器
docker restart 容器id或者容器名
重新启动容器
一般重新启动,都是针对正在运行的容器来说。就像Windows,重新启动只有电脑开机的时候,才有重新启动这一说。但是docker restart也可以对没有启动的容器使用,同时start也可以对已经启动的容器来使用
此时容器正在运行,我们可以对其进行start
此时容器还没有运行,我们可以进行restart,相当于start
停止容器
docker stop 容器id或者容器名
停止正在运行的容器
即便容器没有运行,也可以使用这个命令,返回容器的id
强制停止容器
docker kill 容器id或者容器名
这个docker stop有什么区别呢?
docker stop是温柔停止,类似于关机
docker kill强制停止,类似于拔电源
删除容器
删除已停止的容器
docker rm 容器id或者容器名, 删除镜像是docker rmi,删除容器则是docker rm
docker ps查看没有正在运行的容器,随便删除一个,返回容器id
删除多个容器:docker rm 容器id1 容器id2 容器id3···· 删除所有容器:docker rm $(docker ps -aq) 或者docker ps -aq | xargs docker rm
我们来多创建几个容器,之前说过一个镜像可以创建多个容器。
重要
启动守护式容器
docker run -d 镜像
我们发现在启动之后,使用docker ps并没有列出,而使用docker ps -l查看,发现已经退出了,这是怎么回事? 很重要的要说明的一点: Docker容器后台运行,就必须有一个前台进程. 容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。 这个是docker的机制问题,比如你的web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动响应的service即可。例如 service nginx start 但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用, 这样的容器后台启动后,会立即自杀因为他觉得他没事可做了. 所以,最佳的解决方案是,将你要运行的程序以前台进程的形式运行 所以docker run -d镜像或者docker run 镜像之后,会直接返回一串id,因为容器启动之后会立即自杀,尽管如此,但是这个容器已经被创建出来了。 当我们使用-it的时候不会自动退出,因为相当于创建了一个伪终端
查看容器日志
docker logs -f -t --tail 容器id -t是加入时间戳 -f跟随最新的日志打印 --tail 数字 显示最后多少条
执行以下命令
docker run -d centos /bin/sh -c "while true;do echo hello world;sleep 2;done"
每隔两秒钟打印一次
查看容器内部运行的进程
docker top 容器id
查看容器内部细节
docker inspect 容器id
很长很长,只截取一部分
进入正在运行的容器并与之交互
之前其实有一个问题没有说,是什么呢? 那就是当我们使用ctrl+p+q的时候,会在不停止容器的情况下退出,容器会依旧在运行,但是如果我们想要再次进入已经停止的容器的话,该怎么办呢?
docker attach 容器id或者容器名
我们在容器里输入python,进入交互式环境,然后按下ctrl+p+q退出,再使用docker attch进入,发现依旧回到了当初退出时的状态。
这个命令会直接启动命令行的终端,不会启动新的进程。
docker exec -it 容器id或者容器名 bashShell
docker exec -it 容器id或者容器名 bashShell(说白了就是终端命令)
这个命令是在容器中打开新的终端,比如说,我们在linux上开了一个终端,attch是在原来的终端上执行,exec则是新打开了一个终端,并且可以启动一个新的进程。
从容器内拷贝文件到主机上
比如我这个容器不想要了,但是我里面有一个很重要的文件,这个文件我想把它拿到宿主机上,该怎么做呢? docker cp 容器id:容器路径 目的主机路径
进入my_centos,创建一个文件,然后退出容器,使用cp命令将文件拷贝到主机上
说到拷贝文件,我想起了镜像。在镜像的时候忘记说了。如果没有网络,我们如何将镜像打包拷贝到另一个机器上呢,既然要拷贝到另一台机器上,肯定要先拷贝到本机上。
docker save 镜像名称 > 起一个文件名 :这个过程将镜像打包,打包的后缀名一般为.tar
这里将镜像打包,然后删除镜像。然后使用docker images发现已经没有镜像了
docker load < 将镜像打包的文件名 : 这个过程将打包的文件加载为镜像
再使用load,发现镜像又回来了。
镜像commit
为什么要把镜像放到容器里面来,因为镜像commit是把一个容器变成一个镜像.
比如我们启动了一个容器,在这个容器里面我们做了相应的操作,我们想把当前这个已经做了操作的容器变成一个镜像
docker commit -m="提交的容器信息" -a="作者" 容器id 要创建的目标镜像名[:TAG]
运行tomcat,docker run -it -p 8880:8080 tomcat
-p 主机端口:容器里的服务端口,我们Tomcat默认端口是8080,但是这是在容器里面的。我们是要通过容器来访问容器里面的Tomcat,相当于是爬到鲸鱼背上的集装箱(容器),访问集装箱里面的tomcat,因为我们是要通过docker容器对外暴露的端口来访问,一般为了保持一致,docker容器指定的端口和服务(比如tomcat)的默认端口设置成一样的,但是我们也可以设置成不一样的.因此这里就只能通过8880来访问了
打开浏览器,输入localhost:8880,出现界面如下说明访问成功
除了-p 主机端口:容器里的服务端口,还有-P指定随机端口
docker run -it -P tomcat
还是用指定端口8880重新打开tomcat,点开文档也能访问
然后删除tomcat容器的文档
再次进入打开tomcat官网
以当前运行的(没有文档的)tomcat容器,commit一个新镜像。
docker commit [options] 容器id 新的镜像名
options选项: -a:作者 -m:信息 此外新的镜像名可以是 xx/yy这种格式,xx可以看成是java里面的包名(命名空间),yy可以看成是类名(镜像名)
输入命令:docker commit -a="satori" -m="del docs" 容器id new/my_tomcat:1.5
可以看到此时已经多了一个我们自己创建的镜像,而且此时的tag是1.5,不再是latest
启动我们自己的镜像
而用原来的tomcat是有文档的。
所以这就是提交容器的副本,产生镜像。我们既可以通过镜像生成容器,也可以由容器产生镜像。类似于java里的反射机制,也可以由实例来得到类的信息。
如果以后台启动
可以看到没有了那些哗啦哗啦一大串,因为是以后台启动,只返回一串id,如果输入localhost:6666发现是依旧可以访问的,这里不再演示了
常用命令总结
部分命令会在后面介绍
attach Attach to a running container # 当前 shell 下 attach 连接指定运行镜像
build Build an image from a Dockerfile # 通过 Dockerfile 定制镜像
commit Create a new image from a container changes # 提交当前容器为新的镜像
cp Copy files/folders from the containers filesystem to the host path #从容器中拷贝指定文件或者目录到宿主机中
create Create a new container # 创建一个新的容器,同 run,但不启动容器
diff Inspect changes on a container's filesystem # 查看 docker 容器变化
events Get real time events from the server # 从 docker 服务获取容器实时事件
exec Run a command in an existing container # 在已存在的容器上运行命令
export Stream the contents of a container as a tar archive # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history Show the history of an image # 展示一个镜像形成历史
images List images # 列出系统当前镜像
import Create a new filesystem image from the contents of a tarball # 从tar包中的内容创建一个新的文件系统映像[对应export]
info Display system-wide information # 显示系统相关信息
inspect Return low-level information on a container # 查看容器详细信息
kill Kill a running container # kill 指定 docker 容器
load Load an image from a tar archive # 从一个 tar 包中加载一个镜像[对应 save]
login Register or Login to the docker registry server # 注册或者登陆一个 docker 源服务器
logout Log out from a Docker registry server # 从当前 Docker registry 退出
logs Fetch the logs of a container # 输出当前容器日志信息
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT # 查看映射端口对应的容器内部源端口
pause Pause all processes within a container # 暂停容器
ps List containers # 列出容器列表
pull Pull an image or a repository from the docker registry server # 从docker镜像源服务器拉取指定镜像或者库镜像
push Push an image or a repository to the docker registry server # 推送指定镜像或者库镜像至docker源服务器
restart Restart a running container # 重启运行的容器
rm Remove one or more containers # 移除一个或者多个容器
rmi Remove one or more images # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run Run a command in a new container # 创建一个新的容器并运行一个命令
save Save an image to a tar archive # 保存一个镜像为一个 tar 包[对应 load]
search Search for an image on the Docker Hub # 在 docker hub 中搜索镜像
start Start a stopped containers # 启动容器
stop Stop a running containers # 停止容器
tag Tag an image into a repository # 给源中镜像打标签
top Lookup the running processes of a container # 查看容器中运行的进程信息
unpause Unpause a paused container # 取消暂停容器
version Show the docker version information # 查看 docker 版本号
wait Block until a container stops, then print its exit code # 截取容器停止时的退出状态值
docker容器数据卷
是什么?
先来看看Docker的理念:
* 将运用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的
* 容器之间希望有可能共享数据
Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来,
那么当容器删除后,数据自然也就没有了。我们之前介绍了一个docker cp命令,可以将容器内的数据拷贝到宿主机当中,但是有没有更简单的办法呢?可以不用我们显式的调用命令,而是实现自动关联,我们在容器中新建文件或者修改文件可以自动地同步到宿主机当中呢?
答案是可以的,在docker中我们使用卷的方式。
一句话:有点类似我们Redis里面的rdb和aof文件
能干嘛?
卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷
特点:
1:数据卷可在容器之间共享或重用数据
2:卷中的更改可以直接生效
3:数据卷中的更改不会包含在镜像的更新中
4:数据卷的生命周期一直持续到没有容器使用它为止
- 容器的持久化
- 容器间继承+共享数据
数据卷
直接命令添加
docker run -it /宿主机绝对路径目录:/容器目录 镜像名
此时启动了容器,显然宿主机当前目录是没有host这个目录的,同理容器里面也没有container这个目录。但是当我执行完这条命令之后,我们再来看看。
发现目录自动生成了,这显然是docker帮我们创建的。此时宿主机的host目录和容器的container目录已经共享了。在一个文件夹里面操作会同步到另一个文件夹上。
查看宿主机和容器之间的数据是否共享
突然发现我们刚才的启动方式是有一定问题的,在centos7上面,会发现没有权限。 因此解决这种问题,可以加上一个--privileged=true 因此我现在重新启动 docker run -it --privileged=true -v /home/satori/host:/container centos
因此可以看出来宿主机和容器之间的数据是完全共享的
容器停止退出后,主机修改后数据是否同步
现在问题来了,我将容器退出之后。不是ctrl+p+q命令,容器不停止退出。而是使用exit命令,让容器停止退出,然后在宿主机的目录里面继续创建文件,写数据。然后再次启动容器,那么在容器停止退出时我在宿主机上做的操作会不会也同步到容器的目录里面去呢?
可以看到即使容器停止退出之后,我修改数据依旧会同步。类似于redis的rdb和aof数据持久化,当我启动容器之后我再加载进去就可以了。
带权限
docker run -it --privileged=true -v /home/satori/host:/container:ro centos 只需要在容器的目录后面加上一个ro即可,表示read only,只读
因此可以看到,如果是以只读方式创建容器,那么在宿主机里面是可以修改并创建文件的,但是在容器里面不行,至于数据本身,在宿主机里面进行的操作依旧会进行同步的。
查看容器内部细节
可以使用之前的docker inspect 命令
DockerFile添加
DockerFile会在下一章详细介绍,但是现在可以提前了解一下。
DockerFile相当于是对镜像的描述,可以对DockerFile进行build,得到镜像。如果我们想修改或者创建镜像的话,那么就可以修改或者创建DockerFile文件。
DockerFile相当于是源码文件,镜像相当于是编译之后的字节码。java和python运行的都是字节码文件,如果我们想修改字节码,那么就要修改源码,再重新编译为字节码。DockerFile也是一样的
新建一个目录,就叫mydocker吧,并进入
可在Dockerfile中使用VOLUME指令来给镜像添加一个或多个数据卷
VOLUME["/dataVolumeContainer","/dataVolumeContainer2","/dataVolumeContainer3"] 说明: 出于可移植和分享的考虑,用-v 主机目录:容器目录这种方法不能够直接在Dockerfile中实现。 由于宿主机目录是依赖于特定宿主机的,并不能够保证在所有的宿主机上都存在这样的特定目录。
DockerFile构建
FROM centos VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"] CMD echo "finished,--------success1" CMD /bin/bash
dockerfile会在下一章介绍,先来简单看一看,第一行的 from centos,相当于继承,extend。之前说过镜像是分层的,这样可以共享。比如tomcat,总共四百多兆,这显然太大了。但是如果看tomcat的DockerFile文件的话,会发现开头写着from open-jdk1.8,说明tomcat是包含了jdk的,所以才会这么大。不然只有tomcat没有jdk是没法运行的,因此在删除tomcat的时候,会发现删除的不止一层。镜像就像花卷或者俄罗斯套娃一样,一层套着一层。
VOLUME则是数据卷,里面可以有多个目录。
build生成镜像
docker build -f DockerFile文件 -t 生成的镜像名称 生成在哪个目录
运行生成的镜像
运行镜像之后,发现自动为我们创建了这两个文件夹。
分别创建一个创建文件
但是我们发现,在一开始我们没有向
-v 主机目录:容器目录
那样指定宿主机的目录啊,docker想到了这一点,为什么生成了默认的。怎么查看呢?docker inspect
数据卷容器
是什么?
命名的容器挂载数据卷,其它容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器
宿主机相当于电脑,容器相当于硬盘,电脑的数据放到硬盘里。但是如果我有很多的容器呢?因此数据卷容器,相当于硬盘挂载到硬盘上,这样硬盘之间的数据也可以共享。
创建多个容器
以我们之前创建的centos,来创建名为dc01、dc02、dc03的三个容器
那么现在问题来了,我创建dc02,要挂载到dc01上。因此使用
--volumes-from 容器
表示挂载到某个容器上所以说容器之间是共享数据的,dc02和dc03都继承自dc01,我们在dc01里面创建的文件,会同步到dc02和dc03里面去,但是我们在dc02和dc03里面做的修改也会作用到dc01里面。因此docker容器不仅仅是父到子,还是子到父。尽管dc02和dc03都继承自dc01,但是三者是共享的。在dc01创建文件会同步到dc02和dc03里面去,同理在dc02创建文件也会同步到dc01和dc03里面去,在dc03创建文件也会同步到dc01和dc02里面去。
删除dc01
那么问题来了,现在dc02和dc03都是继承自dc01,那如果我把dc01删掉,然后在dc02里面创建文件,那么会不会也作用到dc03里面去呢
说明容器之间数据是共享的。
我再创建dc04继承自dc03
发现在dc01和dc02中同步到dc03里面的数据,通通都被同步到dc04里面去了。
删除dc02、dc03
结论
容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止。 通俗点的说,只要没死绝,那么都可以进行全量的备份。
DockerFile解析
是什么?
DockerFile是用来构建Docker镜像的文件,是由一系列命令和参数构成的脚本
构建三步骤
- 编写DockerFile文件,当然要符合相应的规范
- docker build生成镜像
- docker run
文件长什么样?
from scratch,表示这个镜像来自于哪个镜像,scratch这个镜像是所有镜像的基础镜像,相当于所有类的基类object。
maintainer,作者信息
add,后面说
label,标签说明
cmd,默认执行的命令
我们之前写过 docker run -it centos,也可以写docker run -it /bin/bash,如果不加/bin/bash会默认执行,表示当前用户使用的shell是/bin/bash
DockerFile构建过程解析
DockerFile内容基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数,我们之前用小写是不准确的
- 指定按照从上到下,顺序执行
表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交。
docker执行DockerFile的大致流程
- docker执行基础镜像运行一个容器
- 执行每一条指令对容器做出修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新的容器
- 再执行DockerFile中的下一条指令,重复相同的操作,直到所有的指令都完成
- 最终形成一个新的镜像。
DockerFile、docker镜像、docker容器
- DockerFile是软件的原材料
- docker镜像是软件的交付品
- docker容器则可以认为是软件的运行态
DockerFile面向开发,docker镜像成为交付标准,docker容器则涉及部署和运维,三者缺一不可,合力充当docker体系的基石
总结
1 Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
2 Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时,会真正开始提供服务;
3 Docker容器,容器是直接提供服务的。
DockerFile体系结构(保留字指令)
FROM
基础镜像,当前要创建的镜像是基于哪一个镜像的。 比如centos基于scratch,自己创建的镜像FROM centos 的话,等于也基于scratch
MAINTAINER
镜像维护者的姓名和邮箱地址
RUN
容器构建时需要运行的命令
EXPOSE
容器运行时对外暴露出来的端口
WORKDIR
指定在创建容器时,终端登录进来的默认的工作目录,一个落脚点
ENV
用来在构建镜像过程中设置的环境变量 ENV MY_PATH /usr/mytest 这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样; 也可以在其它指令中直接使用这些环境变量, 比如:WORKDIR $MY_PATH
ADD
将宿主机目录下的文件或目录拷贝到镜像,且ADD命令会自动处理url和解压tar包
COPY
类似于ADD,拷贝文件或目录到镜像中 将从构建上下文目录中<源路径>的文件或目录复制到新的一层的镜像内的<目标路径>位置 copy src dst copy ["src", "dst"]
VOLUME
容器数据卷,用于数据保存和持久化工作
CMD
提交一个容器启动时要运行的命令 DockerFile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run -it centos之后的参数替换
ENTRYPOINT
作用和CMD一样,只不过多个命令不会覆盖,而是会追加
ONBUILD
当个构建一个被继承的DockerFile时运行命令,当父镜像被子镜像继承后,子镜像运行时,父镜像会触发onbuild,类似于一个触发器。或者理解为是父镜像里面的一个回调函数,当子镜像运行时,会触发父镜像的回调函数。
案例
Base镜像(scratch)
DockerHub中百分之99的镜像都是通过在base镜像中安装和配置需要的软件构建出来的
自定义镜像mycentos
1.编写
默认的centos镜像是什么样子
那么我们的任务就是,自定义一个centos。具备的特征是:进入之后要默认在/fuck_shanshan目录,支持vim,支持ifconfig
编写DockerFile
# 继承自centos FROM centos # 将/fuck_shanshan设置为环境变量,命名为fs ENV fs /fuck_shanshan # 执行命令,创建目录 RUN mkdir $fs # 指定工作区 WORKDIR $fs # 执行命令安装vim RUN yum -y install vim # 执行命令安装ifconfig RUN yum -y install net-tools # 暴露端口为80 EXPOSE 80 # 指定shell为/bin/bash CMD /bin/bash # 注意:注释不可以可指令写在同一行,像编程语言的话,可以有行内注释,但是dockerfile不可以,不能和指令写在同一行,必须单独写一行
2.构建
3.运行
可以看到,当我们一进去,就已经在/fuck_shanshan目录下了
4.列出镜像的变更历史
docker history 镜像
可以看到,类似于栈一样,从底往上。
CMD/ENTRYPOINT 镜像案例
两者都是指定一个容器启动时要运行的命令
CMD
DockerFile中可以有多个CMD指令,但是只有最后一个生效,CMD会被
docker run 镜像
之后的参数替换。怎么理解呢?我们看看tomcat的dockerfile的最后两句
EXPOSE 8080 CMD ["catalina.sh", "run"]
表示暴露端口为8080,然后执行CMD ["catalina.sh", "run"]。docker run -it tomcat之所以可以启动服务,输入localhost:8080还能看到那只猫,是因为最后一条命令。但是如果我们自己指定参数,那么会咋样呢
为什么会有这个结果,因为我们的ls -l把tomcat的dockerfile文件中的CMD命令给覆盖了,所以只是执行了ls -l,没有启动tomcat
ENTRYPOINT
docker run -it 镜像 之后的参数会被当做参数传递给ENTRYPOINT,之后形成新的命令组合
case
比如我想创建一个centos,能够打印指定目录的centos
vim dockerfile1 输入如下内容 FROM centos CMD ["ls", "-l"] vim dockerfile2 输入如下内容 FROM centos ENTRYPOINT ["ls", "-l"] 然后build docker build -f ./dockerfile1 -t centos1 . docker build -f ./dockerfile2 -t centos2 .
运行两个镜像
运行centos1
运行centos2
区别
两者的区别就在于此。都是运行容器时执行指令。CMD是如果我们自己跟了参数选择覆盖,ENTRYPOINT则是我们自己跟了参数选择追加。
onbuild镜像案例
创建镜像father
FROM centos CMD ["bin","bash"] ONBUILD RUN echo "triggerred -------------"
创建子镜像,继承父镜像
FROM father CMD ["bin", "bash"]