目录
[Docker 架构](#Docker 架构)
[Docker 安装](#Docker 安装)
[CentOS 版本要求](#CentOS 版本要求)
[安装Docker CE(社区版)](#安装Docker CE(社区版))
[从一个ubuntu的hello world说起](#从一个ubuntu的hello world说起)
[Docker 镜像 容器常用命令](#Docker 镜像 容器常用命令)
[1. 镜像列表查找](#1. 镜像列表查找)
[2. 镜像拉取](#2. 镜像拉取)
[3. 镜像删除](#3. 镜像删除)
[4. 镜像更新](#4. 镜像更新)
[5. 镜像生成](#5. 镜像生成)
[6. 镜像标签](#6. 镜像标签)
[1. 容器启动](#1. 容器启动)
[2. 容器查看](#2. 容器查看)
[3. 容器再启动](#3. 容器再启动)
[4. 容器停止和重启](#4. 容器停止和重启)
[5. 后台模式与进入容器](#5. 后台模式与进入容器)
[6. 导出和导入](#6. 导出和导入)
[7. 强制停止容器](#7. 强制停止容器)
[8. 清理停止的容器](#8. 清理停止的容器)
[9. 容器别名及操作](#9. 容器别名及操作)
[10. 容器错误日志](#10. 容器错误日志)
Docker虚拟化
什么是Docker?
Docker是一个开源的应用容器引擎,它让开发者可以打包他们的应用以及依赖包到一个可移植容器中,然后发布到安装了任何 Linux 发行版本的机器上。Docker基于LXC来实现类似VM的功能,可以在更有限的硬件资源上提供给用户更多的计算资源。与同VM等虚拟化的方式不同,LXC不属于全虚拟化、部分虚拟化或半虚拟化中的任何一个分类,而是一个操作系统级虚拟化。
Docker是直接运行在宿主操作系统之上的一个容器,使用沙箱机制完全虚拟出一个完整的操作,容器之间不会有任何接口,从而让容器与宿主机之间、容器与容器之间隔离的更加彻底。每个容器会有自己的权限管理,独立的网络与存储栈,及自己的资源管理能,使同一台宿主机上可以友好的共存多个容器。
Docker借助Linux的内核特性,如:控制组(Control Group)、命名空间(Namespace)等,并直接调用操作系统的系统调用接口。从而降低每个容器的系统开销,并实现降低容器复杂度、启动快、资源占用小等特征。
Docker能干什么?
- 简化配置
这是Docker公司宣传的Docker的主要使用场景。虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件、系统),Docker在降低额外开销的情况下提供了同样的功能。它能让你将运行环境和配置放在代码中然后部署,同一个Docker的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。
- 代码流水线(Code Pipeline)管理
前一个场景对于管理代码的流水线起到了很大的帮助。代码从开发者的机器到最终在生产环境上的部署,需要经过很多的中间环境。而每一个中间环境都有自己微小的差别,Docker给应用提供了一个从开发到上线均一致的环境,让代码的流水线变得简单不少。
- 提高开发效率
不同的开发环境中,我们都想把两件事做好。一是我们想让开发环境尽量贴近生产环境,二是我们想快速搭建开发环境。
理想状态中,要达到第一个目标,我们需要将每一个服务都跑在独立的虚拟机中以便监控生产环境中服务的运行状态。然而,我们却不想每次都需要网络连接,每次重新编译的时候远程连接上去特别麻烦。这就是Docker做的特别好的地方,开发环境的机器通常内存比较小,之前使用虚拟的时候,我们经常需要为开发环境的机器加内存,而现在Docker可以轻易的让几十个服务在Docker中跑起来。
- 隔离应用
有很多种原因会让你选择在一个机器上运行不同的应用,比如之前提到的提高开发效率的场景等。我们经常需要考虑两点,一是因为要降低成本而进行服务器整合,二是将一个整体式的应用拆分成松耦合的单个服务(译者注:微服务架构)。如果你想了解为什么松耦合的应用这么重要,请参考Steve Yege的这篇论文,文中将Google和亚马逊做了比较。
-
整合服务器 正如通过虚拟机来整合多个应用,Docker隔离应用的能力使得Docker可以整合多个服务器以降低成本。由于没有多个操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker可以比虚拟机提供更好的服务器整合解决方案。
-
调适能力 Docker提供了很多的工具,这些工具不一定只是针对容器,但是却适用于容器。它们提供了很多的功能,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试Bug。你可以在《Docker拯救世界》的文章中找到这一点的例证。
-
多租户
另外一个Docker有意思的使用场景是在多租户的应用中,它可以避免关键应用的重写。我们一个特别的关于这个场景的例子是为IoT(物联网)的应用开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂,很难处理,重新规划这样一个应用不但消耗时间,也浪费金钱。
使用Docker,可以为每一个租户的应用层的多个实例创建隔离的环境,这不仅简单而且成本低廉,当然这一切得益于Docker环境的启动速度和其高效的diff命令。
- 快速部署
在虚拟机之前,引入新的硬件资源需要消耗几天的时间。Docker的虚拟化技术将这个时间降到了几分钟,Docker只是创建一个容器进程而无需启动操作系统,这个过程只需要秒级的时间。这正是Google和Facebook都看重的特性。
你可以在数据中心创建销毁资源而无需担心重新启动带来的开销。通常数据中心的资源利用率只有30%,通过使用Docker并进行有效的资源分配可以提高资源的利用率。
Docker和虚拟机的区别?
虚拟机Virtual Machine与容器化技术(代表Docker)都是虚拟化技术,两者的区别在于虚拟化的程度不同。
基本对比

- 虚拟机
- 基础设施(Infrastructure)。它可以是你的个人电脑,数据中心的服务器,或者是云主机。
- 主操作系统(Host Operating System)。你的个人电脑之上,运行的可能是MacOS,Windows或者某个Linux发行版。
- 虚拟机管理系统(Hypervisor)。利用Hypervisor,可以在主操作系统之上运行多个不同的从操作系统。类型1的Hypervisor有支持MacOS的HyperKit,支持Windows的Hyper-V以及支持Linux的KVM。类型2的Hypervisor有VirtualBox和VMWare。
- 操作系统(Guest Operating System)。假设你需要运行3个相互隔离的应用,则需要使用Hypervisor启动3个从操作系统,也就是3个虚拟机。这些虚拟机都非常大,也许有700MB,这就意味着它们将占用2.1GB的磁盘空间。更糟糕的是,它们还会消耗很多CPU和内存。
- 各种依赖。每一个从操作系统都需要安装许多依赖。如果你的的应用需要连接PostgreSQL的话,则需要安装libpq-dev;如果你使用Ruby的话,应该需要安装gems;如果使用其他编程语言,比如Python或者Node.js,都会需要安装对应的依赖库。
- Docker容器
- 主操作系统(Host Operating System)。所有主流的Linux发行版都可以运行Docker。对于MacOS和Windows,也有一些办法"运行"Docker。
- Docker守护进程(Docker Daemon)。Docker守护进程取代了Hypervisor,它是运行在操作系统之上的后台进程,负责管理Docker容器。
- 各种依赖。对于Docker,应用的所有依赖都打包在Docker镜像中,Docker容器是基于Docker镜像创建的。
- 应用。应用的源代码与它的依赖都打包在Docker镜像中,不同的应用需要不同的Docker镜像。不同的应用运行在不同的Docker容器中,它们是相互隔离的。
虚拟机是在物理资源层面实现的隔离,相对于虚拟机,Docker是你APP层面实现的隔离,并且省去了虚拟机操作系统(Guest OS)),从而节省了一部分的系统资源;Docker守护进程可以直接与主操作系统进行通信,为各个Docker容器分配资源;它还可以将容器与主操作系统隔离,并将各个容器互相隔离。虚拟机启动需要数分钟,而Docker容器可以在数毫秒内启动。由于没有臃肿的从操作系统,Docker可以节省大量的磁盘空间以及其他系统资源。
虚拟机与容器docker的区别,在于vm多了一层guest OS,虚拟机的Hypervisor会对硬件资源也进行虚拟化,而容器Docker会直接使用宿主机的硬件资源。
下面我们采用形象的比喻区分两者的隔离级别:
-
服务器:比作一个大型的仓管基地,包含场地与零散的货物------相当于各种服务器资源。
-
虚拟机技术:比作仓库,拥有独立的空间堆放各种货物或集装箱,仓库之间完全独立------仓库相当于各种系统,独立的应用系统和操作系统。
-
Docker:比作集装箱,操作各种货物的打包------将各种应用程序和他们所依赖的运行环境打包成标准的容器,容器之间隔离。
虚拟技术对比
- 隔离性
在于隔离性上面,由于vm对操作系统也进行了虚拟化,隔离的更加彻底。而Docker共享宿主机的操作系统,隔离性较差。
- 运行效率
由于vm的隔离操作,导致生成虚拟机的速率大大低于容器Docker生成的速度,因为Docker直接利用宿主机的系统内核。比如openstack能够以10台/min的速度创建虚拟机,而docker可以做到在几秒钟之内创建大量容器,它们的启动速度是在数量级上的差距。
因为虚拟机增加了一层虚拟硬件层,运行在虚拟机上的应用程序在进行数值计算时是运行在Hypervisor虚拟的CPU上的;另外一方面是由于计算程序本身的特性导致的差异。虚拟机虚拟的cpu架构不同于实际cpu架构,数值计算程序一般针对特定的cpu架构有一定的优化措施,虚拟化使这些措施作废,甚至起到反效果。
- 资源利用率
在资源利用率上虚拟机由于隔离更彻底,因此利用率也会相对较低。
因为虚拟机增加了一层虚拟硬件层,运行在虚拟机上的应用程序在进行数值计算时是运行在Hypervisor虚拟的CPU上的;另外一方面是由于计算程序本身的特性导致的差异。虚拟机虚拟的cpu架构不同于实际cpu架构,数值计算程序一般针对特定的cpu架构有一定的优化措施,虚拟化使这些措施作废,甚至起到反效果。比如对于本次实验的平台,实际的CPU架构是2块物理CPU。
Docker 架构
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
- Docker 客户端(Client) : Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。
- Docker 主机(Host) :一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
Docker 包括三个基本概念:
- 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
- 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
- 仓库(Repository):仓库可看着一个代码控制中心,用来保存镜像。

Docker 安装
CentOS 版本要求
官网要求,使用CentOS7的稳定版本,同时:
- 启用
centos-extras - 推荐使用
overlay2存储驱动
卸载老的Docker及依赖
Lua
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
安装一些依赖库
-
yum-utils 提供 yum-config-manager 类库
-
device-mapper-persistent-data 和 lvm2 被devicemapper 存储驱动依赖
sudo yum install -y yum-utils
device-mapper-persistent-data
lvm2
设置稳定版本的库
Lua
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
安装Docker CE(社区版)
yum install -y docker-ce
安装完之后启动
sudo systemctl start docker
测试是否安装成功
[root@pdai ~]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since Mon 2020-02-17 13:57:45 CST; 39s ago
Docs: https://docs.docker.com
Main PID: 26029 (dockerd)
Tasks: 8
Memory: 36.9M
CGroup: /system.slice/docker.service
└─26029 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd
仓库配置
Docker 安装好以后,我们就要开始为拉取镜像准备了;国内从 DockerHub 拉取镜像有时会特别慢,此时可以配置镜像加速器。
Docker 官方和国内很多云服务商都提供了国内加速器服务,比如:
- 阿里云的加速器:https://help.aliyun.com/document_detail/60750.html
- 网易加速器:http://hub-mirror.c.163.com
- Docker官方中国加速器:https://registry.docker-cn.com
- ustc 的镜像:https://docker.mirrors.ustc.edu.cn
- daocloud:https://www.daocloud.io/mirror#accelerator-doc(注册后使用)
这里配置 Docker官方中国的加速器:
对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)
{"registry-mirrors":["https://registry.docker-cn.com"]}
之后重新启动服务
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
镜像查看和拉取
拉取hello world
Lua
[root@bb ~]# docker pull hello-world:latest
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:9572f7cdcee8591948c2963463447a53466950b3fc15a247fcad1917ca215a2f
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest
看本地仓库是否有这个库
Lua
[root@bb ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 13 months ago 1.84kB
运行这个镜像的实例,即容器
Lua
[root@bb ~]# docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
如果你在没有镜像的时候,直接docker run hello-world也是可以的;它会先检查本地是否有这个镜像,没有的话会先从指定仓库中拉取
容器实例-ubuntu实例
上面是跑了一个官方的Hello-world容器实例, 这里通过介绍运行ubuntu的实例来全面理解如何跑一个Docker实例
从一个ubuntu的hello world说起
Docker 允许你在容器内运行应用程序, 使用 docker run 命令来在容器内运行一个应用程序。这里同样是个Hello World,不同在于它是在容器内部运行的。
Lua
[root@bb ~]# docker run ubuntu:latest /bin/echo "Hello world"
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
5c939e3a4d10: Pull complete
c63719cdbe7a: Pull complete
19a861ea6baf: Pull complete
651c9d2d6c4f: Pull complete
Digest: sha256:8d31dad0c58f552e890d68bbfb735588b6b820a46e459672d96e585871acc110
Status: Downloaded newer image for ubuntu:latest
Hello world
各个参数的含义:
docker: Docker 的二进制执行文件。run: 与前面的 docker 组合来运行一个容器。ubuntu:latest指定要运行的镜像,Docker 首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。/bin/echo "Hello world": 在启动的容器里执行的命令
以上命令完整的意思可以解释为:Docker 以 ubuntu 最新的(默认是latest) 镜像创建一个新容器,然后在容器里执行 bin/echo "Hello world",然后输出结果。
运行交互式的容器
以上,容器跑的是Ubuntu是一个系统实例,能否进入系统进行交互呢?
通过 docker 的两个参数 -i -t,让 docker 运行的容器实现"对话"的能力:
Lua
[root@bb ~]# docker run -i -t ubuntu:latest
root@414bf796cbe4:/# echo 'hello world'
hello world
root@414bf796cbe4:/#
各个参数解析:
-t: 在新容器内指定一个伪终端或终端。-i: 允许你对容器内的标准输入 (STDIN) 进行交互。
可以通过运行 exit 命令或者使用 CTRL+D 来退出容器
root@414bf796cbe4:/# exit
exit
[root@pdai ~]#
运行容器至后台模式
当跑完上面例子之后,用docker ps 命令看下后台是否有docker容器实例?此时是没有的。
需要-d参数,来让容器实例在后台运行,比如:
[root@pdai ~]# docker run -d ubuntu:latest /bin/sh -c "while true; do echo hello world; sleep 1; done"
1a51d2f023c947f2be2d9a78eb863e854ca302c89bf354654c409e23e7dd25d7
在输出中,我们没有看到期望的 "hello world",而是一串长字符,这个长字符串是容器 ID,对每个容器来说都是唯一的,可以通过容器 ID 来查看对应的容器发生了什么。
首先,需要确认容器有在运行,可以通过 docker ps 来查看:
Lua
[root@bb ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a51d2f023c9 ubuntu:latest "/bin/sh -c 'while t..." About a minute ago Up About a minute gifted_brown
输出详情介绍:
- CONTAINER ID: 容器 ID。
- IMAGE: 使用的镜像。
- COMMAND: 启动容器时运行的命令。
- CREATED: 容器的创建时间。
- STATUS: 容器状态(状态有7种)。
- created(已创建)
- restarting(重启中)
- running(运行中)
- removing(迁移中)
- paused(暂停)
- exited(停止)
- dead(死亡)
- PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。
- NAMES: 自动分配的容器名称。
我们通过docker logs 命令,查看指定容器内的标准输出:
Lua
[root@bb ~]# docker logs 1a51d2f023c9
hello world
以下是如何关闭后台实例
Lua
[root@bb ~]# docker stop 1a51d2f023c9
1a51d2f023c9
[root@bb ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@bb ~]#
Docker 镜像 容器常用命令
当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载。
镜像操作
1. 镜像列表查找
# 列出本地所有镜像
docker images
# 列出所有镜像,包括中间层镜像(以JSON格式输出,更详细)
docker images -a --format '{{json .}}'
# 根据镜像名或标签过滤镜像列表
docker images | grep tomcat
-
使用 docker search 命令来搜索镜像
[root@bb ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@pdai ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation... 9132 [OK]
mariadb MariaDB is a community-developed fork of MyS... 3233 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create... 676 [OK]
centos/mysql-57-centos7 MySQL 5.7 SQL database server 68
mysql/mysql-cluster Experimental MySQL Cluster Docker images. Cr... 62
centurylink/mysql Image containing mysql. Optimized to be link... 61 -
NAME: 镜像仓库源的名称
-
DESCRIPTION: 镜像的描述
-
OFFICIAL: 是否 docker 官方发布
-
STARS: 类似 Github 里面的 star,表示点赞、喜欢的意思。
-
AUTOMATED: 自动构建。
2. 镜像拉取
# 拉取官方最新版本镜像
docker pull tomcat
# 拉取指定版本镜像
docker pull tomcat:9.0.78
3. 镜像删除
# 根据镜像ID删除镜像
docker rmi <镜像ID>
# 根据镜像名和标签删除镜像
docker rmi tomcat:9.0.78
# 删除所有未打标签的镜像(<none>:<none>)
docker images | grep none | awk '{print $3}' | xargs docker rmi
4. 镜像更新
# 重新拉取镜像,覆盖本地旧版本(如果远程有更新)
docker pull <镜像名:标签>
# 例如更新tomcat镜像
docker pull tomcat:latest
5. 镜像生成
从 Dockerfile 构建镜像,假设 Dockerfile 在当前目录:
# -t 用于指定镜像的标签,格式为 镜像名:标签
docker build -t myapp:v1 .
6. 镜像标签
# 为已有的镜像添加新标签
docker tag myapp:v1 myapp:latest
# 将本地镜像标记为远程仓库可推送的形式(用于推送至私有仓库)
docker tag myapp:v1 private-registry.example.com/myapp:v1
容器操作
1. 容器启动
# 以默认配置启动容器(以后台模式运行)
docker run -d tomcat
# 启动容器并映射端口(将容器的8080端口映射到宿主机的8080端口)
docker run -d -p 8080:8080 tomcat
# 启动容器并挂载目录(将宿主机的 /host/path 挂载到容器的 /container/path)
docker run -d -v /host/path:/container/path tomcat
2. 容器查看
# 列出正在运行的容器
docker ps
# 列出所有容器(包括已停止的)
docker ps -a
# 查看容器的详细信息(以JSON格式输出)
docker inspect <容器ID或名称>
3. 容器再启动
# 启动已停止的容器
docker start <容器ID或名称>
4. 容器停止和重启
# 停止正在运行的容器
docker stop <容器ID或名称>
# 重启正在运行或已停止的容器
docker restart <容器ID或名称>
5. 后台模式与进入容器
# 以交互模式(前台)启动容器并进入容器内部
docker run -it tomcat bash
# 进入正在运行的容器(如果容器已在运行,使用 exec 命令进入)
docker exec -it <容器ID或名称> bash
6. 导出和导入
# 导出容器的文件系统为一个tar文件
docker export <容器ID或名称> > container_export.tar
# 从tar文件导入为一个新的镜像(导入后需创建容器)
cat container_export.tar | docker import - myimage:v1
7. 强制停止容器
# 强制停止容器(发送 SIGKILL 信号)
docker kill <容器ID或名称>
8. 清理停止的容器
# 清理所有已停止的容器
docker container prune
9. 容器别名及操作
在 Docker 中,并没有直接设置别名的命令,但可以通过修改 ~/.bashrc 或 ~/.zshrc 等文件,使用 alias 命令为常用的 Docker 命令设置别名。例如:
# 在 ~/.bashrc 文件中添加别名
alias dkps='docker ps'
alias dkrm='docker rm'
# 使别名生效
source ~/.bashrc
10. 容器错误日志
# 查看容器的日志(最新的日志输出)
docker logs <容器ID或名称>
# 实时跟踪容器日志(类似 tail -f)
docker logs -f <容器ID或名称>
可视化容器
安装Portainer
编写docker-compose文件
Portainer部署的步骤比较简单,还是以docker-compose文件的形式完成Portainer的安装,首先需要编写的名为portainer.yml的文件,内容如下:
version: "3"
services:
portainer:
image: portainer/portainer:latest
container_name: portainer
ports:
- "9000:9000"
volumes:
- /app/portainer/data:/data
- /var/run/docker.sock:/var/run/docker.sock
完成后,将这个文件上传到我们的服务器即可。
初始化并配置容器
将配置文件上传之后我们就可以尝试启动了,为了方便查看服务是否可以正常启动,我们还先以前台运行的方式启动,到达portainer.yml文件位置,键入前台启动命令
docker-compose -f portainer.yml up
当控制台没有输出错误,并显示正常启动后,我们分开服务器的9000端口,浏览器输入服务器ip:9000,即可进入Portainer初始化界面,它会要求我们配置用户名密码:
