0%

简单了解 Linux 容器是什么 以及 Docker 常用命令操作说明

关于 Docker 系列其他文章的传送门

  1. 《 简单了解 Linux 容器是什么 以及 Docker 常用命令操作说明 》     您当前所在位置

  2. 《 Docker 中 4 种网络模型的概念和实现 》

  3. 《 关于 Docker 中镜像与存储卷的概念和详细使用方法 》

  4. 《 使用 Dockerfile 定制镜像 搭建 php 站点 》

  5. 《 使用 Harbor 创建本地 Docker 仓库 》



top

一、容器

top

18 年,容器技术红了半边天,哦不,已经红满天,如果哪家公司要说自己没用 docker,感觉像落后于这个时代似的,那到底什么是容器呢,容器为什么这么火,它凭什么火,本文将仔细聊聊容器,以及 docker 的基本使用方法。

什么是容器

容器是一种基础工具,泛指任何可以用于容纳其它物品的工具,可以部分或完全封闭,被用于容纳、储存、运输物品。物体可以被放置在容器中,而容器则可以保护内容物。
人类使用容器的历史至少有十万年,甚至可能有数百万年的历史。
容器的类型:

  • 瓶 - 指口部比腹部窄小、颈长的容器。
  • 罐 - 指那些开口较大、一般为近圆筒形的器皿。
  • 箱 - 通常是立方体或圆柱体。形状固定。
  • 篮 - 以条状物编织而成。

Linxu 中的容器是什么呢? 其实最早的时候,容器技术并不是在 Linux 上的,而是在 FreeBSD 中的。

容器的发展

1. jails

jails

在大约 2000 年的时候,使用一种叫 jails 的技术,实现了类似于 Linux 之中 chroot 的方式,实现对文件系统的容器化管理,但是,jails 只能完成对文件系统的容器支撑。

众所周知,在系统运行中,有两颗大树,是“进程树”和“文件系统树”,“进程树”是由 init 顶级进程,和下面的很多枝枝叶叶的子进程组成,而“文件系统树”,是以 / 为顶级目录,下面也有很多枝枝叶叶的子目录组成,而 jails 只对“文件系统树”做容器管理,不能支持“进程树”。

2. VServer

vserver

Linux 觉得这种技术可能会在 Linux 上有用武之地,随后在 2001 年,便出现了 VServer,它基本上复刻了 jails 的实现方式,在 Linux 之上运行。
总的说,Linux 容器只不过是一组在系统上运行的线程而已,然后利用各种的 namespace 机制,让进程在隔离的空间内运行,这种进程和其他进程完全隔离开的机制,就是容器,那么,什么是 namespace 呢?

二、容器的技术

1. 名称空间

说起容器,就不得不说 Linux 中的 namespace(名称空间)

Linux 的容器发展到今天,为了支撑容器这种机制,也经历了很多坎坎坷坷,直到前几年,功能才开始完善起来。

namespace 是用来封装一个全局级别的资源,在一个抽象层上,从而使得我们可以把它进行切分,这种资源包括 6 个。

内核版本 namespace 隔离内容
2.6.19 UTS 主机名、域名
2.6.19 IPC 系统级的进程间通信、消息队列
2.6.24 PID 进程编号 可以生成多个 PID 为 0 的 init 进程
2.6.29 Network 网络设备、网络协议栈、端口等
2.4.19 Mount 挂载点 文件系统 利用 chroot,生成多个根文件系统
3.8 User 用户和用户组

2. Cgroup

容器里运行的进程虽然是隔离的,但是使用的 cpu、内存、IO 等资源,其实根本上还是物理机来提供的,但是如果某一个容器里的进程使用资源很多,抢占了其他容器的资源,甚至让物理机需要使用的资源都岌岌可危,这就不得了了。所以为了不坐视这种事情发生,就让 Cgroup 来管理所有容器使用的 cpu、内存等。Cgroup 会把底层的计算资源 cpu、内存、io 划分成块,有效的调派到容器,实行资源管理限制。

3. LXC

lxc

