Docker简介

Docker是什么

  • Docker是一个开源的应用容器引擎,基于LXC(Linux Container)内核虚拟化技术实现,提供一系列更强的功能,比如镜像、Dockerfile等;
  • Docker理念是将应用及依赖包打包到一个可移植的容器中,可发布到任意Linux发行版Docker引擎上。使用沙箱机制运行程序,程序之间相互隔离;
  • Docker使用Go语言开发。

Docker思想

Docker采用C/S架构,Dcoker daemon作为服务端接受来自客户端请求,并处理这些请求,比如创建、运行容器等。客户端为用户提供一系列指令与Docker daemon交互。

image-20200610145818895

  • 集装箱∶会将所有需要的内容放到不同的集装箱中,谁需要这些环境就直接拿到这个集装箱就可以了。
  • 标准化:
    • 运输的标准化:Docker有一个码头,所有上传的集装箱都放在了这个码头上,当谁需要某一个境,就直接指派大海去搬运这个集装箱就可以了。
    • 命令的标准化:Docker提供了一些列的命令,帮助我们去获取集装箱等等操作。
    • 提供了REST的API:衍生出了很多的图形化界面,Rancher。
  • 隔离性:Docker在运行集装箱内的内容时,会在Linux的内核中单独的开辟一片空间,这片空间不会影响到其他程序。
  • 中央仓库|注册中心:超级码头,上面放的就是集装箱
  • 镜像:就是集装箱,好比是一个目标,可以通过这个目标来创建容器服务,tomcat镜像>run>容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
  • 容器:独立运行一个或者一组应用,通过镜像来创建的(运行起来的镜像)

Docker对比虚拟机

image-20200730095405498

  • Docker有着比虚拟机更少的抽象层
  • Docker利用的是宿主机的内核,vm需要Guest OS。
  • 所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载Guest OS,分钟级别的,而docker是利用当前宿主机的操作系统,省略了复杂的过程,秒级的!

image-20200610161845790

image-20200730105719336

image-20200730105727032

但是,如上围一个宿主机运行了N个容器,多个容器带来的以下问题怎么解决:

  1. 怎么样保证每个容器都有不同的文件系统并且能互不影响?·
  2. 一个docker主进程内的各个容器都是其子进程,那么实现同一个主进程下不同类型的子进程?各个进程间通信能相互访问(内存数据)吗?
  3. 每个容器怎么解决IP及端口分配的问题?·
  4. 多个容器的主机名能一样吗?
  5. 每个容器都要不要有root用户?怎么解决账户重名问题?

以上问题怎么解决?·

Linux Namespace

Namespace是Linux系统的底层概念,在内核层实现,即有一些不同类型的命名空间被部署在核内,各个docker容器运行在同一个docker主进程并且共用同一个宿主机系统内核,各docker容器运行在宿主机的用户空间,每个容器都要有类似于虚拟机一样的相互隔离的运行空间,但是容器技术是在一个进程内实现运行指定服务的运行环境,并且还可以保护宿主机内核不受其他进程的干扰和影响,如文件系统空间、网络空间、进程空间等,目前主要通过以下技术实现

容器运行空间的相互隔离:.

隔离类型 隔离内容 系统调用参数 内核版本
MNT Namespace(mount) 磁盘挂载点和文件系统 CLONE_NEWNS Linux 2.4.19
IPC Namespace(Inter-Progress Communication) 信号量、消息队列、共享内存 CLONE_NEWIPC Linux 2.6.19
UTS Namespace(Unix Timesharing System) 主机名和域名 CLONE_NEWUTS Linux 2.6.19
PID Namespace(Progress Identification) 进程编号 CLONE_NEWIPD 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的账户,但是此用户的有效范围仅是当前容器内,不能访问另外一个容器内的文件系统,即相互隔离、互补影响、永不相见。

Linux Control Groups

在一个容器,如果不对其做任何资源限制,则宿主机会允许其占用无限大的内存空间,有时候会因为代码bug程序会一直申请内存,直到把宿主机内存占完,为了避免此类的问题出现,宿主机有必要对容器进行资源分配限制,比如CPU、内存等,Linux Cgroups的全称是Linux Control Groups,它最主要的作用,就是限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽等等。此外,还能够对进程进行优先级设置,以及将进程挂起和恢复等操作。

