Docker:Docker基础知识
docker是什么
docker 是一个基于Go语言的开源应用容器引擎。
docker可以让开发者打包自己的应用到一个轻量级、可移植的容器中,实现容器化。
不同容器内的程序不会相互影响,想删除某个容器应用,通过直接删除掉容器,能够达到最小残留。
这比起直接安装应用,更加方便管理。
看到这些,感觉docker与虚拟机大同小异,那么他们的区别在哪儿?
docker与虚拟机的区别:
虚拟机运行程序时,如下图:
- HardWare:计算机硬件资源
- Kernel:操作系统内核(内核应该包含在操作系统中,为了虚拟机结构图和docker容器结构图区分,单独拿出来了)
- Host OS:宿主机除内核的操作系统
- Hypervisor:计算机管理程序,分配管理宿主机的资源,实现硬件资源虚拟化。Hypervisor能够直接管理调用硬件资源,甚至可以不需要底层操作系统。
- Guest OS:客户机(虚拟机)操作系统
- Dependencies:相关依赖等
- APP:程序主体
docker容器的程序运行结构,如下图:
- Docker daemon:docker守护进程
对比两张结构图:
1、管理
-
虚拟机需要一层Hypervisor去管理虚拟机的运行情况,Hypervisor能够直接管理调用底层资源,所以它承载着虚拟机(VM)
-
而docker则是运行一个进程(docker daemon)去管理容器,不属于父子关系。
比如docker一个容器运行mysql,通过ps命令(或者pstree)查看进程,mysql-master容器进程的父进程是-bash,-bash的父级是sshd,sshd的父级是systemd,中间没有经过守护进程,如下图:
2、内核
-
虚拟机中还有一个Kernel(操作系统内核),所以虚拟机是不依赖宿主机内核的,只要有Hypervisor,虚拟机就都能够运行。
-
docker的容器中没有内核,只有一层操作系统。因为docker容器的运行都依赖于宿主机内核,这意味着docker只能在linux上运行(其他系统都需要有linux虚拟机),且不能在低版本 Linux宿主机 运行 高版本Linux容器。
3、技术
-
虚拟化,虚拟机的Hypervisor能够直接管理硬件资源,实现硬件隔离。
-
容器化(容器虚拟化),docker daemon依赖宿主机内核的cGroups与namespace特性,实现对容器的资源限制隔离。
docker的三个基本概念
docker的三个基本概念:
- 镜像(Image):用于docker容器的创建运行,方便移植。类似于代码,可以放到其他服务器上去运行。
- 容器(Container):docker运行程序的载体。通过镜像创建容器,可以看成代码运行起来,在内存中开辟的空间,只要有代码都可以运行出同样的程序。此时删除镜像,容器不会发生改变。
- 仓库(Repository):镜像的存储空间,可以看成代码仓库,比如github。
docekr的配置与使用
[Linux][阿里服务器]CentOS7联网安装docker-ce(yum)
[Linux][阿里服务器]CentOS7离线安装docker-ce
docker如何实现容器化
docker的容器化体现在资源限制与容器间隔离,主要依赖于内核的cgroups与namespace特性。
首先看一下这两个特性的作用
docker容器的资源限制,通过cGroups(control groups)实现,
它最主要的作用,就是限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽等等。以文件目录形式 挂载在 /sys/fs/cgroup。
docker容器间的隔离,是通过 namespace 命名空间进行的划分,在不同的 namespace 下 进程相互不可见。
namespace 也分为:
- pid ns(管理进程id PID: Process ID)、
- net ns(管理网络 NET: Networking)、
- mnt ns(管理挂载点 MNT: Mount)、
- ipc ns(管理进程间通信 IPC: InterProcess Communication)
- uts ns(Unix时间系统隔离 UTS: Unix TimesharingSystem)
那么,这两个特性是对进程组的限制,和docker容器有什么关系?
docker容器 实际上就是一个个进程,可以通过 ps -ef 查看运行容器的进程,一个容器可能存在多个进程。
比如,现在我创建一个镜像,里边有一个java程序,一个mysql。
使用docker run运行容器后,该容器至少存在java进程与mysql进程。
这样看来,容器和其中的进程并不能画等号
有时会遇到这样的情况:
内存使用空间过高,导致OOM,
触发的OOM Killer,会杀死容器的某个或某些进程,导致容器存在,但是进程已死 容器无法使用。
解决方案一,
强制kill容器
docker kill <容器别名或者容器id>
,
然后启动容器docker start <容器别名或者容器id>
解决方案二(此方法由于删除容器,容器数据若无挂载数据卷,将导致数据丢失),
强制移除容器
docker rm -f <容器别名或者容器id>
,
然后清理网络占用docker network disconnect --force bridge <容器别名或者容器id>
docker的信号机制
docker stop 与 docker kill 均可以将容器停掉,但二者究竟有什么区别呢?首先,摘录一下官网对这两个功能的描述:
docker stop: Stop a running container (send SIGTERM, and then SIGKILL after grace period) [...] The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL. [emphasis mine]
docker kill: Kill a running container (send SIGKILL, or specified signal) [...] The main process inside the container will be sent SIGKILL, or any signal specified with option --signal. [emphasis mine]
docker stop,支持“优雅退出”。
先发送SIGTERM信号,在一段时间之后(10s)再发送SIGKILL信号。
Docker内部的应用程序可以接收SIGTERM信号,然后做一些“退出前工作”,比如保存状态、处理当前请求等。
docker kill,发送SIGKILL信号,应用程序直接退出。
两种停止容器的方式与 linux下的kill命令的-15(结束必要工作后退出)和-9(强制kill)效果相似。
线上应用优雅退出十分必要。docker stop也不是docker独有的设计,lxc和google borg系统都有类似设计,即在发送SIGKILL之前,发送SIGTERM信号通知任务。
之前有遇到一次因为强制kill导致docker hang死的经历:
当时是由于磁盘使用空间已满,导致一个k8s容器发生故障,清理磁盘后决定重启该容器。
发现无法停止该容器,docker stop后容器依然存在,判断此时容器内进程已停止,使用 docker kill强制停止容器。
重启后发现,容器仍旧没有正常工作,决定重启docker。
停止docker后,发现启动docker后 docker命令无法使用,docker ps 会卡死没有反应。
再次停止docker,检查docker状态,发现停止docker的信号是SIGNKILL,导致docker守护进程没有正常停止。kill 掉守护进程,启动docker之后 可以正常使用。
docker文件目录
不同版本的docker目录不一样,通用目录一般有:
- containers:存放容器基本信息。目录名称对应容器ID,
- config.v2.json 存储环境变量、容器创建时间、容器路径、容器名称、容器网关,文件驱动、网桥等信息
- hostconfig.json 存储容器数据映射、cpu限制、磁盘限制、内存限制、掩码、网关等信息
- hostname 存储容器内部的主机名
- hosts 存储容器内部的主机配置
- image:存放镜像基本信息
- network:存放容器网络连接信息
- swarm
- temp:临时数据
- trust:信任文件
- volumes:容器卷数据信息
- overlay/overlay2/aufs:存储镜像层、容器层文件。
overlay/overlay2/aufs是三种不同的文件存储驱动,以前docker首选aufs驱动,但是有些linux发行版不支持aufs。目前常见overlay和overlay2。
docker的文件存储驱动
overlayFS是和aufs相似的联合文件系统。
docker的overlay存储驱动利用overlayFS的一些特征来构建管理镜像和容器的磁盘结构。
overlay
overlay应用两个目录,upperdir和lowerdir,分别是容器层和镜像层。
通过联合挂载技术将镜像层和容器层挂载到同一层,提供统一视图。
docker pull 拉取镜像后,在docker目录下的overlay目录中会增加对应的镜像目录,镜像目录的名称和镜像ID并不是对应的(
docker1.10开始,使用基于内容的寻址,因此目录名和镜像层的id不一致)。
镜像目录中的root目录为镜像层的所有目录文件。
docker 启动一个容器后,overlay目录下会生成两个目录,xxx....xx和xxx....xx-init。
xxx....xx为读写层,xxx....xx-init为初始层,初始层存储容器的相关环境信息,对容器的修改都是存在于读写层。
overlay2
overlay2 可以实现多层文件结构,镜像层和容器层都有diff和work两个目录,diff用于存储内容,work用于存储修改时的内容。
容器的读写层 多一个merged目录,该目录为联合挂载后的视图目录,容器内的目录文件就是此文件下的文件结构。