Docker 基础管理
容器是什么
VM虚拟机
试想以下场景,你是一名计算机信息安全工程师,最近在研究一些病毒程序,你想看一下当年祸及全网臭名昭著的某款病毒是什么样子的,你会怎么做?
我想你肯定猜到了,使用VM新建虚拟机,在虚拟机下运行病毒程序,这样就不会产生任何威胁。
想想,虚拟机拥有哪些特性呢?最显著的特性无非以下几点:
- 具有隔离性(宿主机和虚拟机间可以互不影响)
- 具有封装性(半封装或者全部封装)
- 具有兼容性
- 独立于硬件
但是虚拟机有一个非常大的问题就是非常占用内存,也非常吃CPU,有没有一种更加轻量级的解决方案呢?
FreeBSD Jail
早期的时候,一款类Unix操作系统FreeBSD上首次出现了容器技术,名为jail(监狱)。
它对比与单纯的直接开虚拟机,更为轻量化,在启停方面能占用更少的资源,但是也有一大问题,环境构建难。
以下是该技术的一些小特点,摘自维基百科:
- 虚拟化:每个软件监狱(jail)都是在主机机器上运行的一个虚拟环境,有它自己的文件系统,行程,用户与超级用户的账户。在软件监狱内运行的行程,它面对的环境,跟实际的操作系统环境几乎是一样的。
- 安全性:每个软件监狱都是独立运作,与其他软件监狱隔离,因此能够提供额外的安全层级。
- 容易删除及创建(维基百科说的,我没说):因为每个软件监狱的运作范围有限,这使得系统管理者可以在不影响整体系统的前提下,以超级用户的权限,来删除在软件监狱下运作的行程。
具体来说,我们应该从哪些部分来构建一个监狱环境?
- 主机名和域名(UTS),你必须保证jail环境与宿主环境的UTS不会产生冲突
- 文件系统隔离(Mount),你必须保证jail环境与宿主环境的文件系统互不干扰,即chroot
- 进程通信隔离(IPC),你必须保证jail环境与宿主环境的进程之间通信互不干扰
- 进程号隔离(PID),你必须保证jail环境与宿主环境中各自的进程号不会冲突
- 用户空间隔离(User),你必须保证jail环境与宿主环境中各自的用户不会产生冲突
- 网络空间隔离(Network),你必须保证jail环境与宿主环境中各自的网络配置不会产生冲突
上面的这些标注点,会通过Linux内核组成一个NameSpaces,不同的NameSpaces资源相互隔离。
光有上面这些条件还不够,你还需要通过Cgroups控制每个不同的NameSpaces之间的资源分配,如下所示
- 资源限制管理
- 优先级设定
- 资源计量
- 资源控制
解决了上面这些问题,你就能搭建一个真正的jail系统来测试病毒了。
当然,前提是你需要非常深厚的C语言功底,这显然不现实,我只想测试一个病毒,结果还要学这么多知识?
故jail因为搭建和销毁困难,也消失在了历史的长河中。
LXC
LXC是Linux中基于FreeBSD jail来做的,它不需要用户再手动的创建及管理容器,即开即用,十分的方便,这也是Docker的前身,但是LXC也有一些弊端,不方便大规模应用。
- 不支持跨主机之间的容器迁徙
- 管理相较于Docker来说还是比较繁琐
- 安全隔离性较差
Docker
Docker是在2013年由Docker公司基于LXC开发,一经问世便十分火热,相较于LXC,它更加的轻量级,且更易于学习和管理容器,是目前最流行的Linux容器解决方案。
体系结构如下,它是一款C/S架构软件,docker daemon作为Server端接收Client端的请求,在Docker世界中,所有常用的应用都被做成了Docker images,你只需要在Docker repository进行下载后使用docker daemon进行启动,即可创建出该应用的容器,需要注意的是不同容器之间允许互相通信:
对于上面的病毒测试需求,你可以直接pull一个centos的镜像,启动这个centos镜像来创建一个容器。
然后在容器中进行病毒测试,这是十分方便的。
容器与虚拟机区别
如下图所示:
Host OS是服务器虚拟化中的一个概念,有一种虚拟化技术是在物理机器上安装操作系统,然后在这个操作系统上安装Hypervisor虚拟化软件,这样就可以在物理机上虚拟化出若干分区,可以分别安装不同的操作系统。那么在这个物理机器上安装的操作系统就叫做Host OS,对应的安装在虚拟分区上的操作系统叫做Guest OS。
左图中的Guest层,还有Hypervisor层在Docker上已经被Docker Engine层所取代,Guest OS 是虚拟机安装的操作系统,是一个完整的系统内核,另外Hypervisor可以理解为硬件虚拟化平台,它在Host OS内以内核驱动的形式存在。
虚拟机实现资源的隔离的方式是利用独立的Guest OS,以及利用Hypervisor虚拟化CPU、内存、IO等设备来实现的,对于虚拟机实现资源和环境隔离的方案,Docker显然简单很多。
Docker并没有和虚拟机一样利用一个独立的Guest OS执行环境的隔离,它利用的是当前Linux内核本身支持的容器方式,实现了资源和环境的隔离,简单来说,Docker就是利用Namespace实现了系统环境的隔离,利用了Cgroup实现了资源的限制,利用镜像实例实现跟环境的隔离。
与虚拟机相比,容器有一个很大的差异,它们被设计用来运行"单进程",无法很好地模拟一个完整的环境,Docker 设计者极力推崇“一个容器一个进程的方式”。
一句话总结,相较于虚拟机,容器的启动更加迅速,管理更加高效,部署更加简单,迁徙更加方便,但是Docker容器的使用应该是具有单一目的的,而不要将Docker容器当做虚拟机来进行使用啥都往进装。
安装Docker
Docker有社区版Docker CE与企业版Docker EE,所以我们下载Docker CE即可。
Docker官方的文档十分齐全,你可以选择参照Docker的官方文档进行安装:
镜像管理
镜像加速
如果你按照官方文档安装完Docker后,我推荐你修改一下镜像的下载源为国内,否则可能发生镜像pull的很慢的情况。
下面将把Docker的Docker-hub配置为aliyun:
$ mkdir -p /etc/docker
$ tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://uoggbpok.mirror.aliyuncs.com"]
}
EOF
最后进行重启:
$ systemctl daemon-reload
$ systemctl restart docker
获取镜像
通过search命令查找centos的进行:
$ docker search centos
如果名称中没有/的分割,则代表是官方镜像,否则是第三方镜像。
使用pull命令从镜像仓库中将centos镜像和nginx镜像进行拉取:
$ docker pull centos:7.5.1804
$ docker pull nginx
可以看到这个centos的镜像非常小,仅有74.69MB,这意味着精简了许多功能,因为Docker容器追寻的就是精简化。
在很多其他的镜像中,如nginx镜像等使用的其实并不是centos作为容器载体,而是Debian系统(Linux的一款发行版),因为它相较于centos更简洁。
直接搜索并获取以及运行镜像创建容器:
$ docker run -it --name="centos" centos:7.5.1804
查看镜像
使用image ls命令对已有镜像进行查看:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest f6d0b4767a6c 2 months ago 133MB
centos 7.5.1804 cf49811e3cdb 2 years ago 200MB
# SIZE的不同是因为下载时是压缩的,当镜像下载完成后就自动解压了
# 选项-a可查看隐藏镜像
标识一个镜像的唯一方式有2种:
- REPOSITORY : TAG
- IMAGE ID
IMAGE ID本身是一个sha256加密的64位号码,默认只截取12位,如果你想截取全部,可以使用以下命令:
$ docker image ls --no-trunc
查看详情
如果你想查看一个镜像的详细内容,可使用如下命令:
$ docker image inspect cf49811e3cdb
导入导出
你可以将镜像导出成一个TAR的安装包:
$ docker image save cf49811e3cdb > /tmp/centos.tar
然后将该包进行导入:
$ docker image load -i /tmp/centos.tar
当然,如果该镜像已存在,会导致你导入不进去。
如果你导入进去了,默认的REPOSITORY和TAG都是NONE,你需要用下面的知识给它重新改名。
镜像标签
使用tag为镜像打上一个标签,其实就是变更镜像的name,这常用于自建镜像中。
$ docker image tag cf49811e3cdb yunya/centos:v1
需要注意,镜像标签的tag必须全小写,当你为已有的一个镜像打上tag后,它不会抛出异常,而是重复创建,如下所示:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest f6d0b4767a6c 2 months ago 133MB
centos 7.5.1804 cf49811e3cdb 2 years ago 200MB
yunya/centos v1 cf49811e3cdb 2 years ago 200MB
镜像删除
使用rm -f来删除某个已有的镜像:
$ docker image rm -f yunya/centos:v1
# f代表强制删除
如果你想删除所有已有的镜像,可以使用如下命令:
$ docker image rm -f `docker image ls -q`
好像MySQL中的子查询,先查询出结果再统一进行rm操作。
容器管理
容器类型
容器类型分为2种,为服务型容器和交互型容器。
例如:nginx这个镜像所创建的容器就是服务型容器,而centos这个镜像所创建的容器则为交互性容器。
交互型容器常用于对代码的调试,对病毒的研究等领域中。
而服务型容器容器则提供持续对外的服务,并不是一次运行。
运行容器
交互型容器运行使用以下命令:
$ docker container run -it --rm centos:7.5.1804 /bin/bash
# i是指能够交互
# t是指tty,即分配一个伪终端
# /bin/bash是指该容器运行的镜像内第一个脚本,可不加
# --rm是指运行完成后立即清除容器,不保存至容器列表,可不加
# --restart=always是指docker服务重启后立即重新运行该容器,而不需要手动start
从容器的交互中退出,可使用exit命令,这一并会关闭掉该容器,亦可使用快捷键CTRL+D。
如果仅想从容器交互中退出而不关闭容器,则可以使用CTRL+P加上CTRL+Q分别按。
服务型容器运行使用以下命令:
$ docker container run -d nginx:latest
# d是指daemon,守护程序
每个镜像可以创建多个容器,在容器创建完成后会生成一个ID号以及随机名称。
你可以使用--name选项来指定容器的名称,如:
$ docker container run -d --name="nginx_01" nginx:latest
$ docker container run -it --name="centos_01" centos:7.5.1804
查看容器
使用以下命令可查看所有的容器:
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7dfd5b1380e4 centos:7.5.1804 "/bin/bash" 11 minutes ago Exited (0) 11 minutes ago centos_01
1ee9fa2fdc3b nginx:latest "/docker-entrypoint.…" 11 minutes ago Up 11 minutes 80/tcp nginx_01
4381870b59fc nginx:latest "/docker-entrypoint.…" 14 minutes ago Up 14 minutes 80/tcp dreamy_banach
772135e5c992 centos:7.5.1804 "/bin/bash" 15 minutes ago Exited (127) 14 minutes ago elegant_johnson
- CONTAINER ID :容器ID
- IMAGE:容器镜像
- COMMAND:容器启动时加载的镜像内初始化程序
- CREATED:容器创建时间
- STATUS:容器运行状态
如果你想查看所有正在运行的容器,则不用加参数-a
如果你想查看所有正在运行的容器的详情,可以加-l
查看详情
如果你想查看一个容器的详细运行情况,如占用的端口,IP地址等,可以使用以下命令:
$ docker container inspect 1ee9fa2fdc3b
# NetworkSettings选项
启停容器
如何启动,停止一个容器?有下面2个命令。
如果对于一个服务型容器来说,你可以直接进行启停操作。
$ docker container stop nginx_01
$ docker container start nginx_01
如果对于一个交互型容器来说,你可在启动时指定 -i参数,让其能够立马进入交互:
$ docker container stop centos_01
$ docker container start -i centos_01
进入容器
如果你想进入一个正在运行的容器,可有2种方法。
首先是attach方式,这种方式通常会对交互型容器进行使用,在启动交互型容器时指定了-dit的参数,后面又想连进去:
$ docker container run -dit --name="centos_100" centos:7.5.1804
$ docker container attach centos_100
$ exit
需要注意的是,使用attach方式进入容器后,一旦使用exit退出容器就会被关闭。
可以看见centos_100这个容器已经被关闭了:
$ docker container ls
图示:
第二种方式更加常用,使用exec方式进入容器,通常会对服务型容器使用,用于在里面进行配置等操作,需要指定-it参数并且指定进入时运行的shell以获得交互功能,如下所示:
$ docker container exec -it nginx_01 /bin/bash
这种方式在exit退出后并不会销毁容器,因此也是更常用的一种方式。
如图所示:
删除容器
删除一个已关闭的容器:
$ docker container rm -f centos_100
删除所有已关闭的容器:
$ docker container prune
$ docker container rm -f `docker container ls -a -q`
导入导出
每次使用镜像启动的容器都会保持镜像中的初始状态。
如果你的镜像经历过一些变更,而你又想对其进行持久化保持以便后续在别的机器上使用时可以对容器进行导入导出。
导出:
$ docker export centos_100 > centos100.tar
导入:
$ docker import centos100.tar centos_100:7.5.1804
导入的容器会保存在镜像中:
$ docker image ls
centos_100 7.5.1804 82c1d87ed6af 43 seconds ago 200MB
nginx latest f6d0b4767a6c 2 months ago 133MB
centos 7.5.1804 cf49811e3cdb 2 years ago 200MB
内部进程
使用以下命令查看容器内部进程信息:
$ docker top nginx_01
容器日志
使用以下命令查看容器的日志:
$ docker logs -ft nginx_100
# -f : 跟踪日志输出
# --since :显示某个开始时间的所有日志
# -t : 显示时间戳
# --tail :仅列出最新N条容器日志
外部访问
默认的服务型容器在启动时会自动分配IP+PORT。
这个IP+PORT对其宿主机有效,对其他机器无效。
比如,我的Mac是真实的物理机,Linux是虚拟机,Docker是安装在虚拟机上的,所以Linux能访问Docker的Nginx容器服务,而Mac访问不了。
我们需要在Nginx容器建立时指定将容器PORT映射在宿主机Linux的某一端口上,如下所示:
$ docker container run -d -p 80:80 --name="nginx_100" nginx:latest
现在,Mac就能访问了,在Mac浏览器上输入Linux地址,就可以看见Nginx页面:
你可以指定的方式多种多样,如下所示:
# 映射所有接口地址,宿主机80映射到容器80
$ docker run -d -p 80:80 nginx:latest
# 映射到指定地址的指定端口,127.0.0.1:80映射到容器80
docker run -d -p 127.0.0.1:80:80 nginx:latest
# 映射到指定地址的任意端口 127.0.0.1的任意端口映射到容器80,可使用inspect命令查看容器详情获得
$ docker run -d -p 127.0.0.1::80 nginx:latest
# 还可以使用udp标记来指定udp端口,没啥用,做DNS服务器能用上
$ docker run -d -p 127.0.0.1:80:80/udp nginx:latest
如果一次指定多个-p参数,则可绑定多个端口:
$ docker run -d \
-p 80:80 \
-p 443:443 \
nginx:latest
数据卷
功能概述
Nginx默认挂的index页面如果我们想对其修改,能用什么方式呢?
通过exec进入容器修改里面的代码吗?这样太蠢了,如果代码很多就要手动一条一条敲。
通过scp命令进行上传吗?Nginx容器中基础操作系统是debian,有可能没有ssh服务相关的软件,所以不太现实。
最好的办法是让容器与宿主机中某一目录做映射,当宿主机上目录内容更改时已启动的容器内部映射目录也跟着修改。
Volume
第一步,在宿主机中建立目录与文件:
$ mkdir -p /opt/nginx/html
# Nginx容器中,index.html的存放目录为:
# /usr/share/nginx/html/index.html
第二步,运行Nginx容器时,指定该容器中Nginx目录与宿主机的目录做映射:
$ docker run -d --name="nginx_a" -p 80:80 -v /opt/nginx/html:/usr/share/nginx/html nginx:latest
第三步,修改宿主机/opt/nginx/html/index.html的文件:
$ vim /opt/nginx/html/index.html
hello,world
尝试在Mac上访问Linux的IP地址,查看Nginx首页是否改变。
数据卷容器
如果有多个容器需要使用同一个目录,可将该目录作为数据卷容器供其使用。
第一步,宿主机上模拟数据目录:
$ mkdir -p /opt/Volume/a
$ mkdir -p /opt/Volume/b
$ touch /opt/Volume/a/a.txt
$ touch /opt/Volume/b/b.txt
第二步,启动数据卷容器:
$ docker run -d --name "nginx_volumes" -v /opt/Volume/a:/opt/a -v /opt/Volume/b:/opt/b centos:7.5.1804 /bin/bash
第三步,使用数据卷容器:
$ docker run -d -p 8085:80 --volumes-from nginx_volumes --name "n8085" nginx:latest
$ docker run -d -p 8086:80 --volumes-from nginx_volumes --name "n8086" nginx:latest
第四步,随意登录一个nginx容器服务,查看opt目录:
$ docker container exec -it n8085 /bin/bash
root@0d160bda09d9:/# ls /opt/{a,b}/
/opt/a/:
a.txt
/opt/b/:
b.txt
在集中管理集群中,大批量的容器都需要挂载相同的多个数据卷时,可以采用数据卷容器进行统一管理。