LXC(Linux container) 的出现简化了容器的使用方式,它能够通过运行一个程序,指定一个模板,来创建出一个用户空间来,模板可以是 ubuntu、windows、debian 等,这个模板就是一个脚本,这个脚本可以触发一个操作系统的安装过程,类似一个真正的安装一样,把 yum 仓库中要安装软件下载到本地,把指定的切根的位置当做真正的根,往里边安装程序包,从而生成假根,或者说是子树的目录结构和所有文件,这是安装生成的,所以它和一个操作系统相比,从本质上没有太大的区别,安装时间很长,安装好后,只要切换根,就得到了另外一套系统环境。

所以 LXC 的缺点是

  • 慢:和安装完整的操作系统没有太大的区别。
  • 难迁移:宿主机宕了就都宕了,除非使用 NFS 之类的共享存储。

但是总的来说,LXC 的使用已经简便了很多,但是量级很重,比起虚拟机来并没有很好的表现

有兴趣的可以看看红帽官网对容器的介绍(网站打开可能微慢)



三、Docker 和容器编排

Docker 是什么

docker

在 2010 年,几个搞 IT 的年轻人,在美国旧金山成立了一家名叫 dotCloud 的公司,那就是 Docker 的母公司,一个划时代的产品来了。         如果想了解 dotCloud 公司的历史可以戳这里

在 Docker 出现之前,大家想使用容器技术,就需要用上边提到的三个组件结合使用,复杂程度可想而知。而 docker 的出现,打破了这种局面,让使用容器的难度大大降低,而容器技术,并不是新生事物,在最早在 2000 年就已经出现了,而 Docker 最大贡献,是利用划时代的技术,将容器的技术推向了火热,那就是 docker 的 镜像。在 kvm 中,也使用了镜像技术,但是 docker 镜像和 kvm 镜像的区别是,docker 的镜像是 分层实现 的。

其实镜像也不是新生技术,但是 docker 的镜像设计精巧的地方在于,镜像内 只包含应用程序及其自身的运行环境 而已,因为这种技术的引入,以后要创建一个容器,就变成了下面这个样子:

  • docker 在互联网上建立了一个镜像仓库,拥有 docker 事先做好的各种镜像
  • 个人在本地服务器上运行着一个 docker 的守护进行,用户想要启动一个 nginx 的话,直接从镜像仓库下载 nginx 的镜像到本地,然后启动即可,再也不用配置 yum 源,再使用 yum install 安装之类的操作,只要一条命令,docker 守护进程就会从指定的镜像仓库把 nginx 的镜像下载回来,然后自动的创建好一个容器,在容器内运行这个 nginx 进程,而这一切,都是 docker 自动完成的。
    image

在 Docker 中贯彻的核心观念是,一个名称空间,只用来运行一个进程,及其这个进程的子进程

docker 最先是使用 LXC 来为自己提供容器管理,后来发现不能满足自己的需求,便研发了 libcontainer 使用,再后来演变成了符合 CNCF 通用标准的 runC,至于哪里冒出来的通用标准,那就是另一个故事了。

如果想了解 CNCF 是什么组织,以及 Docker 公司和 Google、CNCF 之间的“爱恨情仇”,戳这里

runc

总之一句话,Docker 不是容器,而是容器的管理软件。

容器编排

容器的运行是需要有 三个层次

  1. 容器引擎:运行单个容器的底层引擎。
  2. 容器编排:根据规则对容器进行调度、配置、组合、部署、回收、迁移等,以提供应用部署、维护、扩展机制等功能。
  3. 服务治理:按照 Anne Thomas Manes 的定义是:企业为了确保事情顺利完成而实施的过程,包括最佳实践、架构原则、治理规程、规律以及其他决定性的因素。服务治理指的是针对 SOA 的建设和实施的过程。当然现在也适用于微服务的建设和实施过程。

参考

1. Docker 的容器编排

容器单一的运行是没有价值的,需要用容器的 编排系统,所以 docker 的公司推出了自己的容器编排系统:docker 编排三剑客。

名字 功能
docker-machine 用来构建运行 docker 的主机
docker-swarm 负责 docker 集群管理
docker-compose 容器编排引擎

2. Google 的容器编排 kubernetes

k8svs

其实容器技术在 google 已经运行了十几年,作为内部运行高效 IT 系统的不传之秘,没有公开过,现在 docker 开源了容器技术,而且异常火热,google 为了不让自己失去对容器技术的话语权,所以把自己的容器技术公开出来,但是对比 docker,显得很简陋和粗糙,简直不是同一个时代的。

