Docker:超详细的Docker基本概念介绍、命令总结与实战

文章目录

一、Docker概述

1.1 Docker的定义

Docker是一个开源的应用容器引擎,基于go语言开发并遵循apache2.0协议开源,让开发者可以打包它们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器完全使用沙盒机制,相互之间不会存在任何接口,几乎没有性能开销,可以很容易的在机器和数据中心运行。最重要的是,它们不依赖于任何语言、框架或者包装系统。

官方学习网站:
1.docker官网
2.docker中文库

1.2 Docker的组成

在这里插入图片描述
镜像(Image):

docker镜像就好比一个模板,我们可以通过这个模板来创建容器服务,tomcat镜像===>run==>tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。

容器(container):

是用镜像运行的实例

仓库(repository):

仓库就是存放 镜像(image)的地方!
仓库又可以分为 公有仓库和私有仓库

1.3 Docker支持的重要技术

Docker本质就是宿主机的一个进程,docker是通过namespace实现资源隔离,通过cgroup实现资源限制 ,通过写时复制技术(copy-on-write)实现高效文件操作 (类似于虚拟机的磁盘,比如分配500g并不是实际占用物理磁盘500g,只有当需要修改时才复制一份数据)。

资源隔离类型总结如下:

类型功能说明
MNT Namespace提供磁盘挂载点和文件系统的隔离能力
IPC Namespace提供进程间通信的隔离能力
Net Namespace提供网络隔离能力
UTS Namespace提供主机隔离能力
PID Namespace提供进程隔离能力
User Namespace提供用户隔离能力

1.4 Docker容器与虚拟机的区别

Docker容器技术和虚拟机技术,都是虚拟化技术。但是Docker有着比虚拟机更少的抽象层。 由于Docker不需要Hypervisor实现硬件资源虚拟化,运行在Docker容器上的程序直接使用的都是实际物理机的硬件资源。所以Docker效率比虚拟机效率高。达到了秒级启动的地步。

在这里插入图片描述

在这里插入图片描述

具体差异总结如下表:

名称Docker虚拟机
启动速度秒级分钟级
计算能力损耗几乎无损耗 50%左右
性能接近原生弱于
系统支持量(单机)上千个几十个
隔离性通过namespace实现资源隔离,通过cgroup实现限制资源的最大使用量完全隔离,每个虚拟机都有独立的硬件
内核所有容器共享宿主机的内核每个虚拟机都有独立的操作系统和内核

1.5 Docker特性

  • 文件系统隔离:每个进程容器运行在一个完全独立的根文件系统里。
  • 资源隔离:系统资源,像CPU和内存等可以分配到不同的容器中,使用cgroup。
  • 网络隔离:每个进程容器运行在自己的网路空间,虚拟接口和IP地址。
  • 日志记录:Docker将收集到和记录的每个进程容器的标准流(stdout/stderr/stdin),用于实时检索或者批量检索
  • 变更管理:容器文件系统的变更可以提交到新的镜像中,并可重复使用以创建更多的容器。无需使用模板或者手动配置。
  • 交互式shell:Docker可以分配一个虚拟终端并且关联到任何容器的标准输出上,例如运行一个一次性交互shell。

二、Docker安装与配置

2.1 Docker卸载

如果你是第一次安装Docker,那么你可以跳过此小节的介绍,但如果你的服务器上面有旧版的Docker版本,你想卸载它,只需按照如下步骤去操作即可:

# 1.卸载依赖
yum remove docker-ce docker-ce-cli containerd.io
# 2.删除资源
rm -rf /var/lib/docker
rm -rf /var/lib/containerd
# /var/lib/docker  docker的默认工作路径

操作后效果如下图,需要你确认,因此键盘敲下Y后回车:

在这里插入图片描述
然后记得去查看卸载是否干净:

在这里插入图片描述

2.2 Docker安装

Docker有两个分支版本:Docker CE和Docker EE,即社区版和企业版,本篇文章都以Docker CE为例。Docker安装的整体步骤按照如下去操作即可:

# 1.卸载旧的版本
yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
# 2.需要的安装包
yum install -y yum-utils
# 3.设置镜像的仓库
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo # 默认是从国外的。
yum-config-manager \
    --add-repo \
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 推荐使用阿里云的。
# 安装容器之前,更新yum软件包索引。
yum makecache fast
# 4.安装容器相关的。docker-ce(社区版)docker-ee(企业版)
yum install docker-ce docker-ce-cli containerd.io
# 5.启动docker
systemctl start docker
# 6.使用docker version查看是否安装成功
docker version

按照上面总结的步骤一步步操作后执行效果如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.3 配置其他镜像源加速

  1. 登录容器镜像服务控制台。
  2. 单击【镜像中心】 > 【镜像加速器】,可以看到阿里云为您提供了一个专属的镜像加速地址。
  3. 按步骤配置使用这四个命令即可。
# 1.新建etc下的docker目录
sudo mkdir -p /etc/docker
# 2.配置Docker的自定义镜像仓库地址。请将下面命令中的镜像仓库地址https://xxx.xxx.xxx.com替换为阿里云为您提供的专属镜像加速地址。
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://xxx.xxx.xxx.com"]
}
EOF
# 3.重新加载配置服务
sudo systemctl daemon-reload
# 4.重启Docker服务
sudo systemctl restart docker