具体实现

  • 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@docker01 ~]$ ll /sys/fs/cgroup/
总用量 0
dr-xr-xr-x 2 root root 0 7月 20 09:09 blkio
lrwxrwxrwx 1 root root 11 7月 20 09:09 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 7月 20 09:09 cpuacct -> cpu,cpuacct
dr-xr-xr-x 2 root root 0 7月 20 09:09 cpu,cpuacct
dr-xr-xr-x 2 root root 0 7月 20 09:09 cpuset
dr-xr-xr-x 4 root root 0 7月 20 09:09 devices
dr-xr-xr-x 2 root root 0 7月 20 09:09 freezer
dr-xr-xr-x 2 root root 0 7月 20 09:09 hugetlb
dr-xr-xr-x 4 root root 0 7月 20 09:09 memory
lrwxrwxrwx 1 root root 16 7月 20 09:09 net_cls -> net_cls,net_prio
dr-xr-xr-x 2 root root 0 7月 20 09:09 net_cls,net_prio
lrwxrwxrwx 1 root root 16 7月 20 09:09 net_prio -> net_cls,net_prio
dr-xr-xr-x 2 root root 0 7月 20 09:09 perf_event
dr-xr-xr-x 4 root root 0 7月 20 09:09 pids
dr-xr-xr-x 2 root root 0 7月 20 09:09 rdma
dr-xr-xr-x 5 root root 0 7月 20 09:09 systemd

有了以上的chroot、namespace、cgroups就具备了基础的客器运行环境,但是还需要有相应的容器创建与删除的管理工具、以及怎么样把容器运行起来、容器数据怎么处理、怎么进行启动与关闭等问题需要解决,于是容器管理技术出现了。

容器管理工具

目前使用docker,早期有使用LXC。

LXC

LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,官方网站: https://linuxcontainers.org/

LXC启动容器依赖于模板,清华模板源:https://mirrors.tuna.tsinghua.edu.cn/help/Ixc-images/
但是做模板相对较难,需要手动一步步创构建文件系统、准备基础目录及可执行程序等,而且在大规模使用容器的场景很难横向扩展,另外后期代码升级也需要重新从头构建模板,基于以上种种原因便有了docker。

Docker

Docker启动一个容器也需要一个外部模板,docke的镜像可以保存在一个公共的地方共享使用,只要把镜像下载下来就可以使用,最主要的是可以在镜像基础之上做自定义配置并且可以再把其提交为一个镜像,一个镜像可以被启动为多个容器。

Docker的镜像是分层的,镜像底层为库文件且只读层既不能写入也不能删除数据,从镜像加载启动为一个容器后会生成一个可写层,其写入的数据会复制到容器目录,但是容器内的数据在删除容器后也会被随之删除。

image-20200720093339523

Pouch

https://www.infoq.cn/article/alibaba-pouch/
https://github.com/alibaba/pouch/

Podman

https://podman.io

Docker优点

  • 快速部署:短时间内可以部署成百上千个应用,更快速交付到线上。
  • 高效虚拟化:不需要额外的hypervisor支持,直接基于linux实现应用虚拟化,相比虚拟机大幅提高性能和效率。
  • 节省开支:提高服务器利用率,降低IT支出。
  • 简化配置:将运行环境打包保存至容器,使用时直接启动即可。
  • 快速迁移和扩展:可跨平台运行在物理机、虚拟机、公有云等环境,良好的兼容性可以方便将应用从A宿主机迁移到B宿主机,甚至是A平台迁移到B平台。

Docker缺点

  • 隔离性:各应用之间的隔离不如虚拟机彻底。

Docker(容器)的核心技术

容器规范

容器技术除了的docker之外,还有coreOS的rkt,还有阿里的Pouch,为了保证容器生态的标准性和健康可持续发展,包括Linux基金会、Docker、微软、红帽谷歌和IBM等公司在2015年6月共同成立了一个叫opencontainer(oci)的组织,其目的就是制定开放的标准的容器规范,目前OCI一共发布了两个规范,分别是runtime spec和image format spec,有了这两个规范,不同的容器公司开发的容器只要兼容这两个规范,就可以保证容器的可移植性和相互可操作性。

容器runtime