但是 google 自己使用的容器编排技术 Borg,在内部已经运行十几年了,而且 google 在自己庞大的业务支持下,积攒了很多容器编排的技术,所以 google 就用自己家的 GO 语言,结合对容器编排丰富的经验,从头编写,造出了 Kubernetes,这对 docker 来说是一个很大的打击,堪比小说《三体》中,四维世界对三维世界的降维打击一样,打的 docker 毫无还手之力,所以 docker 自家的编排系统系统 docker-swarm,基本已经凉凉了,只有很少很少的公司还在使用。

扩展阅读:
从 Docker 的转变,谈容器生态与微服务的发展
OCI 和 runc:容器标准化和 docker



四、Docker 的详细使用方法

Docker 的一些基本知识

docker 属于 cs 架构,有客户端和服务端,两者之间通过 http 或 https 协议,使用 unix 套接字进行本地通信,相对于使用 TCP 的套接字应用来说,效率很高

在 docker 内部,使用了基于 RESTful 风格的 API 提供服务,所以 docker 将内部所有的管理组件视为对象 (Object),同一个对象被抽象成资源(Resource Type) 的概念,每个资源类型相当于 mysql 中的一个定义好的数据结构,在 docker 内部,一些比较重要的资源是 ContainerImageNetworkVolume,docker 的客户端就是去连接 Daemon 端,去完成某一个对象的增删改查等操作,对应在 HTTP 里的无非就是 GET,PUT,DELETE,POST

Docker 的主要部件和功能

  1. client

    • docker build
    • docker pull
    • docker run
  2. server daemon

    • containers 容器运行时环境
    • images   镜像存储环境 依赖分层文件系统:
      • aufs linux     内核没收录 centos 不支持 ubuntu 支持
      • overlayfs      红帽最新版支持的分层文件系统
      • dm(devicemapper)  性能差的一批
  3. Registry:可以理解为仓库 (Repository) 的注册列表,一般一个仓库 (Repository) 只用来放一个应用程序 (Application) 的不同版本 (Version),每个版本可以不用版本号标识,而是通过标签(tag) 来标识,一个镜像可以有多个标签,但是只有一个唯一的 ID 标示



CentOS 安装和配置 docker-ce 方法

docker 有 2 个版本:

  • docker-ce 开源社区版
  • docker-ee 商业版

ce 和 ee 的详细差异可以查看这篇文章

这里主要介绍 docker-ce 的安装和使用,CentOS 官方源中也收录了 docker 二进制包,但是比较老旧,我们可以按照官方的安装文档,或者使用国内的各大 mirrors 站点,比如阿里云来安装最新版

1. 从 docker 官方安装

在新主机上首次安装 Docker CE 之前,需要设置 Docker 存储库。之后,可以从存储库安装和更新 Docker。

1
2
3
4
5
6
7
8
9
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2

yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

yum install docker-ce

2. 从阿里云安装

下载阿里云 mirrors 站点中关于 docker 的 repo 文件:

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

yum install docker-ce

3. 配置镜像加速

因为某些不可抗力,如果国内访问 DockerHub 比较慢,可以使用国内的镜像加速服务,如果感觉速度不慢也可以不使用,这里推荐用阿里云或 docker 官方的,任选一种即可

1
2
3
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}

4. 启动 docker 守护进程

1
2
systemctl daemon-reload
systemctl start docker

docker 的守护进程不是 docker,而是containerd

1
2
3
4
[root@docker ~] ps -ef|grep containerd
root 7817 1 00:04:56 /usr/bin/containerd
root 7818 1 00:00:07 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 8770 8741 00:00:00 grep --color=auto containerd

docker daemon 启动后会创建一个叫 docker 的网桥,默认网段是 172.17.0.0/16

1
2
3
4
5
6
7
8
9
[root@centos_1 docker] ip a s docker0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:22:ce:de:82 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever

[root@centos_1 docker] brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024222cede82 no

解决防火墙 FORWORD 链默认 DROP

安装 Docker 后,Docker 会把防火墙的 FORWORD 链默认规则改成 DROP,在有些情况下会影响网络转发,就算手动改成 ACCEPT,Docker 服务重启后还会恢复默认 DPOP,下面说一个永久的解决办法。

修改 Docker 的服务脚本。

1
vim /usr/lib/systemd/system/docker.service