除此之外,大家还可以去阅读两个云服务器厂商提供的安装与配置步骤:
1.基于ECS快速搭建Docker环境-阿里云
2.云服务器 搭建 Docker-最佳实践-文档中心-腾讯云 (tencent.com)

如果按照上述步骤配置后,速度还是不尽人意,那这里还提供一个大神写的博客《从Docker拉取镜像一直失败超时?这些解决方案帮你解决烦恼》,文中提供了很多种方式用来解决Docker拉取镜像超时问题。

2.4 Docker运行hello-world

既然上面已经配置完全了,那我们来一个经典的开始,来拉取hello-world镜像,看看是否能运行起来。基本操作步骤如下:

# 1.查看当前有哪些镜像
docker images

# 2.去dockerHub拉取hello-world镜像
docker pull hello-world

# 3.上一步拉取后,再次看当前有哪些镜像
docker images

# 4.运行hello-world
docker run hello-world

# 5.查看当前运行的docker容器
docker ps

# 6.因为hello-world打印完就停止了,因此利用docker ps -a查看已经停止运行的容器
docker ps -a

上述命令运行后效果显示图如下:

在这里插入图片描述


三、Docker镜像

3.1 镜像的定义

镜像是一种轻量级,可执行化的独立软件包,用来打包软件和其所依赖的环境,它包含运行某个软件所需要的所有内容,包括代码、运行时库、环境变量和配置文件。

3.2 镜像分层原理

3.2.1 分层存储

①分层存储的工作原理
Docker镜像是由一系列只读的层(layer)组成的。这些层按照从下到上的顺序堆叠,每一层都是基于下面一层的变化。当我们创建或更新一个镜像时,只有被改变的部分会被添加为一个新层,其他部分保持不变。

②层的创建过程
通常,我们使用Dockerfile来定义和创建Docker镜像。Dockerfile中的每一条指令(如FROM,RUN,COPY等)都会创建一个新的层。

例如,考虑以下Dockerfile:

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

这个Dockerfile会创建四个层:

基础层:从ubuntu:22.04镜像开始。

第二层:运行apt-get update && apt-get install -y python3,安装Python3。

第三层:将app.py文件复制到镜像的/app/目录。

第四层:指定容器启动时运行的命令python3 /app/app.py

③层的存储与复用
这些层都是只读的,并且每一层只存储与前一层的差异部分。当我们构建一个新镜像时,Docker会检查每一层的内容是否已经存在。如果一个层与现有的层完全相同,Docker会直接复用这个层,而不是重新创建。

这种机制大大提高了构建和存储效率。例如,如果你有多个Dockerfile都基于ubuntu:22.04,那么这个基础层只需要存储一次,所有的镜像都可以共享它。

④容器的可写层
当一个容器从镜像启动时,Docker会在镜像的顶部添加一个新的可写层。容器对文件系统的所有改变,如创建新文件,修改现有文件,删除文件等,都会被记录在这个可写层中。这个可写层允许多个容器共享同一个镜像,同时又保持各自的状态。当容器被删除时,可写层也会被删除,而底层的镜像保持不变。

通过详细解释分层存储的工作原理、层的创建过程、层的存储与复用,以及容器的可写层,我们可以更深入地理解Docker如何利用分层存储来优化镜像的构建、存储和运行效率。

3.2.2 重用与共享

①共享的实现方式

  • 内容寻址:每个层都有一个唯一的哈希值,这个哈希值是根据层的内容计算出来的。如果两个层的内容完全相同,它们的哈希值也相同。Docker使用这个哈希值来判断两个层是否相同。
  • 层的复用:当构建一个新镜像时,Docker会检查每一层的哈希值。如果一个层的哈希值与现有的层相同,Docker会直接使用现有的层,而不是重新创建。
  • 共享层的存储:所有的镜像层都存储在Docker主机的文件系统中。如果多个镜像共享一个层,这个层在文件系统中只存储一次,所有的镜像都引用这个共享的层。

②共享机制的优点

  • 节省存储空间:多个镜像可以共享相同的层,而无需重复存储,大大节省了存储空间。
  • 加速镜像构建:当构建一个新镜像时,如果所需的层已经存在,Docker会直接使用现有的层,而不是重新创建,从而加速了构建过程。
  • 加速镜像分发:当从Docker仓库拉取镜像时,如果某些层已经存在于本地,只需拉取缺失的层,减少了网络传输的数据量,加速了镜像分发。

3.3 联合文件系统

联合文件系统(Union File System,简称 UnionFS)是 Docker 镜像的基石。它是一种分层、轻量级并且高性能的文件系统,支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

3.3.1 工作原理

UnionFS 的工作原理是将多个不同的目录(也叫分支)内容联合挂载到同一个目录下,并呈现为单个一致的文件系统。这些分支可以是只读的,也可以是可读写的。当对这个虚拟文件系统进行修改时,实际上是在可写的分支上进行操作,而原始的文件并没有被修改。在 Docker 中,镜像就是由多个只读层组成的。当我们启动一个容器时,Docker 会在这些只读层之上添加一个可读写层。当容器修改现有的文件时,该文件将被复制到可读写层,并在那里被修改。这种机制被称为写时复制(Copy-on-Write)。

3.3.2 加载过程

当启动一个容器时,Docker 会从镜像的底层开始,依次加载每一层文件系统,直到最顶层的可读写层。这个过程可以看作是一个 UnionFS 的加载过程。