runtime是真正运行容器的地方,因此为了运行不同的容器,runtime需要和操作系统内核紧密合作相互在支持,以便为容器提供相应的运行环境。
目前主流的三种runtime:

  • Lxc: linux上早期的runtime,Docker早期就是采用Lxc作为runtime。
  • runc:目前Docker默认的runtime,runc遵守OCI规范,因此可以兼容Ixc。.
  • rkt:是CoreOS开发的容器runtime,也符合OCI规范,所以使用rktruntime也可以运行Docker容器。

容器管理工具
管理工具连接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

统一保存镜像而且是多个不同镜像版本的地方,叫做镜像仓库。
lmage registry: docker官方提供的私有仓库部署工具。
Docker hub: docker官方的公共仓库,已经保存了大量的常用镜像,可以方便大家直接使用。
Harbor: vmware提供的自带web界面自带认证功能的镜像仓库,目前有很多公司使用。

编排工具

当多个容器在多个主机运行的时候,单独管理容器是相当复杂而且很容易出错,而且也无法实现某一台主机宕机后容器自动迁移到其他主机从而实现高可用的目的,也无法实现动态伸缩的功能,因此需要有一种工具可以实现统一管理、动态伸缩、故障自愈、批量执行等功能,这就是容器编排引擎。

容器编排通常包括容器管理、调度、集群定义和服务发现等功能。

  • 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安装及基础命令介绍

较旧的Docker版本称为dockerdocker-engine。如果已安装这些程序,请卸载它们以及相关的依赖项。

官方网址: https://www.docker.com/

系统版本选择

Docker目前已经支持多种操作系统的安装运行,比如Ubuntu、CentOS、Redhat、Debian、Fedora,甚至还支持了Mac和Windows,在linux系统上需要内核版本在3.10或以上,docker版本号之前一直是0.X版本或1.X版本,但是从2017年3月1号开始改为每个季度发布一次稳版,其版本号规则也统一变更为YY.MM,例如17.09表示是2017年9月份发布的,本次演示的操作系统使用Centos 8.2为例。

Docker版本选择

Docker之前没有区分版本,但是2017年初推出(将docker更名为)新的项目Moby,github地址: https://github.com/moby/moby,Moby项目属于Docker项目的全新上游,Docker将是一个隶属于的Moby的子产品,而且之后的版本之后开始区分为CE版本(社区版本)和EE(企业收费版),CE社区版本和EE企业版本都是每个季度发布一个新版本,但是EE版本提供后期安全维护1年,而CE版本是4个月,本次演示的Docker版本为18.03以下为官方原文:
https://blog.docker.com/2017/03/docker-enterprise-edition/

kubernetes结合使用的时候,要安装经过kubernetes官方测试通过的docker版本,避免出现不兼容等未知的及不可预估的问题发生

下载rpm包安装

官方rpm包下载地址

https://download.docker.com/linux/centos/7/x86_64/stable/Packages/

二进制下载地址

https://download.docker.com/
https://mirrors.aliyun.com/docker-ce/linux/static/stable/x86_64/

阿里镜像下载地址

https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/

通过修改yum源安装

1
2
3
wget -O /etc/yum.repos.d/docker.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

dnf install -y docker-ce

验证Docker信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[root@docker01 ~]$ docker info
Containers: 2#当前主机运行的容器总数
Running: 1#有几个容器是正在运行的
Paused: O#有几个容器是暂停的
Stopped:1#有几个容器是停止的
lmages: 3#当前服务器的镜像数
Server Version: 18.09.9 #服务端版本
Storage Driver: overlay2#正在使用的存储引擎
Backing Filesystem: xfs #后端文件系统,即服务器的磁盘文件系统
Supports d_type: true #是否支持d_type
Native Overlay Diff: true #是否支持差异数据存储
Logging Driver: json-file #日志类型
Cgroup Driver: cgroupfs #Cgroups类型
Plugins:#插件
volume: local#卷
Network: bridge host macvlan null overlay# overlay跨主机通信
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog#日志类型
Swarm: inactive #是否支持swarmw
Runtimes: runc#已安装的容器运行时
Default Runtime: runc#默认使用的容器运行时
Init Binary: docker-init#初始化容器的守护进程,即 pid为1的进程
containerd version:894b81a4b802e4eb2a91d1ce216b8817763c29fb#版本
runc version:425e105d5a03fabd737a126ad93d62a9eeede87f# runc版本
init version: fec3683 #init版本
Security Options:#安全选项
Apparmor#安全模块 https://docs.docker.com/engine/security/apparmor/
seccomp#审计(操作) https://docs.docker.com/engine/security/seccomp/
Profile: default#默认的配置文件
Kernel Version: 4.18.0-193.el8.x86_64# 宿主机内核版本
Operating System: CentOS Linux 8 (Core)#宿主机操作系统
OSType: linux#宿主机操作系统类型
Architecture: x86_64#宿主机架构
CPUs: 4#宿主机CPU数量
Total Memory: 952.1MiB#宿主机总内存
Name: docker01 #宿主机hostname
ID: 3A2H:67TW:HNSU:D3AQ:MPLB:SY2G:GPNH:HG7U:4IHC:JLIK:TXRH:IJM7#宿主机ID
Docker Root Dir: /var/lib/docker#宿主机数据保存目录
Debug Mode: false#端是否开启debug
Registry: https://index.docker.io/v1/ #镜像仓库
Labels:#其他标签
Experimental: false #是否是测试版
Insecure Registries:# 非安全的镜像仓库
127.0.0.0/8
Live Restore Enabled: false#是否开启活动重启(重启docker-daemon 不关闭容器)
Product License: Community Engine# 产品许可信息

Docker存储引擎

目前docker的默认存储引擎为overlay2,不同的存储引擎需要相应的系统支持,如需要磁盘分区的时候传递d-type文件分层功能,即需要传递内核参数开启格式化磁盘的时候的指定功能。

历史更新信息:

https://github.com/moby/moby/blob/master/CHANGELOG.md

官方文档关于存储引擎的选择文档:
https://docs.docker.com/storage/storagedriver/select-storage-driver/

存储驱动类型:

  • AUFS (AnotherUnionFS)是一种Union FS,是文件级的存储驱动。所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中。简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。这种文件系统可以一层一层地叠加修改文件。无论底下有多少层都是只读的,只有最上层的文件系统是可写的。当需要修改一个文件时,AUFS创建该文件的一个副本,使用CoW(写时复制)将文件从只读层复制到可写层进行修改,结果也保存在可写层。在Docker中,底下的只读层就是image,可写层就是Container,是 Docker 18.06及更早版本的首选存储驱动程序,在内核3.13上运行Ubuntu 14.04时不支持overlay2。

  • Overlay:一种Union FS文件系统,Linux内核3.18后支持。

  • Overlay2: Overlay的升级版,到目前为止,所有Linux发行版推荐使用的存储类型。

  • Devicemapper:是CentOS和RHEL的推荐存储驱动程序,因为之前的内核版本不支持overlay2,但是当前较新版本的CentOS和RHEL现在已经支持overlay2,因此推荐使用overlay2

  • ZFS(Sun-2005)/btrfs(Oracle-2007):目前没有广泛使用。

  • Vfs:用于测试环境,适用于无法使用copy-on-write文件系统的情况。此存储驱动程序的性能很差.通常不建议用于生产。

Docker官方推荐首选存储引擎为overlay2,devicemapper存在使用空间方面的一些限制,虽然可以通过后期配置解决,但是官方依然推荐使用overlay2,以下是网上查到的部分资料:.

https://www.cnblogs.com/youruncloud/p/5736718.html

image-20200722090703775

如果docker数据目录是一块单独的磁盘分区而且是xfs格式的,那么需要在格式化的时候加上参数-n ftype=1,否则后期在启动容器的时候会报错不支持d-type。

Docker服务进程

有四个进程:

  • dockerd:被client直接访问,其父进程为宿主机的systemd守护进程
  • docker-proxy:实现容器通信,其父进程为dockerd
  • containerd:被dockerd进程调用以实现与runc交互
  • containerd-shim:真正运行容器的载体,其父进程为containerd

容器的创建与管理过程

  1. dockerd通过grpc和 containerd模块通信(runc)交换,dockerd 和 containerd通信的socket文件:frun/containerd/containerd.sock
  2. containerd在 dockerd启动时被启动,然后containerd启动grpc请求监听,
    containerd处理grpc 请求,根据请求做相应动作。
    /usr/bin/dockerd -H fd:// –containerd=/run/containerd/containerd.sock
  3. 若是创建容器,containerd拉起一个container-shim容器进程,并进行相应的创建操作。
  4. container-shim被拉起后,start/exec/create拉起runC进程,通过exit、control文件和 containerd 通信,通过父子进程关系和SIGCHLD(信号)监控容器中进程状态。
  5. 在整个容器生命周期中,containerd通过epoll监控容器文件,监控容器事件。

