Docker基础知识点
Docker 的组成:
https://docs.docker.com/engine/docker-overview/
Docker 主机(Host):一个物理机或虚拟机,用于运行Docker 服务进程和容器。
Docker 服务端(Server):Docker 守护进程,运行docker 容器。
Docker 客户端(Client):客户端使用docker 命令或其他工具调用docker API。
Docker 仓库(Registry): 保存镜像的仓库,类似于git 或svn 这样的版本控制系
Docker 镜像(Images):镜像可以理解为创建实例使用的模板。
Docker 容器(Container): 容器是从镜像生成对外提供服务的一个或一组服务。
官方仓库: https://hub.docker.com/
Docker 对比虚拟机:
资源利用率更高:一台物理机可以运行数百个容器,但是一般只能运行数十个虚
拟机。
开销更小:不需要启动单独的虚拟机占用硬件资源。
启动速度更快:可以在数秒内完成启动
使用虚拟机是为了更好的实现服务运行环境隔离,每个虚拟机都有独立的内核,虚拟化可以实现不同操作系统的虚拟机,但是通常一个虚拟机只运行一个服务,很明显资源利用率比较低且造成不必要的性能损耗,我们创建虚拟机的目的是为了运行应用程序,比如Nginx、PHP、Tomcat 等web 程序,使用虚拟机无疑带来了一些不必要的资源开销,但是容器技术则基于减少中间运行环节带来较大的性能提升。
Linux Namespace 技术:
隔离类型 | 功能 | 系统调用参数 | 内核版本 |
---|---|---|---|
MNT Namespace(mount) | 提供磁盘挂载点和文件系统的隔离能力 | CLONE_NEWNS | Linux 2.4.19 |
IPC Namespace(Inter-Process Communication) | 提供进程间通信的隔离能力 | CLONE_NEWIPC | Linux 2.6.19 |
UTS Namespace(UNIX Timesharing System) | 提供主机名隔离能力 | CLONE_NEWUTS | Linux 2.6.19 |
PID Namespace(Process Identification) | 提供进程隔离能力 | CLONE_NEWPID | Linux 2.6.24 |
Net Namespace(network) | 提供网络隔离能力 | CLONE_NEWNET | Linux 2.6.29 |
User Namespace(user) | 提供用户隔离能力 | CLONE_NEWUSER | Linux 3.8 |
MNT Namespace:
每个容器都要有独立的根文件系统有独立的用户空间,以实现在容器里面启动服
务并且使用容器的运行环境,即一个宿主机是ubuntu 的服务器,可以在里面启
动一个centos 运行环境的容器并且在容器里面启动一个Nginx 服务,此Nginx 运
行时使用的运行环境就是centos 系统目录的运行环境,但是在容器里面是不能
访问宿主机的资源,宿主机是使用了chroot 技术把容器锁定到一个指定的运行目录里面。
例如:/var/lib/containerd/io.containerd.runtime.v1.linux/moby/容器ID
IPC Namespace:
一个容器内的进程间通信,允许一个容器内的不同进程的(内存、缓存等)数据访问,但是不能夸容器访问其他容器的数据。
UTS Namespace:
UTS namespace(UNIX Timesharing System 包含了运行内核的名称、版本、底层体
系结构类型等信息)用于系统标识,其中包含了hostname 和域名domainname ,
它使得一个容器拥有属于自己hostname 标识,这个主机名标识独立于宿主机系
统和其上的其他容器。
PID Namespace:
Linux 系统中,有一个PID 为1 的进程(init/systemd)是其他所有进程的父进程,那么在每个容
器内也要有一个父进程来管理其下属的子进程,那么多个容器的进程通PID namespace 进程隔离
(比如PID 编号重复、器内的主进程生成与回收子进程等)。
Net Namespace:
每一个容器都类似于虚拟机一样有自己的网卡、监听端口、TCP/IP 协议栈等,
Docker 使用network namespace 启动一个vethX 接口,这样你的容器将拥有它自
己的桥接ip 地址,通常是docker0,而docker0 实质就是Linux 的虚拟网桥,网桥
是在OSI 七层模型的数据链路层的网络设备,通过mac 地址对网络进行划分,并
且在不同网络直接传递数据。
User Namespace:
各个容器内可能会出现重名的用户和用户组名称,或重复的用户UID 或者GID,
那么怎么隔离各个容器内的用户空间呢?
User Namespace 允许在各个宿主机的各个容器空间内创建相同的用户名以及相
同的用户UID 和GID,只是会把用户的作用范围限制在每个容器内,即A 容器和
B 容器可以有相同的用户名称和ID 的账户,但是此用户的有效范围仅是当前容
器内,不能访问另外一个容器内的文件系统,即相互隔离、互不影响、永不相见。
宿主机与Docker容器对应的进程关系
docker是docker命令执行到dockerd,然后dockerd通过本地service文件调用containerd,这里dockerd只是起到转发到containerd请求,一但到containerd就会拉起一个containerd-shim,这个进程就会拉起一个封装环境我们称之为沙箱
如果有启动1个容器,那么dockerd下会有1个docker-proxy进程,启动两个容器,那么dockerd下会有两个docker-proxy进程,以此类推,有多少个容器,dockerd下就会有多少个docker-proxy进程
containerd下面会有一堆containerd-shim进程,containerd-shim进程下面就是你的容器了
dockerd端口管理,是由(多个)docker-proxy管理还会起很多线程,这里就是维护端口映射,如果你在创建容器把80端口映射,它就会调用内核去创建iptables规则
进程10687到了容器里会被重新初始化为PID=1,在容器里pid为1进程不是systemd也不会init,它是通过类似于tail -f这样的命令或者是nginx -g daemon off; 这样的某个服务的二进制初始化的,pid为1的进程的目的是为了保证容器的运行,因为在linux系统下必须有一个pid 为1的进程作为当前系统上其他的进程的父进程。
Linux control groups:
在一个容器,如果不对其做任何资源限制,则宿主机会允许其占用无限大的内存
空间,有时候会因为代码 bug 程序会一直申请内存,直到把宿主机内存占完,为
了避免此类的问题出现,宿主机有必要对容器进行资源分配限制,比如 CPU、内
存等,Linux Cgroups 的全称是 Linux Control Groups,它最主要的作用,就是限制
一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。此外,
还能够对进程进行优先级设置,以及将进程挂起和恢复等操作。
验证系统 cgroups:
Cgroups 在内核层默认已经开启,从 centos 和 ubuntu 对比结果来看,显然内核
较新的 ubuntu 支持的功能更多。
cgroups 中内存模块:
# cat /boot/config-4.15.0-54-generic | grep MEM | grep CG
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
# CONFIG_MEMCG_SWAP_ENABLED is not set
CONFIG_SLUB_MEMCG_SYSFS_ON=y
cgroups 具体实现:
blkio:块设备 IO 限制。
cpu:使用调度程序为 cgroup 任务提供 cpu 的访问。
cpuacct:产生 cgroup 任务的 cpu 资源报告。
cpuset:如果是多核心的 cpu,这个子系统会为 cgroup 任务分配单独的 cpu 和
内存。
devices:允许或拒绝 cgroup 任务对设备的访问。
freezer:暂停和恢复 cgroup 任务。
memory:设置每个 cgroup 的内存限制以及产生内存资源报告。
net_cls:标记每个网络包以供 cgroup 方便使用。
ns:命名空间子系统。
perf_event:增加了对每 group 的监测跟踪的能力,可以监测属于某个特定的
group 的所有线程以及运行在特定 CPU 上的线程。
查看系统 cgroups:
[root@node1 ~]# ll /sys/fs/cgroup/
total 0
drwxr-xr-x 5 root root 0 Jun 30 18:12 blkio
lrwxrwxrwx 1 root root 11 Jun 30 18:12 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 Jun 30 18:12 cpuacct -> cpu,cpuacct
drwxr-xr-x 5 root root 0 Jun 30 18:12 cpu,cpuacct
drwxr-xr-x 3 root root 0 Jun 30 18:12 cpuset
drwxr-xr-x 5 root root 0 Jun 30 18:12 devices
drwxr-xr-x 3 root root 0 Jun 30 18:12 freezer
drwxr-xr-x 3 root root 0 Jun 30 18:12 hugetlb
drwxr-xr-x 5 root root 0 Jun 30 18:12 memory
drwxr-xr-x 3 root root 0 Jun 30 18:12 net_cls
drwxr-xr-x 3 root root 0 Jun 30 18:12 perf_event
drwxr-xr-x 5 root root 0 Jun 30 18:12 systemd
有了以上的 chroot、namespace、cgroups 就具备了基础的容器运行环境,但是还
需要有相应的容器创建与删除的管理工具、以及怎么样把容器运行起来、容器数
据怎么处理、怎么进行启动与关闭等问题需要解决,于是容器管理技术出现了。
容器管理工具:
目前主要是使用 docker,早期有使用 lxc。
docker:
Docker 启动一个容器也需要一个外部模板但是较多镜像,docke 的镜像可以保存
在一个公共的地方共享使用,只要把镜像下载下来就可以使用,最主要的是可以
在镜像基础之上做自定义配置并且可以再把其提交为一个镜像,一个镜像可以被
启动为多个容器。
Docker 的镜像是分层的,镜像底层为库文件且只读层即不能写入也不能删除数
据,从镜像加载启动为一个容器后会生成一个可写层,其写入的数据会复制到容
器目录,但是容器内的数据在删除容器后也会被随之删除。
Docker 的优势:
快速部署:短时间内可以部署成百上千个应用,更快速交付到线上。
高效虚拟化:不需要额外的 hypervisor 支持,直接基于 linux 实现应用虚拟化,
相比虚拟机大幅提高性能和效率。
节省开支:提高服务器利用率,降低 IT 支出。
简化配置:将运行环境打包保存至容器,使用时直接启动即可。
快速迁移和扩展:可夸平台运行在物理机、虚拟机、公有云等环境,良好的兼容
性可以方便将应用从 A 宿主机迁移到 B 宿主机,甚至是 A 平台迁移到 B 平台。
Docker 的缺点:
隔离性:各应用之间的隔离不如虚拟机彻底。
docker(容器)的核心技术:
容器规范:
容器技术除了的 docker 之外,还有 coreOS 的 rkt,还有阿里的 Pouch,为了保
证容器生态的标准性和健康可持续发展,包括 Linux 基金会、Docker、微软、红
帽谷歌和、IBM、等公司在 2015 年 6 月共同成立了一个叫 open container(OCI)
的组织,其目的就是制定开放的标准的容器规范,目前 OCI 一共发布了两个规范,
分别是 runtime spec 和 image format spec,有了这两个规范,不同的容器公司开
发的容器只要兼容这两个规范,就可以保证容器的可移植性和相互可操作性。
容器 runtime(runtime spec):
runtime 是真正运行容器的地方,因此为了运行不同的容器 runtime 需要和操
作系统内核紧密合作相互在支持,以便为容器提供相应的运行环境。
目前主流的三种 runtime:
Lxc:linux 上早期的 runtime,Docker 早期就是采用 lxc 作为 runtime。
runc:目前 Docker 默认的 runtime,runc 遵守 OCI 规范,因此可以兼容 lxc。
rkt:是 CoreOS 开发的容器 runtime,也符合 OCI 规范,所以使用 rktruntime 也可
以运行 Docker 容器。
runtime 主 要 定 义 了 以 下 规 范 , 并 以 json 格 式 保 存 在/run/docker/runtime-runc/moby/容器 ID/state.json 文件,此文件会根据容器的状态实时更新内容:
版本信息:存放 OCI 标准的具体版本号。
容器 ID:通常是一个哈希值,可以在所有 state.json 文件中提取出容器 ID 对容器进行
批量操作(关闭、删除等),此文件在容器关闭后会被删除,容器启动后会自动生成。
PID:在容器中运行的首个进程在宿主机上的进程号,即将宿主机的那个进程设置为容
器的守护进程。
容器文件目录:存放容器 rootfs 及相应配置的目录,外部程序只需读取 state.json 就
可以定位到宿主机上的容器文件目录。
容器创建:创建包括文件系统、namespaces、cgroups、用户权限在内的各项内容。
容器进程的启动:运行容器启动进程,该文件在
/run/containerd/io.containerd.runtime.v1.linux/moby/容器 ID/config.json。
容器生命周期:容器进程可以被外部程序关停,runtime 规范定义了对容器操作信号的
捕获,并做相应资源回收的处理,避免僵尸进程的出现。
容器镜像(image format spec):
OCI 容器镜像主要包含以下内容:
文件系统:定义以 layer 保存的文件系统,在镜像里面是 layer.tar,每个 layer 保存了
和上层之间变化的部分,image format spec 定义了 layer 应该保存哪些文件,怎么表
示增加、修改和删除的文件等操作。
manifest 文件:描述有哪些 layer,tag 标签及 config 文件名称。
config 文件:是一个以 hash 命名的 json 文件,保存了镜像平台,容器运行时容
器运行时需要的一些信息,比如环境变量、工作目录、命令参数等。
index 文件:可选的文件,指向不同平台的 manifest 文件,这个文件能保证一个镜
像可以跨平台使用,每个平台拥有不同的 manifest 文件使用 index 作为索引。
父镜像:大多数层的元信息结构都包含一个 parent 字段,指向该镜像的父镜像。
参数:
ID:镜像 ID,每一层都有 ID
tag 标签:标签用于将用户指定的、具有描述性的名称对应到镜像 ID
仓库:Repository 镜像仓库
os:定义类型
architecture :定义 CPU 架构
author:作者信息
create:镜像创建日期
容器管理工具:
管理工具连接 runtime 与用户,对用户提供图形或命令方式操作,然后管理工
具将用户操作传递给 runtime 执行。
lxc 是 lxd 的管理工具。
Runc 的管理工具是 docker engine,docker engine 包含后台 deamon 和 cli 两部
分,大家经常提到的 Docker 就是指的 docker engine。
Rkt 的管理工具是 rkt cli。
容器定义工具:
容器定义工具允许用户定义容器的属性和内容,以方便容器能够被保存、共享
和重建。
Docker image:是 docker 容器的模板,runtime 依据 docker image 创建容器。
Dockerfile:包含 N 个命令的文本文件,通过 dockerfile 创建出 docker image。
ACI(App container image):与 docker image 类似,是 CoreOS 开发的 rkt 容器的镜
像格式。
Registry:
统一保存镜像而且是多个不同镜像版本的地方,叫做镜像仓库。
Image registry:docker 官方提供的私有仓库部署工具。
Docker hub:docker 官方的公共仓库,已经保存了大量的常用镜像,可以方便大
家直接使用。
Harbor:vmware 提供的自带 web 界面自带认证功能的镜像仓库,目前有很多公司使用。
172.31.200.101/project/centos:7.2.1511
172.31.200.101/project/centos:latest
172.31.200.101/project/java-7.0.59:v1
172.31.200.101/project/java-7.0.59:v2
编排工具:
当多个容器在多个主机运行的时候,单独管理容器是相当复杂而且很容易出
错,而且也无法实现某一台主机宕机后容器自动迁移到其他主机从而实现高可用
的目的,也无法实现动态伸缩的功能,因此需要有一种工具可以实现统一管理、
动态伸缩、故障自愈、批量执行等功能,这就是容器编排引擎。
容器编排通常包括容器管理、调度、集群定义和服务发现等功能。
Docker swarm:docker 开发的容器编排引擎。
Kubernetes:google 领导开发的容器编排引擎,内部项目为 Borg,且其同时支持docker 和 CoreOS。
Mesos+Marathon:通用的集群组员调度平台,mesos(资源分配)与 marathon(容器编排平台)一起提供容器编排引擎功能。
docker(容器)的依赖技术:
容器网络:
docker 自带的网络 docker network 仅支持管理单机上的容器网络,当多主机运
行的时候需要使用第三方开源网络,例如 calico、flannel 等。
服务发现:
容器的动态扩容特性决定了容器 IP 也会随之变化,因此需要有一种机制可以
自动识别并将用户请求动态转发到新创建的容器上,kubernetes 自带服务发现功能,需要结合 kube-dns 服务解析内部域名。
容器监控:
可以通过原生命令 docker ps/top/stats 查看容器运行状态,另外也可以使
heapster/ Prometheus 等第三方监控工具监控容器的运行状态。
数据管理:
容器的动态迁移会导致其在不同的 Host 之间迁移,因此如何保证与容器相关
的数据也能随之迁移或随时访问,可以使用逻辑卷/存储挂载等方式解决。
日志收集:
docker 原生的日志查看工具 docker logs,但是容器内部的日志需要通过 ELK
等专门的日志收集分析和展示工具进行处理。
docker安装
# Ubuntu
[root@long-ubuntu ~]# apt purge ufw lxd lxd-client lxcfs -y lxc-common
# 安装常用的包
[root@long-ubuntu ~]# apt install -y iproute2 ntpdate tcpdump telnet traceroute nfs-kernel-server nfs-common lrzsz tree openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev ntpdate tcpdump telnet traceroute gcc openssh-server lrzsz tree openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev ntpdate tcpdump telnet traceroute iotop unzip zip
# 删除之前的docker版本
[root@long-ubuntu ~]# apt-get remove docker docker-engine docker.io
[root@long-ubuntu ~]# apt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
# 下载并导入key
[root@long-ubuntu ~]# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
[root@long-ubuntu ~]# sudo add-apt-repository "deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
$(lsb_release -cs) \
stable"
# 查看可以安装的版本信息
[root@long-ubuntu ~]# apt-cache madison docker-ce
# 安装指定版本
[root@long-ubuntu ~]# apt install -y docker-ce=5:19.03.15~3-0~ubuntu-bionic docker-ce-cli=5:19.03.15~3-0~ubuntu-bionic
# 创建目录
[root@long-ubuntu ~]# sudo mkdir -p /etc/docker
# 添加镜像加速
[root@long-ubuntu ~]# sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://rzd1bb7q.mirror.aliyuncs.com"]
}
EOF
# 重新加载
[root@long-ubuntu ~]# sudo systemctl daemon-reload
# 重启
[root@long-ubuntu ~]# sudo systemctl restart docker
报错
Ubuntu18.04安装包时遇到的报错如下:
[root@Bj-Ubuntu ~]# apt purge ufw lxd lxd-client lxcfs lxc-common
E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
解决办法:
第一种情况:
进程中存在与apt相关的正在运行的进程:
首先检查是否在运行apt,apt-get相关的进程
[root@Bj-Ubuntu ~]# ps aux | grep -i apt
如果存在与apt相关的正在运行的进程,kill掉进程;
[root@Bj-Ubuntu ~]# sudo killall apt apt-get
如果进行完上面的步骤还是无法顺利执行apt-get 操作,则属于第二种情况:
第二种情况:
进程列表中已经没有与apt,apt-get相关的进程在运行,但依然报错,在这种情况下,产生错误的根本原因是lock file。 lock file用于防止两个或多个进程使用相同的数据。 当运行apt或apt-commands时,它会在几个地方创建lock files。 当前一个apt命令未正确终止时,lock file未被删除,因此它们会阻止任何新的apt / apt-get命令实例,比如正在执行apt-get upgrade,在执行过程中直接ctrl+c取消了该操作,很有可能就会造成这种情况。
要解决此问题,首先要删除lock file。
使用lsof命令获取持有lock file的进程的进程ID,依次运行如下命令:
[root@Bj-Ubuntu ~]# lsof /var/lib/dpkg/lock
[root@Bj-Ubuntu ~]# lsof /var/lib/apt/lists/lock
[root@Bj-Ubuntu ~]# lsof /var/cache/apt/archives/lock
需要注意的是,以上命令执行结果如果无返回,说明没有正在运行的进程;如果返回了相应的进程,需要kill掉。
删除所有的lock file
[root@Bj-Ubuntu ~]# sudo rm /var/lib/apt/lists/lock
[root@Bj-Ubuntu ~]# sudo rm /var/cache/apt/archives/lock
[root@Bj-Ubuntu ~]# sudo rm /var/lib/dpkg/lock
最后重新配置一下dpkg:
[root@Bj-Ubuntu ~]# sudo dpkg --configure -a
如果上述命令不出任何错误,就可以重新安装了