假设我们有一个包含三个层的镜像:一个基础层,一个中间层,和一个顶层。当我们基于这个镜像启动一个容器时,Docker 会:

  1. 加载基础层,这通常是一个操作系统的文件系统,如 Ubuntu 的文件系统。

  2. 加载中间层,这可能包含一些基本的工具和库,如 Python 解释器。

  3. 加载顶层,这通常包含我们的应用代码和配置。

  4. 添加一个可读写层,用于存储容器运行时的修改。

这四层被 UnionFS 挂载到同一个目录下,呈现为一个完整的文件系统给容器使用。当容器修改文件时,修改会被写入可读写层。

3.3.3 UnionFS的优点

  • 节省存储空间:多个镜像可以共享相同的只读层,从而减少磁盘占用。
  • 加速镜像构建和部署:当构建一个新镜像时,只需要构建与现有镜像不同的层,相同的层可以直接复用。这大大加速了镜像的构建和部署过程。
  • 促进镜像的分发:由于镜像是分层存储的,当我们下载一个镜像时,实际上只需要下载我们本地没有的层。这减少了网络传输的数据量,加速了镜像的分发。

3.4 commit镜像

# 提交容器成为一个新的副本
docker commit
# 命令和git原理类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
# 1.启动一个默认的tomcat。
# 2.发现这个默认的tomcat是没有webapps应用,镜像的原因,官方的镜像默认webapps下面是没有文件的。
# 3.我自己拷贝进去了基本的文件。
# 4.将我们操作过的容器通过commit提交为一个镜像!我们以后就使用我们修改过的镜像即可,这就是我们自己的一个修改的镜像。

在这里插入图片描述

3.5 镜像大小问题

Docker镜像的大小是一个重要的考量因素,因为它直接影响到镜像的存储、传输和部署效率。镜像越大,占用的存储空间越多,传输和部署的时间也越长。因此,优化镜像大小是Docker使用中的一个重要课题。

3.5.1 基础镜像大小

基础镜像是构建其他镜像的起点,它通常包含操作系统的核心文件和一些常用的库。基础镜像的大小会直接影响到所有基于它构建的镜像的大小。因此,在选择基础镜像时,我们需要权衡功能完整性和镜像大小。如果你的应用不需要完整的操作系统,选择一个最小化的基础镜像可以显著减小最终镜像的大小。

3.5.2 软件依赖

除了基础镜像,应用运行所需的软件库和其他依赖也会增加镜像的大小。每个 RUN 指令安装的软件包,每个COPY或ADD指令添加的文件,都会增加镜像的大小。

为了减小镜像大小,我们可以采取以下措施:

  1. 只安装必要的软件包:仔细评估每个软件包的必要性,只安装运行应用所必需的包。

  2. 清理安装缓存:在安装软件包后,删除下载的软件包缓存,如/var/cache/apt/archives/。

  3. 合并RUN指令:尽可能将多个RUN指令合并为一个,这样可以减少镜像的层数,从而减小镜像大小。

  4. 使用.dockerignore文件:在COPY或ADD文件时,使用.dockerignore文件排除不必要的文件和目录,减小镜像大小。

3.5.3 多阶段构建

多阶段构建是Docker提供的一个功能,它允许我们在一个Dockerfile中使用多个FROM语句,每个FROM语句都可以使用不同的基础镜像,并且每个阶段都可以从前一个阶段复制文件。

这个功能对于需要构建依赖项但最终镜像不需要这些依赖项的情况非常有用。通过多阶段构建,我们可以在一个阶段安装所有必要的依赖项并构建应用,然后在另一个阶段只复制构建好的应用,而不复制构建依赖项。这样可以显著减小最终镜像的大小。


四、Docker容器数据卷

4.1 容器数据卷的定义

Docker容器在产生数据的时候,如果不通过Docker commit生成新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除之后,数据自然而然的也会消失。当应用与运行的环境打包形成容器运行,伴随着容器运行产生的数据,我们希望这些数据能够持久化,容器之间也能够实现数据的共享,容器中引用了数据卷的概念。

4.2 数据卷的使用

使用命令-v来挂载,通过 -v挂载目录后会对这个俩个目录中的数据进行双向绑定(无论是添加操作还是删除操作)

docker run -it -v 主机目录:容器目录 镜像名 /bin/bash
# 测试,查看容器信息
docker inspect 容器id
  • mounts 加载的文件系统
  • source 是源地址 就是 当前你这个docker里面的地址目录
  • destination 是 这个容器的目录

我们先来个小案例感受一下,把容器中的home目录挂载到主机的home目录下,然后往容器目录中写入文件,在主机确认是否已同步。

1.挂载目录
docker run -it -v /home/test01:/home nginx /bin/bash

2.查看容器信息
docker inspect 容器id

3.docker容器中home目录里新建dockertest.txt,并且往文件里echo进去“docker study”
echo "docker study" > home/dockertest.txt

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

另外再打开一个终端,先cd /home目录,这下我们发现多出来了一个test01目录,这个test01目录就是我们刚刚启动的容器内部的home目录,并且,此时这两个目录是同步的状态,查看test01目录下多了dockertest.txt文件,然后里面内容是docker study。

在这里插入图片描述

4.3 具名挂载和匿名挂载

挂载方式可以分为两种:具名挂载和匿名挂载。
所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxxx/_data(xxxx是卷名)
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况在使用的具名挂载

4.3.1 匿名挂载

docker run -d -p --name -v 容器内路径 镜像名

我们首先使用匿名挂载的命令启动容器看一下挂载内容:

1. 匿名挂载
docker run -d -P --name=nginxt01 -v /etc/nginx nginx

2.查看卷信息
docker volume ps

在这里插入图片描述

在这里插入图片描述
上图中的2a开头的一段数字就是挂载到宿主机的名字。

4.3.2 具名挂载

具名挂载就很简单了,跟我们之前演示的指定路径挂载很相似。

docker run -d -p --name -v  卷名:容器内路径 镜像名
1. 匿名挂载
docker run -d -P --name=nginxt02 -v meilihao:/etc/nginx nginx

2.查看卷信息
docker volume ps

在这里插入图片描述
在这里插入图片描述

注:上面在使用inspect命令时,可以看见只写了容器ID的前缀802,它就能帮我们查出信息。这是一种简便的方式,因为802就足以帮我们区分是哪个容器了(其实其它命令也可以使用这样的简单方式,只要容器ID或者镜像ID能唯一确定一个对象就行)

总结一下,挂载方式可以根据如下方式去区分:

如何确定是具名挂载还是匿名挂载:
-v  容器内路径               #匿名挂载
-v  卷名:容器内路径          #具名挂载
-v  /宿主机路径:容器内路径    # 指定路径挂载

拓展

# 通过 -v 容器内的路径:ro    rw    改变读写权限
ro    read only    # 只读
rw    read write    # 可读可写
# 一旦设置了容器权限,容器对我们挂载出来的内容就有了限定。
docker run -d -p 3344:80 --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -p 3344:80 --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
# 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!

4.4 容器数据卷作用和特点

卷就是目录或者文件,存在一个或者多个容器之中,由Docker 挂载到容器,但是不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或者共享数据的特性。卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker 不会在容器删除时删除其挂载的数据卷。

其存在如下特点:

  • 数据卷可在容器之间共享或者重用数据。
  • 卷中的更改可以直接生效。
  • 数据卷中的更改不会包含在镜像的更新中。
  • 数据卷的生命周期一直持续到没有容器使用它为止。

五、DockerFile

5.1 DockerFile定义

Dockerfile 是一个用来构建镜像的文本文件,其中包含了一系列指令,告诉 Docker 如何构建镜像。在构建过程中,Docker 会读取 Dockerfile 中的指令,并按照指令的顺序逐步执行,最终生成一个新的镜像。

5.2 Dockerfile构建镜像过程

构建步骤:
1、编写一个dockerfile文件
2、docker build 构建成为一个镜像
3、docker run运行镜像
4、docker push发布镜像(DockerHub、阿里云镜像仓库!)

5.2.1 分层构建

Docker 的镜像构建过程是分层的。每执行一条 Dockerfile 指令,都会在当前镜像的顶部创建一个新的层。这种分层构建的机制有以下优点:

  • 复用层:不同的镜像可以共享相同的层,减少磁盘占用和加速构建过程。
  • 缓存:如果 Dockerfile 的指令和上下文没有改变,Docker 可以使用缓存的层,无需重新执行指令。
  • 最小化变更:由于每个层都是独立的,对镜像的修改可以仅限于某些层,而不影响其他层。

举个例子,假设我们有以下的 Dockerfile:

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

当我们构建这个镜像时,Docker会:

  1. 从 ubuntu:22.04 镜像开始,这是第一层。
  2. 执行 RUN 指令,在第一层的基础上创建第二层,该层包含了更新的软件包列表和安装的 Python3。
  3. 执行 COPY 指令,在第二层的基础上创建第三层,该层包含了复制到镜像中的 app.py 文件。
  4. 执行 CMD 指令,在第三层的基础上创建第四层,该层指定了容器启动时运行的默认命令。

每一层都只包含了与前一层的差异部分,而不是完整的文件系统。这种分层构建的方式大大提高了构建和存储的效率。

5.2.2 缓存利用

为了加速构建过程,Docker 会尽可能地利用缓存。当我们重新构建一个镜像时,Docker 会检查每个指令的缓存情况:

  • 如果该指令和上下文没有改变,且存在可用的缓存层,Docker 会直接使用缓存层,而不会重新执行该指令。
  • 如果该指令或上下文发生了改变,Docker 会重新执行该指令,并为后续的指令invalidate缓存。

因此,为了最大限度地利用缓存,我们应该将最不可能改变的指令放在 Dockerfile 的前面,如 FROM,LABEL 等,将最可能改变的指令放在 Dockerfile 的后面,如 COPY,ADD 等。

例如,不要这样写:

RUN apt-get update
RUN apt-get install -y python3

而应该这样写:

RUN apt-get update && apt-get install -y python3

这样,如果 apt-get update 的结果没有变化,apt-get install 就可以直接使用缓存,而不需要重新执行。

5.2.3 构建上下文

当我们执行 docker build 命令时,当前目录被称为构建上下文(build context)。Docker 会将构建上下文中的文件发送到 Docker daemon,daemon 根据 Dockerfile 中的指令构建镜像。

这意味着,Dockerfile 中的 COPYADD 指令只能复制构建上下文中的文件。如果我们试图复制上下文之外的文件,会得到一个错误。
为了减小构建上下文的大小,提高构建效率,我们应该将 Dockerfile 放在一个空目录或者项目根目录中。使用 .dockerignore 文件排除不需要的文件和目录,如 .git,node_modules 等。避免使用 ADD 指令自动解压缩归档文件,而是在 RUN 指令中显式地解压缩。

例如,假设我们有以下的项目结构:

.
├── .git
├── .dockerignore
├── Dockerfile
├── app.py
└── README.md

我们可以在 .dockerignore 文件中添加以下内容:

.git
README.md

我们可以在 .dockerignore 文件中添加以下内容:

FROM python:3.9
COPY app.py /app/
CMD ["python", "/app/app.py"]

这样,当我们执行 docker build 命令时,只有 app.py 文件会被发送到 Docker daemon ,而 .git 目录和 README.md 文件会被排除在构建上下文之外,从而减小了构建上下文的大小,提高了构建效率。

5.3 Dockerfile命令

DockerFile需要注意的编写规范:
1、#代表注释
2、指令必须要大写,后面最少需要带一个参数,最多无限制;
3、执行dockerfile的时候,指令是按照从上到下的顺序执行的;

FROM        # 基础镜像,一切从这里开始构建
MAINTAINER    # 镜像是谁写的:姓名+邮箱
RUN            # 以FROM中定义的image为基础环境运行指令命令,生成结果将作为新image的一个镜像层,并可由后续指令所使用。RUN后跟要执行的命令。
ADD            # 与COPY指令的功能相似,但ADD传输压缩包的时候,是可以解压的。
WORKDIR        # 相当于cd切换目录的命令,如果切换的那个地方没有哪个目录,则会自动创建一个目录。
VOLUME        # 指定基于新生成的Image运行Container时期望作为volume使用的目录。
EXPOSE        # 指定基于新生成的lmage运行Container时期望 暴露的端口,但实际暴露与否取决于"docker run”命令的选项,支持TCP和UDP协议。
CMD            # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT    # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD        # 当构建一个被继承DockerFile这个时候就会运行ONBUILD的指令。触发指令。
COPY        # 相当于cp命令,复制主机上或者前一阶段构建结果中(需要使用–from选项)文件或目录生成新的镜像。
ENV            # 以键值格式设定环境变量,可被其后的指令所调用,且基于新生成的image运行的Container中也会存在这些变量。

六、Docker网络

6.1 清除本机Docker环境

为了更好的搞清楚Docker网络,我们先把前面用到的Docker镜像和Docker容器进程先清一下:

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)

6.2 Docker网络原理

# linux系统中用ifonfig查看网络情况,windows系统中用ipconfig查看网络情况
ifconfig

我们使用ifconfig可以看到三组网络。首先是docker0,这是我们本节的重点,docker的网络地址。之后是eth0,阿里云的外网地址。lo口,本地环回地址,可以代表localhost。

在这里插入图片描述

关于docker0呢,它其实是一个叫docker0的虚拟网桥,我们可以使用brctl命令来查看一下。

# 1.要使用brctl命令,需要先下载包bridge-utils
yum -y install bridge-utils

# 2.查看
brctl show 

在这里插入图片描述

Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的 Container-IP 直接通信。

Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部的网络是无法絮叨这个地址的,这也意味着外部网络无法直接通过Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主机(端口映射),即docker run 创建容器时候通过-p或者-P参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。

docker run -d --name test1 -P nginx					#随机映射端口(从32768开始)
 
docker run -d --name test2 -p 43000:80 nginx		#指定映射端口
 
#查看容器的输出和日志信息
docker logs 容器的ID/名称

6.3 veth-pair技术

什么是veth-pair技术?我们首先来启动两个tomcat容器,借用tomcat的网络连接帮我们去理解它。

docker run -d -P --name=tomcat01 tomcat:7
docker run -d -P --name=tomcat02 tomcat:7
提示:选择tomcat7是因为这个镜像包含了ip addr 等常用命令!

启动机器之后,我们查看容器ip,通过容器的ip 去ping宿主机ip,发现是通的。

# 1.进入到tomcat01容器中查看网络情况
docker exec -it tomcat01 ip addr

# 2.ping网络地址
ping 172.17.0.2

在这里插入图片描述
理解:我们每启动一个docker容器,docker就会给docker容器分配一个ip,安装docker之后,会产生一个叫docker0的网卡,这里使用的就是veth-pair技术。
使用ip addr命令,查看我们的网卡。

在这里插入图片描述

我们发现多出来了两个网卡,到了这里,你已经知道这两张网卡是那里来的了吧。没错,是启动容器之后产生的!我们回过头来查看我们在启动的容器IP,就会很清晰的发现,这个网卡是成对存在的!容器内的24对应着宿主机的25,容器内的26对应宿主机的27。

在这里插入图片描述

veth-pair 就是一堆的虚拟设备接口,他们都是成对出现的,一端连接着协议,一端连接着彼此。使得它充当了一个桥梁的作用。

根据上面tomcat的网络连接绘制一个网络模型图:

在这里插入图片描述

结论:tomcat01和tomcat02是公用的一个路由器,docker 0。
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用IP

在这里插入图片描述

6.4 Docker的几种网络模式

Host模式,使用 --net=host指定:容器不会虚拟出自己的网卡,配置主机的IP等,而是使用宿主机的IP和端口

Container模式,使用--net=container:NAME_or_ID 指定: 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP。(一般不用)

None模式,使用 --net=none 指定: 该模式关闭了容器的网络功能。(一般不用)

Bridge模式,使用 --net=bridge 指定:默认为该模式(桥接,自己创建也是用它),此模式会为每一个容器分配,设置IP等,并将容器连接到一个docker0 的虚拟网桥,通过docker 0 网桥以及iptables nat 表配置与宿主机通信。

6.4.1 Host模式

如果启动容器的时候使用host模式,那么这个容器就不会获取一个独立的Network Namespace,而是和宿主机共用一个network namespace,容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,比如文件系统,进程列表等还是和宿主机是分隔开来的。

使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。

在这里插入图片描述

6.4.2 Container模式

创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围(端口不能一致),只有一个容器有自己的网卡,出去的话还是docker0进行通讯。

这个模式指定新创建的容器和已经存在的一个容器共享一个network namespace(命名空间),而不是和宿主机共享,同样两个容器除了网络方面,其他的如文件系统,进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。

在这里插入图片描述

6.4.3 None模式

使用none模式,Docker容器拥有自己的network namespace,但是并不为Docker容器进行任何网络配置。也就是说,这个docker容器没有网卡,IP,路由等信息,需要我们自己为Docker容器添加网卡,配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性,可以安全的存储数据,不会被攻击,可以充当仓库。

在这里插入图片描述

6.4.4 Bridge模式(默认模式)

Bridge模式是docker容器中默认的模式,像前面章节启动tomcat那样都是以Bridge模式启动的。

当docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有的容器,就通过交换机连在了一个二层网络中。从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair 设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以veth xxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中,可以通过brctl show命令查看。

bridge模式是docker的默认网络模式,不写-net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT 规则,实现端口转发功能。可以使用iptables -t nat -vnL查看

在这里插入图片描述

6.4.5 自定义网络

前面我们已经知道了docker容器内部的ip是有可能会发生改变的,那么当我们调用服务时应当使用服务名来调用。这里以tomcat01举例,ping tomcat01是ping不通的,但是ping ip能够ping通。

在这里插入图片描述

因此,我们需要建立自定义网络,将两个容器都加入,使其能够通过服务名调取服务。首先第一步先建立网络:

# 1.清除前面运行的tomcat容器
docker rm -f $(docker ps -aq)

# 2.自己创建bridge网络
docker network create --driver bridge --subnet 192.168.0.0/24 --gateway 192.168.0.1 testnet

# 3. 查看所有网络情况
docker network ls

# 4. 查看某个网络的具体信息
docker network inspect 网卡名字  #查看网卡详细信息

在这里插入图片描述

然后,在自己建立的网络中运行两个容器:

docker run -d -P --name=tomcat01-net --net=testnet tomcat:7
docker run -d -P --name=tomcat02-net --net testnet tomcat:7

docker network inspect testnet   #查看testnet网卡详细信息

在这里插入图片描述

最后再验证是否能通过服务名代替ip访问:

docker exec -it tomcat01-net ping -c 3 IP
docker exec -it tomcat01-net ping -c 3 tomcat02-net

提示,ping -c可以自定义ping的次数

在这里插入图片描述


七、Docker常用命令

上面介绍了一些Docker的基本概念和重要知识点,里面或多或少都用到了一些命令,但是使用的并不全面,因此本章节全面梳理了一下Docker的常用命令,大家按模块去练习和记忆即可。

7.1 帮助命令

# 显示docker的版本信息
docker version    
# 显示docker的系统信息,包括镜像和容器的数量    
docker info   
# 帮助命令           
docker 命令 --help         

帮助文档地址:Reference documentation

7.2 镜像命令

7.2.1 搜索镜像

# 搜索对应镜像名称
docker search 镜像名
# 还可以在后面加一些过滤参数,如搜索stars大于3000的镜像
docker search 镜像名 --filter=STARS=3000

7.2.2 拉取镜像

# 下载镜像,如果不带版本号,则默认拉取latest版本
docker pull 镜像名[:tag]

在这里插入图片描述

7.2.3 查看镜像

# 查看当前存在的镜像
docker images
# 参数说明
-a, --all # 显示所有镜像 (docker images -a)
-q, --quiet # 仅显示镜像id (docker images -q)

在这里插入图片描述

7.2.4 根据镜像id获取镜像详细信息

docker inspect 镜像ID号

# 内容解释
lowerdir是镜像层,目录或者文件是只读的,其实就是rootfs,image layer可以分很多层,所以对应的lowerdir是可以有多个目录
upperdir是在lowerdir之上的容器层,这层是可读可写的,在启动一个容器时候会进行创建,所有的对容器数据更改都发生在这里层
MergedDir是表现层,是容器的挂载点

在这里插入图片描述

7.2.5 存出镜像

docker save -o 存储文件名 存储的镜像
# 比如存出镜像命名为nginx存在当前目录下
docker save -o /opt/nginx-1.20.tar nginx:1.20		

7.2.6 载入镜像

docker load -i 存储文件名
# 比如把GuessNumberDemo.tar镜像上传到linux上,就需要用到docker load -i命令。(记得带上路径)
docker load -i /root/GuessNumberDemo.tar

7.2.7 删除镜像

# 删除指定的镜像
docker rmi -f 镜像id
# 删除多个镜像(空格分隔)
docker rmi -f 镜像id 镜像id 镜像id
# 删除全部的镜像
docker rmi -f $(docker images -aq)

7.2.8 上传镜像

上传镜像时,默认会上传到 docker Hub 官方公共仓库https://hub.docker.com,因此需要提前注册使用公共仓库的账号。可以使用 docker login 命令来输入用户名、密码和邮箱来完成注册和登录。除此之外,在上传镜像之前,还需要先对本地镜像添加新的标签,然后再使用 docker push 命令进行上传。

docker tag nginx:latest meilihao/nginx:web		#添加新的标签时必须在前面加上自己的dockerhub的username
docker login								    #登录公共仓库
Username:
password:
docker push meilihao/nginx:web					#上传镜像

7.2.9 查看镜像变更历史

docker history 镜像名称:tag

7.3 容器命令

注意:我们得先有镜像,才可以创建容器。

7.3.1 创建容器

docker create [选项] 镜像
# 参数说明
-i 让容器开启标准输入接受用户输入命令
-t 让 Docker 分配一个伪终端 tty
-it 合起来实现和容器交互的作用,运行一个交互式会话 shell

7.3.2 启动容器

docker start 容器的ID/名称
docker ps -a

7.3.3 新建容器并启动

docker run [可选参数] image
# 参数说明
--name="name"        容器名字:用来区分容器
-d                    后台方式运行:相当于nohup
-it                    使用交互式运行:进入容器查看内容
-p                    指定容器的端口(四种方式)小写字母p
    -p ip:主机端口:容器端口
    -p 主机端口:容器端口
    -p 容器端口
    容器端口
-P                     随机指定端口(大写字母P)

# 问题:docker ps发现centos停止了
# 常见的坑:docker容器使用后台运行,就必须要有要一个前台进程,docker发现没有应用,就会自动停止。
# 比如:nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了

7.3.4 列出所有运行的容器

docker ps    # 列出当前正在运行的容器
# 命令参数可选项
-a        # 列出当前正在运行的容器+历史运行过的容器
-n=?    # 显示最近创建的容器(可以指定显示几条,比如-n=1)
-q        # 只显示容器的编号

7.3.5 根据容器id查看容器具体信息

docker inspect 容器id # 根据容器id查看容器具体信息

7.3.6 退出容器

exit        # 容器直接停止,并退出
ctrl+P+Q    # 容器不停止,退出

7.3.7 删除容器

docker rm 容器id                    # 删除容器(不能删除正在运行的容器)如果要强制删除:docker rm -f 容器id
docker rm -f $(docker ps -aq)        # 删除全部容器
docker ps -a -q|xargs docker rm        # 删除所有容器

7.3.8 启动和停止容器

docker start 容器id        # 启动容器
docker restart 容器id    # 重启容器
docker stop 容器id        # 停止当前正在运行的容器
docker kill 容器id        # 强制停止当前容器

7.4 其他命令

7.4.1 查看所有数据卷情况

docker volum ls

7.4.2 docker网络命令

# 查看网络
docker network ls

# 创建网络 
docker network create 网络名称

# 查看指定网络
docker network inspect 网络名称

# 删除网络
docker network rm 网络名称

7.4.3 查看日志