image-20200722100745633

Docker镜像加速配置

国内下载国外的镜像有时候会很慢,因此可以更改docker配置文件添加一个加速器,可以通过加速器达到加速下载镜像的目的。

浏览器打开http://cr.console.aliyun.com,注册或登录阿里云账号,点击左侧的镜像加速器,将会得到一个专属的加速地址,官方会有对应步骤

Docker镜像管理

Docker镜像含有启动容器所需要的文件系统及所需要的内容,因此镜像主要用于创建并启动docker容器。

Docker镜像里面是一层层文件系统,叫做Union File System (Union FS联合文件系统),2004年由纽约州立大学石溪分校开发,联合文件系统可以将多个目录挂载到一起从而形成一整个虚拟文件系统,该虚拟文件系统的目录结构就像普通linux的目录结构一样,docker通过这些文件再加上宿主机的内核提供了一个linux的虚拟环境,每一层文件系统我们叫做一层layer,联合文件系统可以对每一层文件系统设置三种权限,只读(readonly)、读写(readwrite)和写出(whiteout-able),但是 docker镜像中每一层文件系统都是只读的,构建镜像的时候,从一个最基本的操作系统开始,每个构建的操作都相当于做一层的修改,增加了一层文件系统,一层层往上叠加,上层的修改会覆盖底层该位置的可见性,这也很容易理解,就像上层把底层遮住了一样,当使用镜像的时候,我们只会看到一个完全的整体,不知道里面有几层也不需要知道里面有几层,结构如下:

image-20200720093339523

演示aufs联合挂载:
image-20200722111051035

一个典型的Linux文件系统由bootfs 和 rootfs两部分组成,bootfs(boot file system)主要包含bootloader和 kernel,bootloader主要用于引导加载kernel,当kernel被加载到内存中后bootfs会被umount掉,rootfs (root file system)包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件,下图就是 docker image中最基础的两层结构,不同的linux发行版(如ubuntu和CentoS )在rootfs这一层会有所区别。但是对于docker镜像通常都比较小,官方提供的centos基础镜像在200MB左右,一些其他版本的镜像甚至只有几MB,docker镜像直接调用宿主机的内核,镜像中只提供rootfs,也就是只需要包括最基本的命令、工具和程序库就可以了,比如 alpine镜像,在5M左右。
下图就是有两个不同的镜像在一个宿主机内核上实现不同的rootfs。

image-20200722111531886

容器、镜像父镜像

image-20200722112309432

docker命令是最常使用的docker客户端命令,其后面可以加不同的参数以实现相应的功能,常用的命令如下

搜索镜像

在官方的docker仓库中搜索指定名称的docker镜像,也会有很多镜像。

1
2
[root@docker01 ~]$ docker search centos:7.6#指定版本号
[root@docker01 ~]$ docker search centos #不指定版本号默认为latest

下载镜像

从docker仓库将镜像下裁到本地,命令格式如下:
docker pull 仓库服务器:端口/项目名称/镜像名称:tag(版本)号

例如:

1
2
3
4
docker images pull https://image.kinmfer.com:80/linux/centos:v1 自定义版本
docker pull https://image.kinmfer.com:80/linux/centos:7.6.1511
docker pull https://image.kinmfer.com:80/linux/ubuntu:git版本号
docker pull https://image.kinmfer.com:80/linux/centos:时间戳 2020-07-22_11:55:51

查看本地镜像

下载完成的镜像比下载的大,因为下载完成后会解压

1
2
3
[root@docker01 ~]$ docker images
[root@docker01 ~]$ docker image ls
[root@docker01 ~]$ docker image ls --no-trunc

image-20200722120525438

  • REPOSITORY 镜像所属的仓库名称
  • TAG 镜像版本号(标识符),默认为latest
  • IMAGE ID 镜像唯一ID标示
  • CREATED 镜像创建时间。
  • SIZE 镜像的大小

镜像导出

可以将镜像从本地导出问为一个压缩文件,然后复制到其他服务器进行导入使用。

导出方法1

1
2
3
4
[root@docker01 ~]$ docker images save ubuntu -o /opt/ubuntu.tar.gz
[root@docker01 ~]$ ll /opt/
总用量 74484
-rw------- 1 root root 76271616 7月 22 15:10 ubuntu.tar.gz

导出方法2

1
2
3
4
[root@docker01 ~]$ docker save ubuntu:latest > /opt/ubuntu-latest.tar.gz
[root@docker01 ~]$ ll /opt/
总用量 74484
-rw-r--r-- 1 root root 76271616 7月 22 15:13 ubuntu-latest.tar.gz

镜像导入

导入方法1

1
[root@docker01 ~]$ docker images load -i /opt/ubuntu.tar.gz

导入方法2

1
[root@docker01 ~]$ docker load < /opt/ubuntu.tar.gz

删除镜像

docker rmi 镜像ID/镜像名称

通过镜像启动容器的时候 镜像不能被删除,除非将容器全部关闭

1
2
3
4
5
6
7
[root@docker01 ~]$ docker rmi 5ac22cccc3ae# 或者docker images rm 5ac22cccc3ae
Untagged: mysql:latest
Untagged: mysql@sha256:c455bbcaa8b9c5c636c45f6184f970caeb3d8b545a0390e1b72a253e07aef8fd
Deleted: sha256:5ac22cccc3ae67ca42ed92b55c8fa7c68967ec6b875d15d761467d40097368b6
Deleted: sha256:86c56823286628a66aa344188924e576c85b94b4734c418a6a0e123068170c4f
Deleted: sha256:28f36292125ccc90319e3153c5eb83d374d2875c9e46a7538568fc32458ec034
Deleted: sha256:291205778291931aee087875f7d1c30382db776809095e30260079f998025426

获取运行参数帮助

1
[root@docker01 ~]$ docker daemon --help

容器操作基础命令

image-20200728150227180

命令格式

1
2
3
docker run [选项] [镜像名] [shell命令] [参数]
docker run [参数选项] [镜像名称,必须在所有选项的后面][/bin/echo 'hello world']#单次执行,没有自定义容器名称。
docker run centos /bin/echo 'hello wold’ #启动的容器在执行完shel命令就退出了

从镜像启动一个容器

会直接进入到容器,并随机生成容器ID和名称

1
2
[root@docker01 ~]$ docker run -it nginx:1.16.1 bash
root@3d1f640314b3:/#

-i 选项指示 docker 要在容器上打开一个标准的输入接口

-t 指示 docker 要创建一个伪 tty 终端,连接容器的标准输入接口,之后用户就可以通过终端进行输入。

退出容器不注销:ctrl+p+q

显示正在运行的容器

1
2
[root@docker01 ~]$ docker ps
[root@docker01 ~]$ docker container ls

image-20200725074055560

显示所有容器

包括当前正在运行以及已经关闭的所有容器

1
[root@docker01 ~]$ docker ps -a

image-20200725074142559

-n, –last int Show n last created containers (includes all states) (default -1)

删除运行中的容器

即使容正在运行当中,也会被强制删除掉

image-20200725080544981

-f:强制删除

-v:删除容器产生的卷组

后台运行容器

1
[root@docker01 ~]$ docker container run -it -d  nginx:1.16.1

image-20200725093210238

随机端口映射

1
[root@docker01 ~]$ docker run -it -d -P nginx

-P指定随机端口映射,从32768开始

指定端口映射

方式1:本地端口81->容器80端口

1
[root@docker01 ~]$ docker run -p 81:80 --name nginx-test-port1 nginx

方式2:本地IP:本地端口->容器端口

1
[root@docker01 ~]$ docker run -p 192.168.10.205:82:80 --name nginx-test-port2 nginx

方式3:本地IP:本地随机端口->容器端口

1
[root@docker01 ~]$ docker run -p 192.168.10.205:.80 --name nginx-test-port3 nginx

方式4:本机IP:本地端口:容器端口/协议,默认为tcp 协议

1
[root@docker01 ~]$ docker run -p 192.168.10.205:83:80/udp --name nginx-test-port4 nginx

方式5:一次性映射多个端口+协议

1
[root@docker01 ~]$ docker run -p 86:80/tcp -p 443:443/tcp -p 53:53/udp --name nginx-test-port5 nginx

