Docker 归根到底是一种容器虚拟化技术(操作系统虚拟化),其依赖的核心技术主要包括 Linux 操作系统的命名空间(Namespaces),控制组(Control Groups),联合文件系统(Union File Systems)和 Linux 虚拟网络支持。
docker 对于系统内核有严格要求,因此建议使用尽量新的操作系统
建议使用 CentOS7 以后的版本
可以选择使用 rpm 包进行安装
# 请自行选择最新docker版本
$ wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.12.0.ce-1.el7.centos.x86_64.rpm
$ yum install docker-ce-17.06.2.ce-1.el7.centos.x86_64.rpm
也可以设置 yum 源,并通过
yum install
进行安装
$ cat /etc/yum.repos.d/docker.repo
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
$ yum install docker-engine -y
安装之后,使用 docker version
进行验证和版本确认。
同 CentOS 相同,Ubuntu 也尽量使用新的版本。然后通过设置 apt 源并在线安装
$ sudo apt-get update
$ sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt-get update
https://download.docker.com/linux/static/stable/x86_64/
。/usr/bin
目录下
$ tar zxf docker-18.06.1-ce.tgz
$ mv docker/* /usr/bin/
$ rm -rf docker*.tgz
$ cat /etc/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
ExecReload=/bin/kill -s HUP $MAINPID
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
#TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
# restart the docker process if it exits prematurely
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
[Install]
WantedBy=multi-user.target
$ chmod +x /etc/systemd/system/docker.service
$ systemctl daemon-reload //重载systemd下 xxx.service文件
$ systemctl start docker //启动Docker
$ systemctl enable docker.service //设置开机自启
docker version
验证其版本及安装情况首先确认安装
yum-plugin-downloadonly
yum install yum-plugin-downloadonly
然后选择某个文件,并将离线文件下载到其中
# 选择/opt文件夹
yum install --downloadonly --downloaddir=/opt/docker docker
当需要安装时,执行 rpm 命令
rpm -Uvh /opt/docker/*.rpm --nodeps --force
Docker 采用了标准的 C/S 架构,包括了客户端和服务端两大部分。
客户端和服务端既可以运行在一个机器上,也可以通过 socket 或 RESTFul API 来进行通信
Docker daemon 一般在宿主主机后台运行,作为服务端接收来自客户的请求。Docker 服务端默认监听本地的 unix:///var/run/docker.sock
套接字,只允许本地 root 用户访问。可以通过 -H
选项来修改监听的方式。
Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现与 Docker Daemon 的交互
命名空间 Namespace 是 Linux 内核针对实现容器虚拟化引入的一个强大特性。
每个容器都可以拥有自己独特的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。
在操作系统中,包括内核、文件系统、网络、PID、UID、IPC、内存、硬盘、CPU 等资源,所有的资源都是应用进程直接共享的。要实现虚拟化,除了要实现对内存、CPU、网络 IO、硬盘 ID、存储空间等的限制外。还要实现文件系统、网络、PID、UID、IPC 等的隔离。
Linux 通过命名空间管理进程号 PID,对于同一进程(同一个 task_struct),在不同的命名空间中,看到的进程号不相同,每个进程命名空间有一套自己的进程号管理方法。进程命名空间是一个父子关系的结构,子空间中的进程对于父空间是可见的。新 fork 出的进程在父命名空间和子命名空间将分别有一个进程号来对应。(容器启动的初始进程,其父进程正是 Docker 的主进程)。
通过 PID 命名空间,每个名字空间中的进程就可以互相隔离
网络命名空间为进程提供了一个完全独立的网络协议栈的视图(包括网络设备接口、IPv4 和 IPv6 协议栈、IP 路由表、防火墙规则、sockets 等等)。这样每个容器的网络就可以隔离开来。Docker 通过虚拟网络设备的方式,将不同命名空间的网络设备连接到一起。在默认情况下,容器的虚拟网卡将同本地主机上的 docker0 网桥连接在一起。
容器中的进程交互(IPC)还是采用了 Linux 常见的进程间交互方法,包括信号量、消息队列和共享内存等。PID 的命名空间和 IPC 命名空间可以组合起来一起使用,同一个 IPC 命名空间内的进程彼此可见和交互,但不同命名空间的进程无法交互
类似 chroot
,将一个进程放到一个特定的目录执行。挂载命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间看到的文件目录彼此被隔离
UTS 命名空间允许每个容器拥有独立的主机名和域名,从而可以虚拟出一个独立主机名和网络空间的环境,就跟网络上一台独立的主机一样。
每个容器都可以由不同的用户和组 id,即容器内使用特定的内部用户执行程序,而非本地系统上存在的用户。
每个容器内部都可以有 root 账号,跟宿主主机不在一个命名空间中。
控制组(CGroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,Docker 才能避免多个容器同时运行时的系统资源竞争。
CGroups 可以提供对容器的内存、CPU、磁盘 IO 等资源进行限制和计费管理。其功能主要有如下:
可以在 /sys/fs/cgroup/memory/docker/
目录下看到对 Docker 组应用的各种限制项。
联合文件系统(UnionFS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。
联合文件系统是实现 Docker 镜像的技术基础。镜像可以通过分层来进行继承。
Docker 中使用 AUFS
。当 Docker 利用镜像启动一个容器时,将利用镜像分配文件系统并且挂载一个新的可读写的层给容器,容器会在这个文件系统中创建,并且这个可读写的层被添加到镜像中。
Docker 的网络实现就是利用了 Linux 上的网络命名空间和虚拟网络设备(veth pair)
要实现网络通信,机器至少需要一个网络接口与外界通信,并可以收发数据包;此外,如果不同子网之间要进行通信,需要额外的路由机制。
Docker 中的网络接口默认都是虚拟的接口(转发效率极高,因为内核中进行数据复制来实现虚拟接口之间的数据转发),对于本地系统和容器内系统来看,虚拟接口跟一个正常的以太网卡相比没有区别,只是速度快得多。
Docker 容器网络利用了 Linux 虚拟网络技术,它在本地主机和容器内分别创建了一个虚拟接口,并让它们彼此相连通
Docker 创建一个容器的时候,会执行如下操作:
veth
开头的唯一 IDeth0
,这个接口仅在容器的命名空间中可见eth0
,并配置默认路由网关为 docker0 网卡的内部接口 dockr0 的 IP 地址。这样连接后,容器就可以使用它所能看到的 eth0 虚拟网卡来连接其他容器和访问外部网络。
此外,容器的运行可以通过 --net
来指定容器的网络配置
容器是 Docker 的另一个核心概念,它是镜像的一个运行实例,所不同的是,它带有额外的可写文件层。
如果认为虚拟机是模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用,那么 Docker 容器就是独立运行的一个或一组应用,以及它们的必须运行环境
docker create
创建一个新的容器docker create ubuntu:latest
使用 docker start
创建的容器会出与停止状态,可以使用 docker start
来启动它。
docker run
, 等价于先执行 docker create
,再执行 docker start
命令docker run ubuntu /bin/echo "helloworld"
关于守护态与交互模式: 当使用
docker run -d
时,Docker 会将容器以守护态的形式运行,容器会在后台持续运行命令直到进程结束;当使用docker run -t -i
时,容器会分配一个伪终端(-t),并绑定到容器的标准输入上(-i),这样用户可以直接与容器进行命令交互
可以使用 docker stop
来终止一个运行中的容器,它会首先向容器发送 SIGTERM信号
,过一段时间之后(默认为 10s),再发送 SIGKILL信号
终止容器。当 Docker 容器应用终结时,容器也自动终止。
docker stop mycontainer
可以使用 docker ps -a
来查看到处于终止状态的容器 ID 信息。
处于终止状态的容器,可以通过 docker start
命令来重新启动。
此外,docker restart
命令会将一个运行态的容器终止,然后再启动它
在启动容器时,如果使用 -d
参数,容器启动后会进入后台。要进入容器,有以下方法
比如要进入 mycontainer
容器
docker attach myconainer
但是使用 attach 命令并不方便,当多个窗口同时 attach 到同一个容器,所有窗口都会同步显示,如果某个窗口阻塞,则所有窗口无法执行操作。
使用 exec
来执行命令,可以通过 shell 来访问容器
docker exec -ti mycontainer /bin/bash
使用 docker rm
可以删除处于终止状态的容器
docker rm mycontainer
用户在使用 Docker 的过程中,往往需要能查看容器内应用产生的数据,或者需要把容器内的数据进行备份,甚至多个容器之间进行数据的共享,这必然涉及容器的数据管理操作。
容器中管理数据的方式主要有两种:
数据卷是一个可供容器使用的特殊目录,它绕过文件系统,提供:
当使用 docker run
的时候,使用 -v
可以在容器内创建一个数据卷,多次使用 -v
可以创建多个数据卷
docker run -d -v /webapp ubuntu
其实,以上只是容器在默认目录创建了一个空白卷,然后将空白卷映射到容器内的路径。如果要指定挂载本地一个已有的目录,
docker run -d -v /webapp:/root/webapp ubuntu
以上命令会将本地 /root/webapp
目录挂载到容器的 /webapp
目录中。
Docker 挂载数据卷的默认权限是读写(rw),用户也可以通过只读(ro)指定为只读
docker run -d -v /webapp:/root/webapp:ro ubuntu
如果直接挂载一个文件到容器,对文件进行编辑时,可能会造成文件 inode 的改变,所以推荐直接挂载文件所在的目录
如果用户需要在容器直接共享一些持续更新的数据,可以使用数据卷容器。数据卷容器实际就是一个普通的容器。
docker run -d -v /mydata --name mydata ubuntu
--volumes-from
来挂载 mydata
容器中的数据卷了docker run -ti --volumes-from mydata --name db1 ubuntu
docker run -ti --volumes-from mydata --name db2 ubuntu
db1
的容器数据修改时,db2
也可见其修改,同理,db2
的修改对 db1
也是可见的docker rm `docker ps -a | grep Exited | awk '{print $1}'`
docker rmi -f `docker images | grep '<none>' | awk '{print $3}'`
docker volume rm $(docker volume ls -qf dangling=true)
docker image prune -a