docker logs -tf --tail 容器id
# 自己编写一段shell脚本
docker run -d centos /bin/sh -c "while true;do echo kuangshen;sleep 1;done"
[root@root //]# docker logs -tf  容器id
[root@root //]# docker logs -tf --tail 10  容器id
# 显示日志
-tf                        # 显示日志
--tail number    # 要显示的日志条数

7.4.4 查看容器中进程的信息

# 命令 docker top 容器id 
[root@root ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED              STATUS              PORTS      NAMES
25eb9d70b2b4   redis     "docker-entrypoint.s…"   About a minute ago   Up About a minute   6379/tcp   awesome_chatterjee
[root@root ~]# docker top 25eb9d70b2b4
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
systemd+            181442              181422              0                   09:47               ?                   00:00:00            redis-server *:6379

7.4.5 进入当前正在运行的容器

# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 命令
docker exec -it 容器id /bin/bash
# 测试
[root@root ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
88d23bcbe1f2   centos    "/bin/sh -c 'while t…"   13 minutes ago   Up 13 minutes             silly_lichterman
[root@root ~]# docker exec -it 88d23bcbe1f2 /bin/bash
[root@88d23bcbe1f2 /]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:23 ?        00:00:00 /bin/sh -c while true;do echo kuangshen;sleep 1;done
root       841     0  0 08:37 pts/0    00:00:00 /bin/bash
root       858     1  0 08:37 ?        00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
root       859   841  0 08:37 pts/0    00:00:00 ps -ef
# 方式二
docker attach 容器id
# 测试
[root@root ~]# docker attach 88d23bcbe1f2
正在执行当前的代码...
# docker exec        # 进入容器后开启一个新的终端,可以再里面操作(常用)
# docker attach        # 进入容器正在执行的终端,不会启动新的进程。

7.4.6 从容器内拷贝文件到主机

docker cp 容器id:容器内路径 目的主机的路径
[root@iZbp13qr3mm4ucsjumrlgqZ home]# ll
total 0
[root@iZbp13qr3mm4ucsjumrlgqZ home]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@iZbp13qr3mm4ucsjumrlgqZ home]# docker run -it centos /bin/bash
[root@6eda31ad7987 /]# [root@iZbp13qr3mm4ucsjumrlgqZ home]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
6eda31ad7987   centos    "/bin/bash"   17 seconds ago   Up 16 seconds             stoic_kepler
# 进入到容器内部
[root@iZbp13qr3mm4ucsjumrlgqZ home]# docker attach 6eda31ad7987
[root@6eda31ad7987 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@6eda31ad7987 /]# cd /home/
[root@6eda31ad7987 home]# ls
# 在容器的/home路径下创建test.java文件
[root@6eda31ad7987 home]# touch test.java
[root@6eda31ad7987 home]# ls
test.java
[root@6eda31ad7987 home]# exit
exit
[root@iZbp13qr3mm4ucsjumrlgqZ home]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@iZbp13qr3mm4ucsjumrlgqZ home]# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED              STATUS                      PORTS     NAMES
6eda31ad7987   centos    "/bin/bash"   About a minute ago   Exited (0) 28 seconds ago             stoic_kepler
# 将文件拷贝出来到主机上(在主机上执行该命令)
[root@iZbp13qr3mm4ucsjumrlgqZ home]# docker cp 6eda31ad7987:/home/test.java /home
[root@iZbp13qr3mm4ucsjumrlgqZ home]# ls
test.java
# 拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现,自动同步(容器内的/home路径和主机上的/home路径打通)

如果有朋友想对命令原理做个深入了解,可以去阅读这位大神的博客《Docker常用命令原理图》


八、实战测试

前文也是介绍了Docker的基础知识和常用命令,里面夹杂了一些动手案例,但是并不全面,本章节再补充两个实战案例供大家练习。

8.1 部署Nginx

我们先来实现部署Nginx的案例,按照如下步骤执行命令:

# 1.搜索镜像
docker search nginx

# 2.下载镜像
docker pull nginx

# 3.查看镜像
docker images

# 4.启动容器
docker run -d --name nginx01 -p 80:80 nginx

# 5.查看容器
docker ps

# 6.测试访问
curl 127.0.0.1:80

# 7.进入容器修改页面
docker exec -it 容器ID /bin/bash

在这里插入图片描述

最后在网站上输入自己的公网IP即可(如果访问不了,请检查云服务器上的端口号是否放开)

在这里插入图片描述

8.2 自定义镜像并发布外网

本案例以自定义一个tomcat镜像为例,先利用Dockerfile文件进行镜像的构建,然后将其发布到docker hub上。经过这个案例,前面的知识基本就已经串起来了。

8.2.1 制作镜像

一般一个项目都在一个文件夹中,我们只需要在项目目录下编辑一个 Dockerfile 文件即可,当执行docker bulid指定时如果没有-f参数指定 dockerfile 文件,会默认寻找项目目录下的 Dockerfile 来构建镜像,所以我们名字通常设定为Dockerfile。

前置准备工作:创建Dockerfile文件,以及一个任意内容的readme.md,然后准备一下tomcat和jdk的jar包。tomcat和jdk的jar包可以通过网站下载完后再借用Xftp传输到对应的文件夹中。

在这里插入图片描述

然后编辑Dockerfile,内容如下:

FROM centos
MAINTAINER wasteland<123456@qq.com>

# 宿主机目录下文件拷贝到容器内,文件如果不是绝对路径会默认寻找 dockerfile 文件的同级目录下,所以最好所有文件都在同一个目录下
COPY readme.md /usr/local/readme.md

# 添加我们自己的安装包
ADD jdk-8u152-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.102.tar.gz /usr/local

#安装vim编辑器
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
RUN yum makecache
RUN  yum -y install vim
RUN  yum -y install net-tools


# 配置工作目录
ENV MYPATH /usr/local
WORKDIR $MYPATH

# 配置环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_152
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.102
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.102
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

# 暴露端口
EXPOSE 8080

# 启动的时候自动运行tomcat,打印日志
CMD /usr/local/apache-tomcat-9.0.102/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.102/bin/logs/catalina.out

然后进行构建:

# 构建,不指定 dockerfile 文件会自动寻找该目录下的 Dockerfile
docker build -t mytomcat .

在这里插入图片描述

构建完之后查看本地镜像情况:

在这里插入图片描述

8.2.2 访问测试

在上一小节已经构建完镜像,接下来就是利用docker把它跑起来:

# 运行
docker run -d -p 8080:8080 --name mytomcat \
-v /root/mytomcat/test:/usr/local/apache-tomcat-9.0.102/webapps/test \
-v /root/mytomcat/logs:/usr/local/apache-tomcat-9.0.102/logs \
--privileged=true \
mytomcat

然后在浏览器中输入公网ip:8080进行测试:

在这里插入图片描述

然后在我们自定义的 tomcat 服务器中上传一个项目,在本地挂载目录,丢一个项目上去。首先进入到我们挂载的宿主机目录下/root/mytomcat下:

在这里插入图片描述

浏览器访问ip地址:8080/test/,测试效果如下图:

在这里插入图片描述

8.2.3 发布外网

如果我们想发布到外网上,那首先我们需要注册docker hub 账号,网址:https://hub.docker.com/(这里当然需要科学上网哈)

在这里插入图片描述

然后按照如下步骤操作即可:

# 1.登录用户
docker login -u 用户名 -p 密码

# 2.给要上传的镜像打上标签
docker tag 镜像id 镜像名:版本号

# 3.上传镜像
docker push 镜像名:版本号

私有仓库,一般都是公司内部自行搭建的,步骤跟上述相同。


九、参考文章

https://blog.youkuaiyun.com/weixin_45683778/article/details/137873026
https://blog.youkuaiyun.com/qq_28550263/article/details/139248460


十、总结

到这里对于Docker的相关学习就已经结束了,在这个学习过程中除了对一些基本概念要有所了解,而且应该熟练掌握常用命令。此篇文章是作者在阅读了前人优秀的文章后学习与总结得出,希望对您有所帮助。


创作不易,如果有帮助到你的话请给点个赞吧!我是Wasteland,下期文章再见!

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wasteland~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值