全部端口映射

“-P”选项或”–publish-all”将容器的所有计划要暴露端口全部映射至主机端口

计划要暴露的端口使用使用–expose选项指定

1
[root@docker01 ~]$ docker run -d -P --expose 2222 --expose 3333 --name web busybox:latest /bin/httpd -p 2222 -f

查看容器的端口映射

1
[root@docker01 ~]$ docker port  xxx

image-20200725091655379

自定义容器名称

1
[root@docker01 ~]$ docker run -d -it --name nginx-1.16.1 nginx:1.16.1

image-20200725092058731

自定义容器主机名

1
[root@docker01 ~]$ docker run -d -it -h nginx1.16.1 nginx:1.16.1

注入HOST文件

1
[root@docker01 ~]$ docker run -d -it --add-host www.kinmfer.com:1.1.1.1 nginx:1.16.1

指定容器DNS

DNS服务,默认采用宿主机的dns地址

一是将dns地址配置在宿主机

二是将参数配置在docker启动脚本里面–dns=1.1.1.1

1
[root@docker01 ~]$ docker run -it -d --dns 1.1.1.1 centos:8.2.2004

image-20200725131843942

单次运行

容器推出后自动删除

1
[root@docker01 ~]$ docker run --rm -it  nginx:1.16.1

传递运行命令

容器需要有一个前台运行的进程才能保持容器的运行,通过传递运行参数是一种方式,另外也可以在构建镜像的时候指定容器启动时运行的前台命令。

1
[root@docker01 ~]$ docker run -it nginx:1.16.1 tail -f /etc/hosts

image-20200725095050572

复制内容到容器

1
docker cp 文件名称 容器id:容器内部路径

image-20200730100130024

注入环境变量

1
[root@docker01 ~]$ docker run --rm -e WEB_SERVER_ROOT=/usr/local/src nginx:1.18.0-alpine printenv

image-20200729145410114

容器的启动和关闭

1
[root@docker01 ~]$ docker start xxx
1
[root@docker01 ~]$ docker stop xxx
1
[root@docker01 ~]$ docker restart xxx

查看容器的日志

1
[root@docker01 ~]$ docker logs xxx
  • -t, –timestamps 显示时间戳
  • –details 显示额外细节
  • -f, –follow Follow log output
  • –tail string Number of lines to show from the end of the logs (default “all”)

查看容器中进程信息

1
[root@docker01 ~]$ docker top 容器ID

查看容器运行状态

1
[root@docker01 ~]$ docker stats 容器ID

进入到正在运行的容器

使用attach命令

使用方式为docker attach容器名,attach类似于vnc,操作会在各个容器界面显示,所有使用此方式进入容器的操作都是同步显示的且exit后容器将被关闭,不推荐使用。

使用exec命令

执行单次命令与进入容器,不是很推荐此方式,虽然exit退出容器还在运行

1
[root@docker01 ~]$ docker exec -it 3f97e3647eda /bin/bash

image-20200725103816019

使用nsenter命令

推荐使用此方式,nsenter命令需要通过PID进入到容器内部,不过可以使用docker inspect获取到容器的ID

-f, –format string Format the output using the given Go template
-s, –size Display total file sizes if the type is container

获取容器的IP地址

1
2
[root@docker01 ~]$ docker inspect -f "{ {.NetworkSettings.IPAddress} }" 3f97e3647eda
172.17.0.2

获取容器的PID,可以通过PID进入到容器内

1
2
[root@docker01 ~]$ docker inspect -f "{ {.State.Pid} }" 3f97e3647eda
81012

如果没有nsenter命令,就先dnf install util-linux -y

1
[root@docker01 ~]$ nsenter -t 81012 -m -u -i -n -p
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
用法:
nsenter [选项] [<程序> [<参数>...]]
选项:
-a, --all enter all namespaces
-t, --target <pid> 要获取名字空间的目标进程
-m, --mount[=<文件>] 进入 mount 名字空间
-u, --uts[=<文件>] 进入 UTS 名字空间(主机名等)
-i, --ipc[=<文件>] 进入 System V IPC 名字空间
-n, --net[=<文件>] 进入网络名字空间
-p, --pid[=<文件>] 进入 pid 名字空间
-C, --cgroup[=<文件>] 进入 cgroup 名字空间
-U, --user[=<文件>] 进入用户名字空间
-S, --setuid <uid> 设置进入空间中的 uid
-G, --setgid <gid> 设置进入名字空间中的 gid
--preserve-credentials 不干涉 uid 或 gid
-r, --root[=<目录>] 设置根目录
-w, --wd[=<dir>] 设置工作目录
-F, --no-fork 执行 <程序> 前不 fork
-Z, --follow-context 根据 --target PID 设置 SELinux 环境
-h, --help display this help
-V, --version display version