ExecStart 下添加一行,意思是 Docker 启动后,把 FORWARD 改成 ACCEPT,但前提是需要安装了 iptables

1
2
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock   # 这行不动
ExecStartPost=/usr/sbin/iptables -P FORWARD ACCEPT # 添加这行

然后刷新 system 文件,并重启 Docker。

1
2
systemctl daemon-reload
systemctl restart docker

五、docker 客户端常用命令

查看版本信息

docker version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@centos_1 ~] docker version
Client: 客户端版本
Version: 18.09.7
API version: 1.39
Go version: go1.10.8
Git commit: 2d0083d
Built: Thu Jun 27 17:56:06 2019
OS/Arch: linux/amd64
Experimental: false

Server: Docker Engine - Community 服务器端版本
Engine:
Version: 18.09.7
API version: 1.39 (minimum version 1.12)
Go version: go1.10.8
Git commit: 2d0083d
Built: Thu Jun 27 17:26:28 2019
OS/Arch: linux/amd64
Experimental: false

输出详细信息

docker info

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@centos_1 ~] docker info
Containers: 0 # 摘要信息
Running: 0 # 运行了多少容器
Paused: 0 # 暂定了多少容器
Stopped: 0 # 停止了多少容器
Images: 0 # 当前系统上有多少个镜像
Server Version: 18.09.7 # 服务端版本
Storage Driver: overlay2 # 存储驱动,在本地 xfs 基础上
Backing Filesystem: xfs # 本地是 xfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs # 资源控制功能
Plugins:
Volume: local # 卷插件 本地
Network: bridge host macvlan null overlay # 网络插件
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog # 日志插件
Swarm: inactive # docker 自带的编排工具
Runtimes: runc # 运行环境
Default Runtime: runc
Init Binary: docker-init
containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-957.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 972.6MiB
Name: centos_1
ID: Y43I:THLF:5LNG:4ISY:RTGH:3VSQ:OL5D:MXEQ:XBPB:GQGM:J5IB:QRFE
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine

docker client 常用命令参数

在 docker 比较旧的版本中,各种命令混在一起,在新版中将命令都分门别类了
每个命令后可以加 --helphelp 查看语法等帮助信息,建议多用 help,这里不注明详细语法

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
login 登录到 docker_hub
search IMAGE_NAME 从 DockerHub 搜索镜像

image: # 镜像类工具
ls 列出镜像 = docker images
pull 从远程下载镜像到本地
push 上传镜像
rm 删除镜像 = rmi 大杂烩命令
tag 给镜像打标签
inspect 查看镜像详细信息

container: # 容器类命令
ps 查看运行的容器
-a 查看所有状态下的容器

-f --filter 过滤显示 格式是 key=value 可以连接多个 -f id=value -f name=value
id (容器 id)
label (label = 或 label=>)
name (容器名称)
exited (整数 – 容器退出码。只在使用 –all 才有用)
status (created restarting running paused exited dead)
ancestor ([:], or ) – 过滤从指定镜像创建的容器。
before (容器的名称或 id) – 过滤在给定 id 或名称之前创建的容器。
since (容器的名称或 id) – 过滤在给定 id 或名称之后创建的容器。
isolation (default process hyperv) (Windows daemon only)
volume (数据卷名称或挂载点) – 过滤挂载有指定数据卷的容器。
network (网络 id 或名称) – 过滤连接到指定网络的容器。

--format 格式化输出 使用 Go 模板来美化打印容器输出, 可定制 格式是 --format "table {{.ID}} \t {{.Mounts}}"
.ID 容器 ID
.Image 镜像 ID
.Command 引用命令
.CreatedAt 创建容器的时间点
.RunningFor 从容器创建到现在过去的时间
.Ports 暴露的端口
.Status 容器状态
.Size 容器占用硬盘大小
.Names 容器名称
.Labels 容器所有的标签
.Label 指定 label 的值 例如 {{.Label “com.docker.swarm.cpu”}}
.Mounts 挂载到这个容器的数据卷名称

exec 在容器内执行命令
create 创建容器
start 启动容器
run 创建 + 启动
--name 执行容器名字
-i 交互式
-t 附加 TTY 终端
-network 指定网络
--rm 容器一旦停止即刻删除
-d 剥离当前终端 = --detach
-h 指定容器的主机名 = --hostname
-e 传递参数 如 mysql 要求管理员必须有密码,启动容器时注入 docker run -e MYSQL_ROOT_PASSWORD=monster
--add-host 注入解析到 hosts 文件
--dns 注入 dns 地址
--dns-search 注入搜索域
-p 定制对外暴露的方式 (DNAT)

