Docker 最初是dotCloud公司创始人Solomon Hykes在法国期间发起的一个公司内部项目,它是基于dotCloud公司多年云服务技术的一次革新,并与2013年3月以Apache 2.0授权协议开源),主要项目代码在GitHub上进行维护。Docker项目后来还加入了Linux基金会,并成立推动开放容器联盟。
Docker 自开源后受到广泛的关注和讨论,至今其GitHub项目已经超过3万6千个星际和一万多个fork。甚至由于Docker项目的火爆,在2013年底,dotCloud公司决定改名为Docker。Docker最初在Ubuntu12.04上开发实现的;Redhat则从RHEL6.5开始对Docker进行支持;Google也在其PaaS产品中广泛应用Docker。Docker使用Google公司推出的Go语言进行开发实现,基于Linux内核的cgroup,namespace,以及AUFS类的Union FS等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于LXC,从0.7以后开始去除LXC,转而使用自行开发的libcontainer,从1.11开始,则进一步演进为使用runC和containerd。
Docker 容器是资源分割和调度的基本单位,封装整个服务的运行时环境,用于构建、发布和运行发布式应用的一个框架。它是一个跨平台,可移植并且简单易用的容器解决方案。
Docker 容器可以快速自动化地部署应用,并通过操作系统内核技术(namespaces、cgroups等)为容器提供资源隔离与安全保障。Docker 作为轻量级的虚拟化方式,实现了PaaS平台高效部署、运行和维护。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等待,极大的简化了容器的创建和维护。使得Docker技术比虚拟机技术更为轻便,快捷。
传统虚拟化结构对比Docker结构
由于容器不需要进行硬件虚拟化以及运行完整操作系统等额外开销,Docker对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
传统的虚拟机技术启动应用服务往往需要数分钟,而Docker容器应用,由于直接运行于宿主机,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些bug并未在开发过程中被发现。而Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现“这段代码在我机器上没问题啊”这类问题。
对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用Docker可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过Dockerfile来进行镜像构建,并结合持续集成(Continuous lntegration)系统持续进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合持续部署(Continuous Delivery/Deployment)系统进行自动部署。而使用Dockerfile使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
由于Docker确保了执行环境的一致性,使得应用的迁移更加容易。Docker可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
Docker使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker团队同各个开源项目团队一起维护了一大批高质量的官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大降低了应用服务的镜像制作成本。
Docker对比传统虚拟机
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
磁盘使用 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
Docker 是一个客户/服务器(Client/Server,CS)架构(见上图)。Docker客户端是远程控制器,可通过TCP REST向Docker Host 发送请求,包括创建容器、运行容器、保存容器、删除容器等请求。Docker服务端的Daemon 对客户端的请求进行相应的管理,随后通过 driver转发至容器中的libcontainer执行环境。libcontainer提供与不同Linux内核隔离的接口,类似命名空间及控制组。
这种架构允许多个容器在共享同一个Linux内核的情况下完全隔离的运行。
Docker 镜像(Images) | Docker 镜像是用于创建 Docker 容器的模板。 |
Docker 容器(Container) | 容器是独立运行的一个或一组应用。 |
Docker 客户端(Client) | Docker 客户端通过命令行或其他工具使用 Docker API |
Docker 主机(Host) | 一个物理或者虚拟的机器用来执行 Docker 守护进程和容器。 |
Docker 仓库(Registry) | Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com)提供了庞大的镜像集合供使用。 |
Docker Machine | Docker Machine 是一个简化Docker安装的命令行工具,通过一个简单的命令即可在响应的平台上安装Docker,比如VirtualBox、Digital Ocean、Microsoft Azure。 |
这是Docker公司宣传的 Docker 的主要使用场景。Docker 能将运行环境和配置放在代码中然后部署,同一个 Docker 的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。
代码从开发者的机器到最终生产环境上的部署,需要经过很多的中间环境。而每一个中间环境都有微小的差别,Docker 给应用提供了一个从开发到上线均一致的环境,让代码的流水线变得简单不少。
Docker 能提升开发者的开发效率。不同的开发环境中,Docker 都可以把两件事做好,一是可以在开发环境、生产环境之间直接迁移,二是可以让我们快速搭建开发环境。开发环境的机器通常内存比较小,之前使用虚拟机的时候,我们经常需要为开发环境的机器加内存,而使用Docker可以轻易的让几十个服务在Docker中跑起来。
有很多原因会让我们选择在一个机器上运行不同的应用,Docker 非常适合在较低的成本下实现多种应用的隔离。
Docker 隔离应用的能力使得Docker可以整合多个服务器以降低成本。由于没有操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker 可以比虚拟机提供更好的服务器整合解决方案。通常数据中心的服务器资源利用率只有30%,通过使用Docker 并进行有效的资源分配可以大幅度提高服务器资源的利用率。
Docker 提供了很多的工具,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试Bug。
另外一个Docker 的使用场景是在多租户的应用中,它可以避免关键应用的重写。我们一个特别的关于这个场景的例子是为物联网的应用开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂,很难处理,重新规划这样一个应用不但消耗时间,也浪费金钱。
使用Docker,可以为每一个租户的应用层的多个实例创建隔离的环境,这不仅简单而且成本低廉,当然这一切得益于Docker环境的启动速度和高效的diff命令。
在虚拟机之前,购入部署新的硬件资源需要消耗几天的时间。虚拟化技术(Virtualization)将这个时间缩短到了分钟级别。而Docker 通过为进程仅仅创建一个容器而无需启动一个操作系统,再次将这个过程缩短到了秒级。这正式Google和Facebook 都看重的特性。我们可以创建销毁Docker 容器而无需担心重新启动带来的开销。
Docker 包括三个基本概念
Docker 镜像类似于虚拟机镜像,可以将它理解为一个只读的模板。例如,一个镜像可以包含一个基本的操作系统环境,里面仅安装了 Apache 应用程序(或用户需要的其他软件)。可以把它称为一个 Apache镜像。
镜像是创建Docker 容器的基础。通过版本管理和增量的文件系统,Docker提供了一套十分简单的机制来创建和更新现有的镜像,用户甚至可以从网上下载一个已经做好的应用镜像,并直接使用。
Docker 容器类似于一个轻量级的沙箱,Docker 利用容器来运行和隔离应用。容器是从镜像创建的应用运行实例。可以将其启动、开始、停止、删除,而这些容器都是彼此相互隔离的、互不相见的。
可以把容器看做是一个简易版的Linux系统环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子
Docker 仓库类似于代码仓库,它是Docker集中存放镜像文件的场所。
仓库注册服务器是存放仓库的地方,其上往往存放着多个地方。每个仓库集中存放某一类镜像,往往包括多个镜像文件,通过不同的标签(tag)来进行区分。一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应改软件的各个版本。我们可以通过 <仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。以Nginx镜像为例,nginx 是仓库的名字,其内包含有不同的版本标签,如,1.12.2,我们可以通过 nginx:1.12.2来指定需要哪个版本的镜像。如果忽略了标签,比如nginx ,那将视为 nginx:latest。
Docker 仓库可以分为公开仓库(Public)和私有仓库(Private)两种形式。目前最大的公开仓库是Docker Hub, 存放了数量庞大的镜像供用户下载。国内还有(时速云、阿里云)等公开仓库。
当用户不希望公开自己的镜像文件,Docker 也支持用户在本地网络内创建一个只能自己访问的私有仓库。当用户创建了自己的镜像之后就可以使用push命令将其上传到指定的公有或者私有仓库。这样下次在另外一台机器上使用该镜像时,只需要将其从仓库上pull 下来就可以了。
系统要求
Docker 对CentOS的版本:
CentOS 7 (64-bit)
CentOS 6.5 (64-bit)或者更高的版本
前提条件:
Docker 运行在CentOS 7 上,要求系统为64位、系统内核为3.10以上
Docker 运行在CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位、系统内核版本为2.6.32-431 或者更高版本。
[root@server ~]# cat /etc/redhat-release # 查看系统版本号
CentOS Linux release 7.3.1611 (Core)
[root@server ~]# uname -r # 查看内核
3.10.0-514.el7.x86_64
ps:使用yum直接安装的是docker version 1.13.1
[root@server ~]# yum -y install docker # 安装Docker(CentOS7系统CentOS-Extras库中已带Docker)
[root@server ~]# systemctl start docker # 启动Docker
[root@server ~]# systemctl enable docker # 加入开机自启动
yum安装高版本
# 安装
yum -y install yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum -y install docker-ce docker-ce-cli containerd.io
# 配置
mkdir /etc/docker
vim /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"graph": "/data/docker"
}
# 启动
systemctl enable docker --now
docker info
centos6.x安装:
[root@linux ~]# cat /etc/redhat-release
CentOS release 6.9 (Final)
[root@linux ~]# uname -r
2.6.32-696.el6.x86_64
[root@linux ~]# wget https://yum.dockerproject.org/repo/main/centos/6/Packages/docker-engine-1.7.0-1.el6.x86_64.rpm #下载软件包
[root@linux ~]# yum -y install docker-engine-1.7.0-1.el6.x86_64.rpm #安装
# curl -L "https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
[root@docker ~]# docker --help
Usage:
docker [OPTIONS] COMMAND [arg...]
docker daemon [ --help | ... ]
docker [ --help | -v | --version ]
A
self-sufficient runtime for containers.
Options:
--config string Location of client config files (default "/root/.docker") #客户端配置文件的位置
-D, --debug=false Enable debug mode #启用Debug调试模式
-H, --host=[] Daemon socket(s) to connect to #守护进程的套接字(Socket)连接
-h, --help=false Print usage #打印使用
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info") #设置日志级别
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem") #信任证书签名CA
--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem") #TLS证书文件路径
--tlskey string Path to TLS key file (default "/root/.docker/key.pem") #TLS密钥文件路径
--tlsverify Use TLS and verify the remote #使用TLS验证远程
-v, --version Print version information and quit #打印版本信息并退出
Commands:
attach Attach to a running container #当前shell下attach连接指定运行镜像
build Build an image from a Dockerfile #通过Dockerfile定制镜像
commit Create a new image from a container's changes #提交当前容器为新的镜像
cp Copy files/folders from a container to a HOSTDIR or to STDOUT #从容器中拷贝指定文件或者目录到宿主机中
create Create a new container #创建一个新的容器,同run 但不启动容器
diff Inspect changes 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's 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 a container or image #查看容器详细信息
kill Kill a running container #kill指定docker容器
load Load an image from a tar archive or STDIN #从一个tar包中加载一个镜像(对应save)
login Register or 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 a container #暂停容器
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 #重命名容器
restart Restart a running container #重启运行的容器
rm Remove one or more containers #移除一个或者多个容器
rmi Remove one or more images #移除一个或多个镜像(无容器使用该镜像才可以删除,否则需要删除相关容器才可以继续或者-f强制删除)
run Run a command in a new container #创建一个新的容器并运行一个命令
save Save an image(s) to a tar archive #保存一个镜像为一个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 a running container #停止容器
tag Tag an image into a repository #给源中镜像打标签
top Display the running processes of a container #查看容器中运行的进程信息
unpause Unpause all processes within a container #取消暂停容器
version Show the Docker version information #查看容器版本号
wait Block until a container stops, then print its exit code #截取容器停止时的退出状态值
Run 'docker COMMAND --help' for more information on a command. #运行docker命令在帮助可以获取更多信息
搜索镜像 docker search
[root@server ~]# docker search centos #搜索所有的centos的docker镜像
NAME(名称) DESCRIPTION(描述) STARS(下载次数) OFFICIAL(官方) AUTOMATED(自动化)
centos The official build of CentOS. 5091 [OK]
ansible/centos7-ansible Ansible on Centos7 119 [OK]
docker search 其它用法
docker search 参数说明:
--automated=true I false:仅显示自动创建的镜像,默认为否;
--no-trunc=true | false:输出信息不截断显示,默认为否;
-s,--stars=X:指定仅显示评价为指定星级以上的镜像,默认为 0,即输出所有镜像。
例如,搜索所有自动创建的评价为 3+的带 nginx 关键字的镜像,如下所示:
[root@server ~]# docker search --automated -s 3 nginx
镜像是运行容器的前提,官方的Docker Hub 网站已经提供了数十万个镜像供我们下载。可以使用Docker pull 从镜像仓库拉取镜像
docker pull
docker pull [选项] [Docker Registry地址]<仓库名>:<标签>
[root@server ~]# docker pull centos #获取centos镜像
Using default tag: latest
latest: Pulling from library/centos
a02a4930cb5d: Pull complete
Digest: sha256:184e5f35598e333bfa7de10d8fb1cebb5ee4df5bc0f970bf2b1e7c7345136426
Status: Downloaded newer image for centos:latest
# 新版本获取镜像
[root@server ~]# docker image pull nginx:1.11
[root@server ~]# docker images #查看docker镜像
REPOSITORY(来自哪个仓库) TAG(标签) IMAGE ID(唯一ID) CREATED(创建时间) SIZE(大小)
nginx latest 7042885a156a 7 days ago 109MB
centos latest 1e1148e4cc2c 4 weeks ago 202MB
# 新版本查看镜像
[root@server ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 7042885a156a 7 days ago 109MB
centos latest 1e1148e4cc2c 4 weeks ago 202MB
列表包括了仓库名、标签、镜像ID、创建时间以及所占用的空间。
镜像ID则是镜像的唯一标识,一个镜像可以对应多个标签。
docker rmi [选项] <镜像1> [<镜像2> ...]
使用docker rmi 删除本地镜像,后面可以跟镜像ID或者标签。(删除镜像之前先用docker rm 删除依赖于这个镜像的所有容器)。注意docker rm 命令是移除容器。
[root@server ~]# docker rmi imageID/imageName/ # 删除docker镜像
如果要导出镜像到本地,提供给另外一台机使用,可以使用docker save 或者docker image save 命令。
[root@server ~]# docker save centos > /tmp/docker_centos.tar #导出docker镜像到本地
[root@server ~]# ll /tmp/docker_centos.tar
-rw-r--r-- 1 root root 210186752 1月 5 16:05 /tmp/docker_centos.tar
# 新版本也可用下面这种方法
[root@server ~]# docker image save nginx > /tmp/docker_nginx.tar #导出docker镜像到本地
[root@server ~]# ll /tmp/docker_nginx.tar
-rw-r--r-- 1 root root 190512640 1月 5 16:06 /tmp/docker_nginx.tar
从本地文件中导入docker镜像库
[root@server ~]# docker load < /tmp/docker_centos.tar #导入本地镜像到docker镜像库
[root@server ~]# docker image load < /tmp/docker_nginx.tar #新版本也可以用这种方法导入
[root@server ~]# docker image ls #查看导入的情况
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 1e1148e4cc2c 4 weeks ago 202MB
nginx 1.11 5766334bdaa0 21 months ago 183MB
利用docker tag可以给镜像添加一个新的标签,docker tag 原标签 新标签
[root@server ~]# docker image ls #打标签前查看
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 1e1148e4cc2c 4 weeks ago 202MB
nginx 1.11 5766334bdaa0 21 months ago 183MB
[root@server ~]# docker tag centos centos:7.2 #给centos打一个新的标签
[root@server ~]# docker image tag nginx:1.11 nginx:1.12 #给nginx打一个新的标签
[root@server ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos 7.2 1e1148e4cc2c 4 weeks ago 202MB
centos latest 1e1148e4cc2c 4 weeks ago 202MB
nginx 1.11 5766334bdaa0 21 months ago 183MB
nginx 1.12 5766334bdaa0 21 months ago 183MB
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一种是将在终止状态(stopped)的容器重新启动。
因为Docker的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。所以主要命令还是docker run
新建容器 docker create
[root@server ~]# docker create -it centos /bin/bash #创建一个容器
a661c9dd447066f326e13389b4e11222f1430e1cbd56ef4c38416fdd51b1394e
[root@server ~]# docker ps -a #查看容器状态,可以看到通过create创建的容器处于停止状态
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a661c9dd4470 centos "/bin/bash" 39 seconds ago Created angry_wu
[root@server ~]# docker start a661c9dd4470 #根据ID启动上面创建的容器
a661c9dd4470
[root@server ~]# docker ps -a #查看容器状态,处于运行状态
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a661c9dd4470 centos "/bin/bash" About a minute ago Up 3 seconds angry_wu
[root@server ~]# docker attach a661c9dd4470 #根据ID进入到启动的容器中
[root@a661c9dd4470 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@a661c9dd4470 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:35 pts/0 00:00:00 /bin/bash
root 15 1 0 08:38 pts/0 00:00:00 ps -ef
[root@a661c9dd4470 /]# exit #退出容器
exit
[root@server ~]#
新建容器并启动 docker run
# docker run 创建容器常用选项
-i, --interactive 交互式
-t, --tty 分配一个伪终端
-d, --detach 运行容器到后台
-a, --attach list 附加到运行的容器
--dns list 设置DNS服务器
-e, --env list 设置环境变量
--env-file list 从文件读取环境变量
-p, --publish list 发布容器端口到主机
-P, --publish-all 发布容器所有EXPOSE的端口到宿主机随机端口
-h, --hostname string 设置容器主机名
--ip string 指定容器IP,只能用于自定义网络
--link list 添加连接到另一个容器
--network 连接容器到一个网络
--mount mount 挂载宿主机分区到容器
-v, --volume list 挂载宿主机目录到容器
--restart string,容器退出时重启策略,默认no [always|on-failure]
--add-host list 添加其他主机到容器中/etc/hosts
-m,--memory 容器可以使用的最大内存量
--memory-swap 允许交换到磁盘的内存量--memory-swappiness=<0-100> 容器使用SWAP分区交换的百分比(0-100,默认为-1)
--memory-reservation 内存软限制,Docker检测主机容器争用或内存不足时所激活的软 限制,使用此选项,值必须设置低于—memory,以使其优先
--oom-kill-disable当宿主机内存不足时,内核会杀死容器中的进程。建议设置了memory选项再禁用OOM。如果没有设置,主机可能会耗尽内存
--cpus 限制容器可以使用多少可用的CPU资源
--cpuset-cpus 限制容器可以使用特定的CPU
--cpu-shares 此值设置为大于或小于默认1024值,以增加或减少容器的权重, 并使其可以访问主机CPU周期的更大或更小比例
例如,下面的命令输出一个“Hello World”,之后容器自动终止。
[root@server ~]# docker run centos /bin/echo "Hello World" #这跟在本地直接运行/bin/echo "Hello World" 几乎感觉不出任何差别
Hello World
启动一个bash终端,允许用户进行交互
[root@server ~]# docker run --name mydocker -t -i centos /bin/bash
[root@8bad1a6db7b2 /]# pwd
/
[root@8bad1a6db7b2 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
--name: 给容器定义一个名称
-t:让Docker分配一个伪终端并绑定到容器的标准输入上
-i:让容器的标准输入保持打开
/bin/bash:执行一个命令
当利用docker run 来创建容器时,Docker在后台运行的标准操作包括:
启动已终止的容器 docker start
容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其他的资源。可以在伪终端利用 ps 或 top 来查看进程信息
docker ps -a 可以查看所有容器的情况,docker ps 查看已启动容器的情况
[root@server ~]# docker ps -a #查看所有的容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" 8 minutes ago Exited (0) About a minute ago mydocker
4d18a48ee315 centos "/bin/bash" 9 minutes ago Exited (0) 9 minutes ago amazing_herschel
b3918347393a centos "/bin/echo 'Hello Wo…" 12 minutes ago Exited (0) 12 minutes ago competent_lovelace
a661c9dd4470 centos "/bin/bash" 24 minutes ago Exited (0) 20 minutes ago angry_wu
[root@server ~]# docker start 8bad1a6db7b2 #启动一个终止的容器
8bad1a6db7b2
[root@server ~]# docker ps #查看已经启动的容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" 8 minutes ago Up 4 seconds mydocker
守护进程运行(后台运行)
更多的时候,需要让Docker容器在后台运行,而不是直接把执行命令的结果输出在当前宿主机下。此时可以加 -d 参数来实现。
例如下面的命令会在后台运行容器:
[root@server ~]# docker run -d centos /bin/bash -c "while true; do echo hello world; sleep 1; done"
0ca1770a1fd0b21ae05f2f6bd9befbb9eef55576ab0a0b51fc6dc7e427128174
[root@server ~]# docker ps #查看正在运行的docker容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ca1770a1fd0 centos "/bin/bash -c 'while…" 27 seconds ago Up 25 seconds ecstatic_wu
8bad1a6db7b2 centos "/bin/bash" 18 minutes ago Up 10 minutes mydocker
[root@server ~]# docker logs 0ca1770a1fd0 #获取容器输出信息,通过docker logs命令
hello world
hello world
hello world
可以使用 docker stop 来终止一个正在运行的容器。
此外,当Docker容器中指定的应用终结时,容器也会自动终结,例如启动一个终端容器,用户通过exit命令或者Ctrl+d 来退出终端时,所创建的容器立刻终止。
终止状态的容器可以用 docker ps -a 命令看到,也可以通过 docker start 命令来重新启动
[root@server ~]# docker ps -a #查看所有的容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ca1770a1fd0 centos "/bin/bash -c 'while…" 8 minutes ago Up 7 minutes ecstatic_wu
8bad1a6db7b2 centos "/bin/bash" 26 minutes ago Up 17 minutes mydocker
[root@server ~]# docker stop 8bad1a6db7b2 #停止容器
8bad1a6db7b2
[root@server ~]# docker stop 0ca1770a1fd0 #停止容器
0ca1770a1fd0
[root@server ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ca1770a1fd0 centos "/bin/bash -c 'while…" 8 minutes ago Exited (137) 3 seconds ago ecstatic_wu
8bad1a6db7b2 centos "/bin/bash" 27 minutes ago Exited (137) 26 seconds ago mydocker
在使用 -d 参数时,容器启动后会进入后台,某些时候需要进入容器进行操作,有很多种房,包括 docker attach 命令 或者 nsenter 工具 或者 docker exec命令等。
docker attach 是Docker自带的命令,示例:
[root@server ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" 32 minutes ago Exited (137) 5 minutes ago mydocker
[root@server ~]# docker start mydocker #启动已经停止的容器,start可以跟容器ID,也可以是名字
mydocker
[root@server ~]# docker attach mydocker #通过docker attach进入容器
[root@8bad1a6db7b2 /]#
但是使用attach命令有时候并不方便,当多个窗口同时 attach 到同一个容器的时候,所有窗口都会同步显示,当某个窗口因命令阻塞,其他窗口也无法执行操作了。且通过exit退出后容器就自动终止了。
nsenter 启动一个新的shell进程(默认是/bin/bash),同时会把这个新进程切换到和目标(target)进程相同的命名空间,这样就相当于进入了容器内部。nsenter 要正常工作需要root 权限。
[root@server ~]# yum install -y util-linux #centos自带yum源有,直接安装即可
[root@server ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" 43 minutes ago Exited (0) 3 minutes ago mydocker
[root@server ~]# docker start mydocker #启动已经关闭的容器
mydocker
[root@server ~]# docker inspect -f "{{.State.Pid}}" 8bad1a6db7b2 #找到容器的第一个进程ID,说明:后面跟容器ID,容器名字都可以
21176
[root@server ~]# nsenter -t 21176 -m -u -i -n -p #通过这个PID连接到容器
[root@8bad1a6db7b2 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@8bad1a6db7b2 /]# pwd
/
[root@8bad1a6db7b2 /]# exit
logout
[root@server ~]# docker ps #通过nsenter进入后再退出,我们可以看到容器还是处于后台运行中
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" About an hour ago Up 4 minutes mydocker
由于上面两条命令都太长,不方便,所以我们编写一个脚本进入,只需在脚本后面跟容器ID或者容器名字即可
[root@server ~]# cat in_docker.sh
#!/bin/bash
# Use nsenter to access docker
docker_in() {
NAME_ID=$1
PID=$(docker inspect -f "{{.State.Pid}}" $NAME_ID)
nsenter -t $PID -m -u -i -n -p
}
docker_in $1
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" About an hour ago Up 10 minutes mydocker
[root@server ~]# ./in_docker.sh mydocker #执行脚本加上容器ID快速进入
[root@8bad1a6db7b2 /]#
exec 可以在容器内直接执行任意命令,此处我们也可以使用它进入到容器里面
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" About an hour ago Up 13 minutes mydocker
[root@server ~]# docker exec -it mydocker /bin/bash
[root@8bad1a6db7b2 /]# exit
exit
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bad1a6db7b2 centos "/bin/bash" About an hour ago Up 14 minutes mydocker
某些时候,需要将容器从一个系统迁移到另外一个系统,此时可以使用Docker的导入和导出功能。这也是Docker 自身提供的一个重要特性
如果要导出本地某个容器,可以使用 docker export 命令,导出容器是指导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态。export后面跟需要导出的 容器ID 或者 容器名字 都可以
[root@server ~]# docker export mydocker > centos.tar #导出一个容器,也可以使用docker export -o centos.tar mydocker。 -o 指定导出的名字
[root@server ~]# ll centos.tar
-rw-r--r-- 1 root root 209473024 1月 5 17:56 centos.tar
导出的文件又可以使用 docker import 命令导入变成镜像,例如
[root@server ~]# cat centos.tar | docker import - test/centos:7.3
sha256:a8375a86d721a718d70dc99a49005d70ce6a7b65423c1bbd1ce34b23ec787aa9
[root@server ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/centos 7.3 a8375a86d721 7 seconds ago 202MB
可以使用 docker rm 来删除一个处于终止状态的容器。例如
[root@server ~]# docker ps -a #查看所有容器当前状态
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ca1770a1fd0 centos "/bin/bash -c 'while…" About an hour ago Exited (137) About an hour ago ecstatic_wu
a661c9dd4470 centos "/bin/bash" 2 hours ago Exited (0) 2 hours ago angry_wu
[root@server ~]# docker rm a661c9dd4470 #删除已经停止的容器
a661c9dd4470
如果要删除一个运行中的容器,可以添加 -f 参数。 Docker会发送 STGKILL信号给容器,再进行删除。
清理所有处于终止状态的的容器(慎用)
[root@server ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ca1770a1fd0 centos "/bin/bash -c 'while…" About an hour ago Exited (137) About an hour ago ecstatic_wu
4d18a48ee315 centos "/bin/bash" About an hour ago Exited (0) About an hour ago amazing_herschel
b3918347393a centos "/bin/echo 'Hello Wo…" About an hour ago Exited (0) About an hour ago competent_lovelace
[root@server ~]# docker rm $(docker ps -a -q) # 获取全部容器的ID,进行删除
0ca1770a1fd0
4d18a48ee315
b3918347393a
[root@server ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Docker 容器中管理数据主要有两种方式:
数据卷是一个可供一个或者多个容器使用的特殊目录,它绕过UFS,可以提供很多有用的特性:
管理卷 docker volume:
[root@server ~]# docker volume create myvolume #创建一个数据卷
myvolume
[root@server ~]# docker volume inspect myvolume #查看数据卷的信息
[
{
"CreatedAt": "2019-01-05T22:45:46+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/myvolume/_data", #可以看到数据卷创建在宿主机的/var/lib/docker/volume目录下
"Name": "myvolume",
"Options": {},
"Scope": "local"
}
]
[root@server ~]# docker volume ls #查看数据卷列表
DRIVER VOLUME NAME
local myvolume
用卷创建一个容器:
[root@server ~]# docker run -d -it --name myCentos --mount src=myvolume,dst=/data centos #创建一个myCentos容器,并将myvolume数据卷挂载到myCentos容器的/data目录
[root@server ~]# docker exec -it myCentos /bin/bash #进入容器
[root@8f3094a730af /]# ls /data #查看目录为空
[root@8f3094a730af /]# ls /data #当在另外一个终端创建文件后再次进行查看有文件
test.txt
[root@8f3094a730af /]# cat /data/test.txt #查看文件内容
hello world
[root@server ~]# cd /var/lib/docker/volumes/myvolume/_data #在另外一个终端进入到数据目录里面
[root@server _data]# echo "hello world" >> test.txt #在数据目录创建一个文件
src:数据卷名字
dst:需要挂载的目录路径
注意:如果没有指定卷,则自动创建。
此时开启另外一个终端再次创建一个容器,并且挂载同样的数据卷,可以发现这种方式可以在容器之间共享和重用
[root@server ~]# docker run -d -it --name myCentos01 --mount src=myvolume,dst=/data centos #新开一个终端创建新的一个容器,挂载同样的数据卷
[root@server ~]# docker exec -it myCentos01 /bin/bash
[root@45c3ca27ddad /]# ls /data/
test.txt
上面创建容器并挂载数据卷,还可以使用下面这种方法 -v 数据卷名字:挂载路径,Docker挂载数据卷默认权限是读写,也可以通过 :ro 指定为只读。
[root@server ~]# docker run -d -it --name myCentos02 -v myvolume:/test:ro centos #创建一个myCentos02的容器,并将数据卷myvolume挂载到容器的/test目录,并且为只读模式
[root@server ~]# docker exec -it myCentos02 /bin/bash
[root@784f03496b07 /]# ls /test
test.txt
Docker 不会在容器被删除后自动删除数据卷。且如果要删除数据卷,必须先将使用数据卷的容器停止,删除后,才能删除数据卷
[root@server ~]# docker rm myCentos myCentos01 myCentos02 #删除容器
myCentos
myCentos01
myCentos02
[root@server ~]# docker volume rm myvolume #删除数据卷
myvolume
使用 -v 参数也可以指定挂载一个本地主机的目录到容器中
[root@server ~]# docker run -d -ti --name web -v /webapp:/opt/webapp centos #创建一个名字叫web的容器,并且将宿主机的/webapp目录 挂载到容器的/opt/webapp目录
[root@server ~]# ls /webapp/ #查看宿主机的目录,如果没有该目录,会自动创建
[root@server ~]# echo "hello" >> /webapp/index.html #追加一个文件进去
[root@server ~]# docker exec -ti web /bin/bash #进入容器
[root@16c66fe42017 /]# cat /opt/webapp/index.html #查看容器/opt/webapp目录下的数据
hello
-v 参数也可以从主机挂载单个文件到容器中
[root@server ~]# docker run --rm -it -v ~/.bash_history:/.bash_history centos /bin/bash #创建一个容器,并将宿主机的~./bash_history文件挂载到容器的/.bash_history文件
--rm:创建容器时如果带上--rm表示容器终止时自动删除
如果有一些持续更新的数据需要在容器之间共享,最好创建数据卷容器。
数据卷容器其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的。
[root@server ~]# docker run -d -v /dbdata --name dbdata centos #创建一个数据卷容器
[root@server ~]# docker run -d -it --volumes-from dbdata --name db1 centos #创建一个db1容器, 使用 --volumes-from 来挂载 dbdata 容器中的数据卷
[root@server ~]# docker exec -it db1 /bin/bash #进入db1容器中
[root@1863842f0c16 /]# ls #查看已成功挂载dbdata数据卷
anaconda-post.log dbdata etc lib media opt root sbin sys usr
bin dev home lib64 mnt proc run srv tmp var
[root@1863842f0c16 /]# touch dbdata/file1 #在dbdata目录中创建一个文件
[root@server ~]# docker run -d -it --volumes-from dbdata --name db2 centos #再创建一个db2容器,使用 --volumes-from 来挂载 dbdata 容器中的数据卷
[root@server ~]# docker exec -it db2 /bin/bash #进入db2容器中
[root@0fca0699a7c7 /]# ls #查看也成功挂载dbdata数据卷
anaconda-post.log dbdata etc lib media opt root sbin sys usr
bin dev home lib64 mnt proc run srv tmp var
[root@0fca0699a7c7 /]# ls dbdata/ #并且还查看到了db1容器创建的file1文件,说明容器之间的数据共享了
file1
从上面可以看出可以使用超过一个 --volumes-from 参数来指定从多个容器挂载不同的数据卷,也可以从其他已经挂载了数据卷的容器来级联挂载数据卷,示例:
[root@server ~]# docker run -d -ti --name db3 --volumes-from db1 centos #创建一个db3容器,通过 --volumes-from 级联挂载上面的db1容器
[root@server ~]# docker exec -it db3 /bin/bash #进入db3容器中
[root@1d2d8c30fccf /]# ls #查看也有dbdata目录
anaconda-post.log dbdata etc lib media opt root sbin sys usr
bin dev home lib64 mnt proc run srv tmp var
[root@1d2d8c30fccf /]# ls dbdata/ #查看目录,同样有文件
file1
注意:使用 --volumes-from 参数所挂载数据卷的容器自己并需要保持在运行状态。如果删除了挂载的容器(包括dbdata、db1和db2),数据卷也不会被自动删除。如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时使用 docker rm -v 命令来指定同时删除关联的容器。
Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。
容器中可以运行一些网络应用,比如(nginx,apache,php等),要让外部访问这些应用可以通过 -P 或者 -p 参数来制定端口映射
使用 -P 参数时,Docker 会随机映射一个 端口到内部容器开放的网络端口。
[root@server ~]# docker run -d -P --name myNginx nginx:1.11 #创建一个容器,并通过-P随机映射端口到容器的网络端口
[root@server ~]# docker ps #查看容器信息,可以发现宿主机的32769端口映射到容器的80端口了,以及32768映射至443端口
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5ee2527dd496 nginx:1.11 "nginx -g 'daemon of…" 42 seconds ago Up 40 seconds 0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp myNginx
[root@server ~]# curl localhost:32769 #访问宿主机的32769便能访问到容器
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
同样,可以通过 docker logs 命令查看应用的信息, -f 参数 动态查看
[root@server ~]# docker logs -f myNginx
172.17.0.1 - - [06/Jan/2019:02:25:17 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
172.17.0.1 - - [06/Jan/2019:02:38:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
-p (小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。格式有:
ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
映射所有的接口地址
[root@server ~]# docker run -d -p 88:80 --name myNginx01 nginx:1.11 #使用hostPort:containerPort 格式宿主机的88端口映射到容器的80端口,默认会绑定宿主机所有接口上的所有地址
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fab763a9ec68 nginx:1.11 "nginx -g 'daemon of…" 50 seconds ago Up 48 seconds 443/tcp, 0.0.0.0:88->80/tcp myNginx01
映射到指定地址的指定端口
[root@server ~]# docker run -d -p 127.0.0.1:89:80 --name myNginx02 nginx:1.11 #使用ip:hostPort:containerPort 格式指定映射使用一个特定地址,如localhost地址127.0.0.1
913f0cad940155978825b40f8e4c2bf23a4872bbd06e441ced4b8043b72c683e
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
913f0cad9401 nginx:1.11 "nginx -g 'daemon of…" 6 seconds ago Up 5 seconds 443/tcp, 127.0.0.1:89->80/tcp myNginx02
映射到指定地址的任意端口
[root@server ~]# docker run -d -p 127.0.0.1::80 --name myNginx03 nginx:1.11 #使用ip:containerPort 绑定localhost的任意端口到容器的80端口,宿主机会自动分配一个端口
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
16b17ccf83cf nginx:1.11 "nginx -g 'daemon of…" 4 seconds ago Up 3 seconds 443/tcp, 127.0.0.1:32770->80/tcp myNginx03
还可以使用 udp 标记来指定 udp 端口
[root@server ~]# docker run -d -p 127.0.0.1:82:80/udp --name myNginx04 nginx:1.11 #将localhost的82端口映射到容器的80端口,并指定udp
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
79d14927ef84 nginx:1.11 "nginx -g 'daemon of…" 4 seconds ago Up 2 seconds 80/tcp, 443/tcp, 127.0.0.1:82->80/udp myNginx04
查看映射端口配置
使用 docker port 查看当前映射的端口配置,也可以查看到绑定的地址
[root@server ~]# docker port myNginx
443/tcp -> 0.0.0.0:32768
80/tcp -> 0.0.0.0:32769
[root@server ~]# docker port myNginx01
80/tcp -> 0.0.0.0:88
注意:
例如
[root@server ~]# docker run -d --name myNginx05 -p 8088:80 -p 8089:443 nginx:1.11 #将宿主机的8088和8089端口分别映射到容器的80/443端口
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0f18691e88e nginx:1.11 "nginx -g 'daemon of…" 4 seconds ago Up 3 seconds 0.0.0.0:8088->80/tcp, 0.0.0.0:8089->443/tcp myNginx05
容器的连接(linking)系统除了端口映射外,另一种是容器中应用交互的方式。该系统会在源和接收容器之间创建一个隧道,接受容器可以看到源容器指定的信息。
使用 --link 参数可以让容器之间安全的进行交互
[root@server ~]# docker run -e MYSQL_ROOT_PASSWORD=123456 -d --name myDB mysql #先创建一个新的数据库容器
[root@server ~]# docker run -d -P --name myWeb --link myDB:db nginx:1.11 #再创建一个web容器,并将它连接到 myDB 数据库容器
[root@server ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28bcc258fc47 nginx:1.11 "nginx -g 'daemon of…" 37 seconds ago Up 34 seconds 0.0.0.0:32771->80/tcp, 0.0.0.0:32770->443/tcp myWeb
49a6b11e2178 mysql "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp, 33060/tcp myDB
此时,myDB 容器和 myWeb 容器建立互联关系。
--link 参数的格式: --link name:alias
name:是要链接的容器的名称,
alias:是这个链接的别名
Docker 在两个互联的容器之间创建了一个安全的隧道,而且不用映射它们的端口到宿主机上,在启动myDB 容器时候没有使用 -p 和 -P 参数,从而避免了暴露数据库端口到外部网络上。
Docker 通过2种方式为容器公开连接信息:
使用 env 命令可以查看环境变量
[root@server ~]# docker run --rm --name myWeb1 --link myDB:DB nginx:1.11 env #创建一个web容器连接到上面的myDB,查看web容器的环境变量...
DB_PORT=tcp://172.17.0.2:3306
DB_PORT_3306_TCP=tcp://172.17.0.2:3306
DB_PORT_3306_TCP_ADDR=172.17.0.2
DB_PORT_3306_TCP_PORT=3306
DB_PORT_3306_TCP_PROTO=tcp
DB_PORT_33060_TCP=tcp://172.17.0.2:33060
DB_PORT_33060_TCP_ADDR=172.17.0.2
DB_PORT_33060_TCP_PORT=33060
DB_PORT_33060_TCP_PROTO=tcp
DB_NAME=/myWeb1/DB
DB_ENV_MYSQL_ROOT_PASSWORD=123456
其中DB_开头的环境变量是供web容器连接 DB 容器使用的。可以看到连接容器的协议,端口,密码等
除了环境变量,Docker 还添加host信息到父容器的 /etc/hosts 的文件:
[root@server ~]# docker run --rm -ti --name myWeb2 --link myDB:DB nginx:1.11 /bin/bash #创建一个web容器,同样连接到myDB容器
root@9397b6ca77bd:/# cat /etc/hosts #查看hosts文件信息
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 DB 49a6b11e2178 myDB
172.17.0.4 9397b6ca77bd
root@9397b6ca77bd:/# ping myDB
PING DB (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: icmp_seq=0 ttl=64 time=0.142 ms
可以看到这里有2个hosts
,第一个是 web
容器,web容器用id作为他的主机名,第二个是 DB 容器的 ip
和主机名。可以在web
容器中使用ping
命令来测试跟 DB
容器的连通。