2-Docker基础
2-Docker基础
帮助命令
docker version
:查看docker版本信息docker info
:查看Docker系统信息docker 命令 --help
:查看指定命令的用法
镜像常用命令
docker images
:查看所有已下载的镜像,与docker image ls
等价可选项:
-a
:列出所有镜像-q
:只显示镜像ID
docker search 镜像名称
:搜索镜像docker pull 镜像:tag
:从公开仓库拉取指定镜像,如果不加tag版本,默认拉取最新版本latestdocker rmi 镜像名称/ID
:删除指定镜像,等同于docker image rm 镜像名称/ID
docker image prune
:清理临时的,没有被使用的镜像文件,可用选项,-a
:删除所有没有用的镜像,而不只是临时文件
容器常用命令
docker container ls
:查看所有容器,包括暂停、停止、正在运行的容器docker ps
:查看正在运行的容器可选项:
-a
:列出所有容器,和docker container ls
等价
运行容器
docker run [可选参数] image [command]
:使用镜像运行一个容器,其中[command]是运行容器后可执行的命令。
可选项:
-d
:后台方式运行-i
:交互式方式运行-t
:终端,一般与-i
配合使用-p
:有两种用法,-p 主机端口:容器端口
或-p 容器端口
,前一种设置了主机端口和容器端口的映射--name
:容器运行后,docker会设定一个名称,也可使用该选项设置自定义名称-e
:环境配置,可以理解为配置环境变量
进入容器
使用-d
参数,容器会后台运行,有两种进入容器的方式。
docker attach 容器ID
:该命令进入容器后,如果退出,容器会停止运行docker exec
:该命令退出容器时,不会导致容器退出例:进入linux系统:
docker exec -it 容器ID /bin/bash
并且docker exec
进入容器后,会打开新的终端;而docker attach
进入容器后,会进入现有终端。
因此当使用attach
进入容器,退出后,没有了前台进程,容器会自动停止运行。
退出容器
在使用进入容器后,有两种方式退出容器。
exit
:直接在终端输入exit
退出容器,容器会停止运行并退出Ctrl + P + Q
:使用快捷键,这种方式只会退出容器,容器不会停止运行
启动、停止容器
docker stop 容器ID
:停止运行指定容器
docker kill 容器ID
:强制停止容器
可以使用docker start 容器ID
重新运行该容器。
也可使用docker restart 容器ID
重新运行。
restart
和start
的区别
start
:包含容器文件系统挂载的操作restart
:不包含容器文件系统的卸载和挂载操作
restart
是重启一个正在运行的容器,而start
是重新运行已经停止的容器。
因此在对容器重启时,推荐使用docker stop
和docker start
删除容器
docker rm 容器ID
:删除指定容器,正在运行的容器无法删除
如果要删除所有容器,则可以使用:
docker rm -f $(docker ps -aq)
:先通过docker ps -aq
查询所有容器ID,然后使用docker rm -f
删除。
其他常用命令
查看日志
docker logs [可选参数] 容器名称/ID
:查看指定容器的运行日志
可选项:
--details
:展示详细日志-t
:展示时间戳-f
:跟踪日志输出,即实时显示当前正在输出的日志--tail string
:显示指定日志条数,string为日志条数,如查看十条日志:--tail 10
查看容器内部运行进程
docker top 容器ID
查看容器元数据
docker inspect 容器
:返回容器的配置信息
容器内文件与主机文件相互拷贝
容器内文件拷贝到主机:
docker cp 容器ID:文件路径 主机目录路径
只要该容器没有被删除,就可以拷贝。即使该容器停止。
将容器ID:文件路径 主机目录路径
改为主机文件路径 容器ID:容器内目录路径
就可将主机内文件拷贝到容器内。
Docker镜像原理
UnionFS(联合文件系统)
UnionFS(联合文件系统)是一种分层、轻量级、高性能的文件系统。它支持对文件系统的修改作为一次提交来一层层的叠加。
Union文件系统是Docker镜像的基础,镜像可以通过分层来继承,基于基础镜像,可以制作各种类型的应用镜像。
特性:Union文件系统会以此同时加载多个文件系统,但从外面来看,只会看到一个文件系统,它会把各层文件系统联合加载,叠加起来,这样最终的文件系统会包含所有文件和目录。
Docker镜像加载原理
docker镜像是由一层一层的文件系统组成,使用的就是层级文件系统UnionFS.
镜像在下载时,也是一层一层进行下载,各层可以复用。
例如MySQL和Tomcat镜像都需要使用linux内核,那么如果下载了MySQL,那么在Tomcat镜像下载时,linux内核层不需要再下载,,直接复用下载MySQL时所下载的层即可。
bootfs(boot file system)主要包含bootloader和kernel,即启动加载器和内核,bootloader主要负责引导加载kernel,因此在Docker镜像的最底层是bootfs。
rootfs(root file system)在bootf之上,对于不同的Linux发行版,rootfs会有区别。
在Docker使用不同镜像时,共用宿主机系统的bootfs,因此启动加载速度会很快。也是层级文件系统的思想。
例如下载redis镜像
可以看到,镜像是分层下载的。
所有Docker镜像都基于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
例如,基于Ubuntu系统创建一个新的镜像,这就是新镜像的第一次,如果在该镜像中添加Python包,则会创建第二层,以此类推。
Docker镜像都是只读的, 当使用镜像启动容器时,一个新的可写层被加载到镜像的顶层。这一层就是容器层。可以将之前的镜像层和新增加的容器层打包成一个新的镜像。
Docker commit命令
docker commit
:从容器中创建一个新的镜像
语法:docker commit [可选项] container [Repository:[TAG]]
可选项:
-a
:指定提交的镜像作者-c
:使用Dockerfile指令创建镜像-m
:提交时的说明文字-p
:在commit时,将容器暂停
如果想要保存容器当前的运行状态,可使用该命令创建新的镜像。
例如,下载tomcat镜像后,webapps目录为空,可以将webapps.dist目录中文件拷贝到webapps中,然后将当前容器状态创建为一个新的镜像,可使用指令:
docker commit -a "ZhaoXin" -m "add files to dir webapps" 0771adb17da5 new_tomcat:1.0
仓库名称和标签可以自定义。
Docker数据卷
什么是容器卷
数据卷可以实现宿主机和Docker容器之间的文件共享,也可以实现Docker容器间的文件共享。可以理解为目录的挂载。
为什么要使用数据卷
如果数据都在容器中,那么当容器删除后,数据就会丢失。因此需要使用数据卷进行数据持久化。
同时,有时候也需要将主机目录文件和容器目录文件进行同步。
数据卷的使用
使用数据卷后,对宿主机文件的修改会直接影响容器,而不需要将宿主机的文件复制到容器中。
如果运行tomcat时,想将宿主机中/opt/apps
目录与容器中webapps
做一个数据卷(实际就是建立两个目录之间的映射),则指令为:
docker run -d -p 8080:8080 -v /opt/apps:/usr/local/tomcat/webapps tomcat:8.0-jre8
即在运行容器时使用-v 宿主机目录:容器内目录
格式来建立数据卷。
具名挂载和匿名挂载
具名挂载
如果使用docker -v
挂载目录时,没有写完整的主机路径。
例如-v tomcat:/usr/local/tomcat/webapps
,即前面的主机路径并不是/
开头的路径名。
那么Docker默认会在/var/lib/docker/volumes
目录下,创建tomcat
和tomcat/_data
目录,然后将_data
目录与容器内指定目录进行映射。
使用docker volumes ls
查看时,VOLUME NAME
为tomcat
。
也就是说,如果没有指定宿主机路径,那么Docker默认使用/var/lib/docker/volumes
目录。
匿名挂载
如果挂载命令为-v /usr/local/tomcat/webapps
,即只写了容器挂载路径,此时为匿名挂载。
Docker会设定一个字符串为该卷名称,并在/var/lib/docker/volumes
目录中创建文件夹,进行挂载。
综上,如果容器挂载路径冒号:
前,不是完整的宿主机路径,那么会使用该名称作为卷名称,并在/var/lib/docker/volumes
中创建文件夹,进行挂载;
如果只有容器挂载路径,那么Docker会自动创建一个字符串作为卷名称,并以该字符串作为名称在/var/lib/docker/volumes
中创建文件夹,进行挂载。
因此,共有三种挂载方式:
-v /宿主机路径:容器内路径
:指定路径挂载-v 卷名:容器内路径
:具名挂载-v 容器内路径
:匿名挂载
读写权限
在挂载时,还可以改变读写权限。即-v /主机路径:容器路径:ro/rw
ro
:readonly,只读,设定该权限后,挂载的容器目录文件只能在主机中修改,在容器内无法修改rw
:readwrite,可读可写
数据卷容器
数据卷容器用来实现两个或多个容器之间的数据共享。
其中,共享数据的容器为父容器,也叫数据卷容器。
通过--volumes-from
来实现。
例如多个mysql容器实现数据共享。
- 首先运行MySQL主容器,运行命令
docker run -d -p 3306:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:8.0
- 再运行一个容器,并与主容器
mysql01
进行数据共享,运行命令docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 --volumes-from mysql01 --name mysql02 mysql:8.0
主容器mysql01
匿名挂载了宿主机/var/lib/docker/volumes
下的目录,然后容器mysql02
使用--volumes-from mysqlq01
与主容器进行数据共享。
也可以接着运行容器mysql03
、mysql04
等与主容器进行数据共享。
注意:这里所说的数据共享,并不是公用一个目录,而是数据备份。也就是说,即使删掉主容器,mysql的数据依然存在,只有所有容器均被删除,数据才会被删除。
当然例子中主容器挂载到了宿主机,因此在这个例子中,即使所有容器都被删除,mysql中数据依然存在。
Docker网络
docker0
当Docker启动时,会自动在宿主机上创建一个名为docker0
的虚拟网桥,可以理解为一个软件交换机,它会在挂载到它的网络接口之间进行转发。
同时,Docker会随机分配一个本地未占有的私有网段中的一个地址给docker0
接口,并且此后启动的容器内的网口也会自动分配一个同一网段的地址。
所有容器使用本地主机docker0
接口的IP作为默认网关。
docker0
在内核层连通了物理或虚拟网卡,这样就将所有容器和主机放到同一个物理网络。
例如上图,在云服务器使用ip addr
命令,可看到网络信息。
当创建一个Docker容器时,同时会创建一对veth pair
接口(当数据包发送到一个接口时,另一个接口可收到相同的数据包)。这对接口,一端在容器内,为eth0
,另一端在本地并被挂载到docker0
网桥,以veth
开头(例如vethAQI2QT
)。通过这种方式,主机可以和容器通信,容器之间也可以通信,Docker就创建了在主机和所有容器之间的虚拟共享网络。
在服务器运行一个容器,查看网卡信息:
可以看到有一个名为eth0
的接口。
同时在服务器终端再次查看网络信息,可以看到多出一个容器接口。
与上面的网络模型图相符合。
所有容器在默认情况(即不指定网络)下,都默认使用docker0
路由,Docker会给每个容器分配一个可用的IP。
容器的访问控制,主要通过Linux系统上的iptables
防火墙进行管理和实现,iptables
是Linux默认的防火墙软件。
容器访问有三种情况:
- 容器访问外部网络
- 外部网络访问容器
- 容器之间互相访问
下面分别介绍。
容器访问外部网络
容器想要访问外部网络(是指互联网,不是本机网络),需要本地系统的转发支持。在Linux系统中,检查转发是否打开。
执行命令 sysctl net.ipv4.ip_forward
显示net.ipv4.ip_forward = 1
代表转发已开启,为0
则表示没有打开,需要手动开启。
容器到外部网络的链接,源地址都会被NAT(network address translation网络地址转换)
成本地系统的IP地址。通过iptables
的源地址伪装操作实现。
外部网络访问容器
容器允许外部访问,可以在启动容器docker run
时使用-p
或-P
参数来启用。
不管使用哪种方法,其实都是在本地的iptalbe
中的nat
表中添加相应规则。
例如运行tomcat容器并指定端口:docker run -d -p 3306:3306 --name tomcat tomcat:8
那么查看iptable
的nat
表为:
1 |
|
可以看到,添加了对应的规则。
注意:
这里的规则映射了
0.0.0.0
,意味着将接受主机来自所有接口的流量,可以通过-p IP:host_port:container_port
或-p IP::port
来指定运行访问容器的主机上的IP和接口如果希望永久绑定到某个固定的IP地址(即只允许该地址访问),可在docker配置文件
/etc/docker/daemon.json
中添加如下内容。1
2
3{
"ip": "0.0.0.0"
}
容器间互相访问
容器之间互相访问,需要两方面支持。
- 容器的网络拓扑是否已经互联。默认情况下,所有容器都会被连接到
docker0
网桥 - 本地系统的防火墙软件
iptables
是否允许通过
访问所有端口
当启动Docker服务时,默认会添加一条转发策略到本机的iptables
的FORWORD
链上,策略可以为通过(ACCEPT
)或禁止(DROP
)。可以通过--icc=true
(默认值)或-icc=false
来决定。
可见,默认情况下,不同容器之间是允许网络互通的。
如果为了安全考虑,不允许容器间网络互通,可以在Docker配置文件中配置{"icc":"false"}
来禁止。
访问指定端口
关闭容器互通后,可以使用 --link CONTAINER_NAME
来访问容器。
例如启动了tomcat01
,想要再启动一个tomcat容器tomcat02
,希望容器tomcat02
可以连接容器tomcat01
,可以使用--link
选项。
在启动容器执行docker run
时,添加选项--link tomcat01
。
那么tomcat02
可以使用容器名称tomcat01
或容器ID直接ping
通。
但这种方式一般不会使用。
自定义网络
docker0
不支持使用容器服务名进行通信,因此在实际中很少被使用。
可以自己创建网络,这种方式可以直接通过容器名进行通信。
Docker网络模式
Docker提供了三种网络模式:桥接(bridge)模式,host模式和none模式。
桥接(bridge)模式:是Docker的默认网络模式。将主机网卡和虚拟机的虚拟网卡通过虚拟网桥进行通信,类似于 把物理主机虚拟为一个交换机,所有桥接设置的虚拟机连接到这个交换机的一个借口,物理主机也同样在该交换机中。
虚拟主机可以和本地主机互相访问,虚拟机也可以访问互联网。虚拟机相当于互联网中的一台主机。
Docker就是使用
docker0
作为虚拟网卡进行通信。host模式:Docker使用Linux的Namespaces技术进行资源隔离,其中Network Namespace提供了一份独立的网络环境,包括网卡、路由等。
host模式下,容器不会获得独立的Network Namespace,而是和宿主机共用,容器不会虚拟出自己的网卡、IP等,而是使用宿主机的IP和端口。容器的网络配置和宿主机完全一样。
none模式:Docker容器拥有自己的Network Namespace,但是并不会为容器进行任何网络配置。即这个容器没有网卡、IP、路由等信息。需要自己手动添加配置。因此这种模式下, 容器只有环回网络,没有其他网卡。无法连接互联网。
创建网络
使用docker network create
命令来创建自定义网络。
有其中三项需要设置:
--driver
:指定网络模式,有桥接、none、host三种,不指定默认为桥接模式--subnet
:设定子网掩码,即一个网段,如192.168.0.0/16
–geteway
:指定网关地址,一般是指定的子网范围的第一个地址,如网段为192.168.0.0/16
,那么可将网关设为192.168.0.1
网络创建成功。
使用docker network inspect
查看详细信息。
配置正确。
在运行容器时使用–net 网络名
就可让该容器加入到指定网络中。
例如
分别创建tomcat容器,名为tomcat01
和tomcat02
,并加入mynet
网络。
docker run -d -P --name tomcat01 --net mynet tomcat:8
docker run -d -P --name tomcat02 --net mynet tomcat:8
再次使用docker network inspect
查看信息。
已经给创建的两个容器分配了IP地址。
在myney
网络,两个容器可以直接使用容器名称ping
通,即主机名也是容器名。
连接不同网络
使用docker network connect
将一个网络的容器连接到另一个网络中。
注意:只能是容器和另一个网络连接,不同网络直接无法连接。
格式为docker network connect 网络名 容器名
例如将tomcat01
容器连接到docker01
网络。
命令为:docker network connect bridge tomcat01
使用inspect
查看详细信息。
可以看到,tomcat01
容器被添加到了docker0
网络中。
因此容器和另一网络连接的方式就是将给该容器分配一个该网络对应的随机IP地址。
即一个容器两个地址。
此时,tomcat01
就可以与docker01
中的其他容器连通。