stop 停止容器 = kill -15
kill 强制杀掉容器 = kill -9
restart 重启
paush 暂停
unpause 恢复
rename 重命名
rm 删除
log 查看日志 docker 日志是发往终端的,而不是集中在文件中
--tail n 实时刷新
stats 查看所有容器对资源的占用情况
top 显示一个容器内进程和子进程的信息和资源占用情况
attach 连接到容器的终端中
commit NAME monster0303/mying:v0.1 把容器做成镜像
-p 制作镜像时暂停容器
-a 指定镜像的作者
-c 改变底层默认运行的程序
示例:docker container commit -p -a "monster <admin@monster.com>" -c 'CMD ["/bin/sh","-c","/bin/sh"]' centos_monster centos:sh
# 把一个叫 centos_monster 的容器(容器名,不是镜像名)容器打包成叫 centos:sh 的镜像
# 先暂停容器,指定作者是 monster,将默认要运行的命令 (bash) 改为 sh

network: # 网络类选项
ls 显示当前的网络信息
create 创建网络
-d 驱动 默认 bridge
--subnet 指定子网,默认第一个地址是网关
--gateway 指定网关
--ip-range 指定地址分配范围,默认使用所有可用网络

docker network create --subnet 10.10.0.0/24 mybr0

启动容器

1. 下载镜像到本地

不指 tag 默认是最小化最新版的 Centos7 系统环境,这是一个非常小的系统,可以使用 yum 安装软件

1
docker image pull centos

2. shell 型容器的启动方法

比如 centos

1
2
3
4
5
6
7
8
9
10
11
12
                   #  t:tty 附加终端  -d:运行在后台,不加直接在前台
docker container run --name c1 -it -d centos:7 /bin/bash
# i:交互式 c1:容器名 指定运行的 shell

#可以使用 attach 连接终端
docker container attach c1

#组合键剥离终端到后台
ctrl+p ctrl+q

#在容器内执行命令
docker container exec c1 COMMAND

3. 服务型容器

启动方法,以及会遇到的问题,比如 nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#如果不加 -d 会运行在前台,显示 nginx 的logs 信息,并阻塞当前 shell
docker container run --name nginx -d nginx

#还有一种启动方法,可以加 -it,并指定 shell,容器会在启动时打开 shell 直接连入
#如果不加 -d 会直接在前台打开 shell,加上 -d 会剥离终端在后台运行
#但是这么做已经不是在运行镜像本身的 nginx,而是运行/bin/bash,所以需要进入容器手动启动 nginx
#并且这时 /bin/bash 才是 PID 为 1 的进程,而不是 nginx,所以不可以退出 shell,而需要使用剥离,否则容器会直接停止
docker container run --name nginx -d -it nginx /bin/bash

#如果启动时用 -d 使容器在后台运行,并指定了 shell,可以使用 attach 直接连入容器 shell
docker container attach nginx

#如果启动时用 -d 使容器在后台运行,但是没有指定 shell 也没关系,可以使用 exec 在容器内运行/bin/bash 命令加 -it 直接进入 shell
docker container exec -it nginx /bin/bash

需要 特别注意 的是,启动的容器,如果没有特定指明执行命令的基础环境,默认运行的环境都是 shell,而 shell 是交互式接口,所以像 centos 这种不是以守护进程运行的镜像,都需要指定使用交互式方式运行,不然 shell 一启动就会马上停止,容器也就一起挂了

如果运行服务型的容器,容器中 PID 为 1 的服务,不能和往常一样以 Daemon 守护进程的方式在容器内后台运行(并不是指在后台运行容器),这违背了 docker 的规则,一旦程序在后台运行,容器就没认为没有框架支撑这个空间了,就会停止容器,所以服务要运行在前台,不然一启动就停止,一启动就停止

一般 -d-rm 不在一起使用,因为 -d 放到后台 -rm 就无法判断状态了

如果想把容器的 shell 从当前终端剥离,可以使用下面的组合键,这样只是剥离而不是运行到后台,所以容器不会停止。ctrl + pctrl + q

kawayi