image-20200725110115129

脚本方式

将nsenter命令写入到脚本进行调用,如下

1
2
3
4
5
6
7
#!/bin/bash
docker_in(){
NAME_ID=$1
PID=$(docker inspect -f "{ {if.State.Pid} }" ${NAME_ID})
nsenter -t ${PID} -m -u -i -n -p -u
}
docker_in $1

使用

image-20200725111138862

查看容器内部的hosts文件

image-20200725111557072

容器默认会将实例的ID添加到自己的hosts文件

批量操作容器

1
[root@docker01 ~]$ docker COMMAND `docker ps -a -q`

删除

1
[root@docker01 ~]$ docker rm -fv `docker ps -a -q` # 删除所有容器
1
[root@docker01 ~]$ docker rm -fv `docker ps -a -q -f status=exited` # 删除已退出容器

关闭

1
[root@docker01 ~]$ docker stop `docker ps -a -q`

强制关闭

1
[root@docker01 ~]$ docker kill `docker ps -a -q`

启动

1
[root@docker01 ~]$ docker start `docker ps -a -q`

重启

1
[root@docker01 ~]$ docker restart `docker ps -a -q`

小结

image-20200611085918923

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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 between a container and the local filesystem #从容器中拷贝指定文件或目录到宿主机中
create Create a new container # 创建一个新的容器,同run,但不启动容器
diff Inspect changes to files or directories on a container's filesystem #查看docker容器的变化
events Get real time events from the server # 从docker服务获取容器实时事件
exec Run a command in a running container # 在已存在的容器上运行命令
export Export a container filesystem as a tar archive # 导出容器的内容流作为一个tar归档文件[对应import]
history Show the history of an image # 展示一个镜像形成历史
images List images # 列出系统当前的镜像
import Import the contents from a tarball to create a filesystem image # 从tar包中的内容创建一个新的文件系统镜像[对应export]
info Display system-wide information # 显示系统相关信息
inspect Return low-level information on Docker objects # 查看容器详细信息
kill Kill one or more running containers # 杀死指定的docker容器
load Load an image from a tar archive or STDIN # 从一个tar包加载一个镜像[对应save]
login Log in to a Docker registry # 注册或者登录一个docker源服务器
logout Log out from a Docker registry # 从当前Docker registry退出
logs Fetch the logs of a container # 输出当前容器日志信息
pause Pause all processes within one or more containers # 暂停容器
port List port mappings or a specific mapping for the container # 查看映射端口对应容器内部源端口
ps List containers # 列出容器列表
pull Pull an image or a repository from a registry # 从docker镜像源服务器拉取指定镜像或库镜像
push Push an image or a repository to a registry # 推送指定镜像或者库镜像至docker源服务器
rename Rename a container # 给docker容器重新命名
restart Restart one or more containers # 重启运行的容器
rm Remove one or more containers # 移除一个或者多个容器
rmi Remove one or more images # 移除一个或者多个镜像[无容器使用该镜像时才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run Run a command in a new container # 创建一个新的容器并运行一个命令
save Save one or more images to a tar archive (streamed to STDOUT by default) # 保存一个镜像为一个tar包[对应load]
search Search the Docker Hub for images # 在docker hub中搜索镜像
start Start one or more stopped containers # 启动容器
stats Display a live stream of container(s) resource usage statistics # 实时显示容器资源使用统计
stop Stop one or more running containers # 停止容器
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE # 给源中镜像打标签
top Display the running processes of a container # 查看容器中运行的进程信息
unpause Unpause all processes within one or more containers # 取消暂停容器
update Update configuration of one or more containers # 更新一个或多个容器配置
version Show the Docker version information # 查看docker版本号
wait Block until one or more containers stop, then print their exit codes # 截取容器停止时的退出状态值