Docker概述

docker官网:https://www.docker.com/
文档:https://docs.docker.com/ Docker的文档是超级详细的!
仓库:https://hub.docker.com/
Linux Container容器(LXC)是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。

  • 传统虚拟机,虚拟出一条硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件
  • 容器内的应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以轻便
  • 每个容器间是互相隔离,每个容器内都有一个属于自己的文件系统,互不影响

Docker的基本组成

  • Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是 image镜像文件。只有通过这个镜像文件才能生成 Docker 容器。

  • Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。类似Java中的类。

  • Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。类似Java中类的实例化。

    • 它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。

    • 可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

    • 容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

  • 仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库分为公开仓库(Public)和私有仓库(Private)两种形式。

Docker是怎么工作的?

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。

Docker的工作流程如下:

  • 用户使用Docker客户端执行命令,例如创建容器或构建镜像。
  • Docker客户端将命令发送给Docker守护程序。
  • Docker守护程序根据命令执行相应操作,可能需要下载或从本地存储中加载镜像。
  • Docker守护程序创建容器实例并运行应用程序。
  • 容器与宿主系统共享内核,但有自己的文件系统和进程空间,因此可以隔离运行。
  • 用户可以使用Docker客户端来管理容器的生命周期,包括启动、停止、删除等操作。
  • Docker守护程序还可以与Docker仓库通信,以获取新的镜像或将本地构建的镜像推送到仓库。

为什么Docker比Vm快

Docker 比传统虚拟机(VM)快的主要原因在于容器化技术的设计和实现方式与虚拟机不同,导致了更轻量级、更高效的性能。

以下是 Docker 相对于传统 VM 的速度优势的主要原因:

  1. 共享内核: Docker 容器与宿主系统共享相同的操作系统内核,而传统虚拟机需要在每个 VM 中运行独立的操作系统。由于共享内核,容器比虚拟机更轻量级。这意味着容器不需要启动整个操作系统,只需加载应用程序和其依赖项。虚拟机则需要模拟整个操作系统,包括内核,这会占用更多的资源和时间。
  2. 更少的资源消耗: 由于容器共享内核,它们在内存和计算资源方面要求较少。虚拟机需要分配大量的内存和CPU资源来模拟完整的操作系统。这意味着在相同硬件上可以运行更多的容器实例,从而提高了资源利用率。
  3. 更快的启动时间: 由于容器是轻量级的,它们的启动速度非常快。通常,容器可以在几秒钟内启动,而虚拟机可能需要几分钟来启动。这对于弹性扩展和快速部署非常有用。
  4. 更少的虚拟化开销: 虚拟机需要额外的虚拟化层来模拟硬件和操作系统,这会引入额外的性能开销。容器化使用了更轻量级的虚拟化技术,减少了这种开销。
  5. 更好的资源管理: Docker 提供了先进的资源管理和隔离机制,允许您精确地控制容器使用的资源。这可以确保不同容器之间的资源不会相互干扰,并允许更好地利用物理资源。
Docker容器 虚拟机(VM)
操作系统 与宿主机共享OS 宿主机OS上运行虚拟机OS
存储大小 镜像小,便于存储和传输 镜像庞大(vmdk,vdi等)
运行性能 几乎无额外性能损失 操作系统额外的CPU,内存消耗
移植性 轻便,灵活,适应于Linux 笨重,与虚拟化技术耦合度高
硬件亲和度 面向软件运维者 面向硬件运维者
部署速度 快速,秒级 较慢,10以上

Docker常用命令

帮助命令

帮助文档的地址:https://docs.docker.com/engine/reference/commandline/

1
2
3
4
5
6
7
docker version #显示docker的版本信息。
docker info #显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help #帮助命令

# 查看docker容器使用内存情况
# (每秒刷新,也挺耗内存的一个命令)
docker stats

镜像命令

1
2
3
4
5
6
7
8
#查看所有本地主机上的镜像 可以使用docker image ls代替
docker images
# 搜索镜像
docker search
# 下载镜像 docker image pull
docker pull
# 删除镜像 docker image rm
docker rmi
  1. docker images列出本地主机上的镜像

    • 显示说明:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      REPOSITORY:表示镜像的仓库源
      TAG:镜像的标签
      IMAGE ID:镜像ID
      CREATED:镜像创建时间
      SIZE:镜像大小

      同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。

      如果你不指定一个镜像的版本标签,例如你只使用 ubuntu,
      docker 将默认使用 ubuntu:latest 镜像
    • OPTIONS说明:

      1
      2
      3
      4
      -a :列出本地所有的镜像(含中间映像层)
      -q :只显示镜像ID。
      --digests :显示镜像的摘要信息
      --no-trunc :显示完整的镜像信息
  2. docker search 某个XXX镜像名字

    • 从网站:https://hub.docker.com 上查找

    • 命令: docker search [OPTIONS] 镜像名字

    • OPTIONS说明

      1
      2
      3
      --no-trunc : 显示完整的镜像描述
      -s : 列出收藏数不小于指定值的镜像。
      --automated : 只列出 automated build类型的镜像;
  3. docker pull 某个XXX镜像名字下载镜像

    • docker pull 镜像名字[:TAG]
  4. docker rmi 某个XXX镜像名字ID删除镜像

    • 删除单个: docker rmi -f 镜像ID

    • 删除多个: docker rmi -f 镜像名1:TAG 镜像名2:TAG

    • 删除全部:docker rmi -f $(docker images -qa)

容器命令

  • 有镜像才能创建容器,这是根本前提(下载一个CentOS镜像演示docker pull centos)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 新建容器并启动
    docker run 镜像id
    # 列出所有运行的容器 docker container list
    docker ps
    # 删除指定容器
    docker rm 容器id
    # 启动容器
    docker start 容器id
    # 重启容器
    docker restart 容器id
    # 停止当前正在运行的容器
    docker stop 容器id
    # 强制停止当前容器
    docker kill 容器id

新建并启动容器

  • docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

    • OPTIONS说明(常用):有些是一个减号,有些是两个减号

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      --name="容器新名字": 为容器指定一个名称;
      -d: 后台运行容器,并返回容器ID,也即启动守护式容器;

      -i:以交互模式运行容器,通常与 -t 同时使用;
      -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;

      --rm 当退出时删除运行的容器

      -P(大写): 随机端口映射;
      -p: 指定端口映射,有以下四种格式

      ip:hostPort:containerPort
      ip::containerPort
      hostPort:containerPort
      containerPort
  • 启动交互式容器docker run -it centos /bin/bash

    • 默认使用镜像centos:latest,-it 以交互模式启动一个容器,在容器内执行/bin/bash命令。

列出运行的容器

  • docker ps [OPTIONS]

    • OPTIONS说明(常用):

      1
      2
      3
      4
      5
      -a :列出当前所有正在运行的容器+历史上运行过的
      -l :显示最近创建的容器。
      -n:显示最近n个创建的容器。
      -q :静默模式,只显示容器编号。
      --no-trunc :不截断输出。

退出容器

  • 两种退出方式
    1. exit,容器停止退出
    2. ctrl+P+Q容器不停止退出

启动容器

  • docker start 容器ID或者容器名

重启容器

  • docker restart 容器ID或者容器名

停止容器

  • docker stop 容器ID或者容器名

强制停止容器

  • docker kill 容器ID或者容器名

删除已停止的容器

  • docker rm 容器ID

  • 一次性删除多个容器

    • docker rm -f $(docker ps -a -q)
    • docker ps -a -q | xargs docker rm

容器命令2

启动守护式容器

  • 后台启动命令 docker run -d 容器名,容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。

  • 启动一个centos容器

    1
    2
    #使用镜像centos:latest以后台模式启动一个容器
    docker run -d centos
  • 然后docker ps -a 进行查看, 会发现容器已经退出,说明很重要的一点: Docker容器需要后台运行,就必须有一个前台进程

  • 比如你web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动响应的service即可。例如service nginx start, 但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用, 就会立刻停止。最佳的解决方案是将你要运行的程序以前台进程的形式运行。

  • 添加前台进程

    1
    docker run -d centos /bin/sh -c "while true;do echo hello zzyy;sleep 2;done"

查看容器日志

  • docker logs -f -t --tail 容器ID

    1
    2
    3
    # -t 加入时间戳   -f 跟随最新的日志打印
    -tf #显示日志信息(一直更新)
    --tail # 数字显示最后多少条
  • 示例

    1
    2
    docker logs -t --tail n 容器id  #查看n行日志
    docker logs -tf 容器id #显示日志信息

查看容器内运行的进程

  • 类似在容器中使用ps命令 。 docker top 容器ID

查看容器内部细节

  • docker inspect 容器ID

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 查看镜像的元数据
    docker inspect 55321bcae33d
    [
    {
    "Id":
    "55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066",
    "Created": "2020-05-15T05:22:05.515909071Z",
    "Path": "/bin/sh",
    ...
    }
    ]

交互式进入运行的容器

常容器都是使用后台方式运行的,需要进入容器,修改一些配置可以使用

1
2
docker exec -it 容器id shell  # 有 /bin/bash  /bin/sh
docker attach 容器ID

上述两个区别:

  • exec 是在容器中打开新的终端,并且可以启动新的进程(常用)
  • attach 直接进入容器启动命令的终端,不会启动新的进程

容器内拷贝文件到主机上

1
2
3
4
5
6
docker cp  容器ID:容器内路径 目的主机路径
#进入docker容器内部
➜ ~ docker exec -it fsfa58c8c141 /bin/bash
#新建一个文件
[root@fsfa58c8c141 /]# echo "hello" > hello.java
➜ ~ docker cp fsfa58c8c141:/hello.java /tmp #拷贝容器内hello.java 到主机下的/tmp目录

镜像命令

1
2
3
docker commit 提交容器成为一个新的副本
# 命令和git原理类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[TAG]

Docker命令帮助文档

命令 备注 说明
attach Attach to a running container 当前 shell 下 attach 连接指定运行镜像
build Build an image from a Dockerfile 通过 Dockerfile 定制镜像
commit Create a new image from a container changes 提交当前容器为新的镜像
cp Copy files/folders from the containers filesystem to the host path 从容器中拷贝指定文件或者目录到宿主机中
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 an existing container 在已存在的容器上运行命令
export Stream the contents of a container as a tar archive 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history Show the history of an image 展示一个镜像形成历史
images List images 列出系统当前镜像
import Create a new filesystem image from the contents of a tarball 从tar包中的内容创建一个新的文件系统映像[对应export]
info Display system-wide information 显示系统相关信息
inspect Return low-level information on a container 查看容器详细信息
kill Kill a running container kill 指定 docker 容器
load Load an image from a tar archive 从一个 tar 包中加载一个镜像[对应 save]
login Register or Login to the docker registry server 注册或者登陆一个 docker 源服务器
logout Log out from a Docker registry server 从当前 Docker registry 退出
logs Fetch the logs of a container 输出当前容器日志信息
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT 查看映射端口对应的容器内部源端口
pause Pause all processes within a container 暂停容器
p List containers 列出容器列表
pull Pull an image or a repository from the docker registry server 从docker镜像源服务器拉取指定镜像或者库镜像
push Push an image or a repository to the docker registry server 推送指定镜像或者库镜像至docker源服务器
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 to a tar archive 保存一个镜像为一个 tar 包[对应 load]
search Search for an image on the Docker Hub 在 docker hub 中搜索镜像
start Start a stopped containers 启动容器
stop Stop a running containers 停止容器
tag Tag an image into a repository 给源中镜像打标签
top Lookup the running processes of a container 查看容器中运行的进程信息
unpause Unpause a paused container 取消暂停容器
version Show the docker version information 查看 docker 版本号
wait Block until a container stops, then print its exit code 截取容器停止时的退出状态值

Docker可视化

portainer是Docker图形化界面管理工具!提供一个后台面板供我们操作!

1
2
3
# 运行如下命令即可 打开可视化服务
docker run -d -p 8080:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer

访问:http://ip:8080/

Docker镜像讲解

镜像是一种轻量级、可执行的独立软件保,用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件

UnionFs (联合文件系统)

UnionFs(联合文件系统):Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,他支
持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(
unite several directories into a single virtual filesystem)。

Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系
统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。

  • boots(boot file system)主要包含 bootloader和 Kernel, bootloader主要是引导加 kernel,
  • Linux刚启动时会加bootfs文件系统,在 Docker镜像的最底层是 boots。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs转交给内核,此时系统也会卸载bootfs。
  • rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu,Centos等等。

平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?

  • 对于个精简的OS,rootfs可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。
  • 由此可见对于不同的Linux发行版, boots基本是一致的, rootfs会有差別,因此不同的发行版可以工用bootfs.

镜像为什么采用分层结构?

所有的 Docker镜像都起始于一个基础镜像层,当进行修改或培加新的内容时,就会在当前镜像层之
上,创建新的镜像层。

举一个简单的例子,假如基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在
该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创健第三个镜像层。在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,上层镜像层中的文件可以覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。

Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统
一的文件系统。Linux上可用的存储引撃有AUFS、 Overlay2、 Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于 Linux中对应的文件系统或者块设备技术,井且每种存储引擎都有其独有的性能特点。

Docker在 Windows上仅支持 windowsfilter 一种存储引擎,该引擎基于NTFS文件系统之上实现了分层
和CoW 。

Docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层!最大的好处,莫过于资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了。

Docker数据管理

Docker的理念是将运用与运行的环境打包形成容器运行,Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了。为了能保存数据在docker中我们使用卷,提出了容器数据卷。卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。

卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性:

  1. 数据卷可在容器之间共享或重用数据

  2. 卷中的更改可以直接生效

  3. 数据卷中的更改不会包含在镜像的更新中

  4. 数据卷的生命周期一直持续到没有容器使用它为止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看所有的volume的情况
docker volume ls

# 显示一个或多个卷的详细信息
docker volume inspect [OPTIONS] VOLUME [VOLUME...]

# 创建卷 --name 指定名称
docker volume create [OPTIONS] [VOLUME]

# 删除所有未使用的本地卷
docker volume prune

# 删除一个或多个卷 -f 强制删除
docker volume rm [OPTIONS] VOLUME [VOLUME...]

# 更新卷(仅限群集卷)此命令与Swarm编排器配合使用。
docker volume update [OPTIONS] [VOLUME]

三种挂载: 匿名挂载、具名挂载、指定路径挂载

1
2
3
4
# 三种挂载: 匿名挂载、具名挂载、指定路径挂载
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载 docker volume ls 是查看不到的

命令挂载卷 -v

官方建议用映射目录的方式来互相同步修改。

1
2
3
4
5
6
--volume	-v		#  绑定挂载卷
--volumes-from # 从指定的容器装载卷

docker run -it -v 主机目录:容器内目录:权限

➜ ~ docker run -it -v /root/data:/home centos /bin/bash

卷权限说明

  • 不指定(默认): 权限同rw

  • ro (readonly 只读)

    • 文件:容器内不能修改,会提示read-only
    • 文件夹:容器内不能修改、新增、删除文件夹中的文件,会提示read-only
  • rw (readwrite 可读可写)

    • 文件:
      • 不管是宿主机还是容器内修改,都会相互同步
      • 容器内不允许删除,会提示Device or resource busy;
      • 宿主机删除文件,容器内的不会被同步
    • 文件夹:不管是宿主机还是容器内修改、新增、删除文件,都会相互同步
    1
    2
    docker run -d -P --name nginx05 -v juming:/etc/nginx:ro nginx
    docker run -d -P --name nginx05 -v juming:/etc/nginx:rw nginx

具名和匿名挂载

  • 匿名挂载: -v只写了容器内的路径,没有写容器外的路径

    1
    2
    3
    4
    5
    6
    7
    # 匿名挂载 -v 容器内路径
    docker run -d -P --name nginx01 -v /etc/nginx nginx

    # 查看所有的volume的情况
    docker volume ls
    DRIVER VOLUME NAME
    local 83a7785666d2612ea6b7ace391743306a5c63e0f913c5c7942de717c42009d9e
  • 具名挂载: -v 卷名:容器内路径

    1
    2
    3
    4
    5
    6
    docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx

    # 查看所有的volume的情况
    docker volume ls
    DRIVER VOLUME NAME
    local juming-nginx

卷路径

  • 查看一下juming-nginx这个卷的信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [root@k8s-master01 ~]# docker volume inspect juming-nginx
    [
    {
    "CreatedAt": "2023-09-08T16:48:48+08:00",
    "Driver": "local",
    "Labels": null,
    "Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
    "Name": "juming-nginx",
    "Options": null,
    "Scope": "local"
    }
    ]
  • 所有的docker容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumes/xxxx/_data

  • 如果指定了目录,docker volume ls 是查看不到的

Docker磁盘空间清理

查看 Docker 的磁盘使用情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 类似于 Linux上的 df 命令
[root@k8s-master01 ~]# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 15 14 2.575GB 794.5MB (30%)
Containers 38 20 4.687kB 2.391kB (51%)
Local Volumes 2 2 338.6kB 0B (0%)
Build Cache 0 0 0B 0B


TYPE列出了 Docker 使用磁盘的 4 种类型:
Images :所有镜像占用的空间,包括拉取下来的镜像,和本地构建的。
Containers :运行的容器占用的空间,表示每个容器的读写层的空间。
Local Volumes :容器挂载本地数据卷的空间。B
uild Cache :镜像构建过程中产生的缓存空间(只有在使用 BuildKit 时才有,Docker 18.09 以后可用)。
最后的 RECLAIMABLE 是可回收大小。

容器清理

1
2
3
4
5
#  仅删除停止运行的容器。
docker container prune
# 删除所有容器(包括停止的、正在运行的)。
docker rm -f $(docker ps -aq)
docker container rm -f $(docker container ls -aq)

镜像清理: 有一些镜像是隐形的:

​ 子镜像,就是被其他镜像引用的中间镜像,不能被删除。
​ 悬挂状态的镜像,就是不会再被使用的镜像,可以被删除。

1
2
3
4
5
6
7
8
9
# 通过镜像的id来删除指定镜像。
docker rmi <image id>
# 除所有镜像。但正在被容器使用的镜像无法删除
docker image rm $(docker image ls -q)

# 可以列出所有悬挂状态的镜像
docker image ls -f dangling=true
docker image rm $(docker image ls -f dangling=true -q)
docker image prune

数据卷清理

1
2
3
# 删除不再使用的数据卷
docker volume rm $(docker volume ls -q)
docker volume prune

缓存清理: Docker 18.09 引入了 BuildKit ,提升了构建过程的性能、安全、存储管理等能力。

1
2
# 删除 build cache。
docker builder prune

一键清理: 注意,这两个命令会把你暂时关闭的容器,以及暂时没有用到的 Docker 镜像都删掉了。

1
2
3
4
5
# 可以用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及 dangling 镜像(即无 tag 的镜像)
docker system prune
# 清理得更加彻底,可以将没有容器使用 Docker镜像都删掉。
docker system prune -a

初识Dockerfile

dockerfile 是用来构建docker镜像的文件!命令参数脚本!

基础知识

  • 每个保留关键字(指令)都是必须是大写字母
  • 执行从上到下顺序
  • #表示注释
  • 每一个指令都会创建提交一个新的镜像层,并提交!

DockerFile指令

保留字 说明
FROM 基础镜像,当前新镜像是基于哪个镜像的
MAINTAINER 镜像维护者的姓名和邮箱地址
RUN 容器构建时需要运行的命令
EXPOSE 当前容器对外暴露出的端口
WORKDIR 指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
ENV 用来在构建镜像过程中设置环境变量
ENV MY_PATH /usr/mytest,这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样;也可以在其它指令中直接使用这些环境变量,比如:WORKDIR $MY_PATH
ADD 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
COPY 类似ADD,拷贝文件和目录到镜像中(不会自解压)。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置COPY [“src”, “dest”]
VOLUME 容器数据卷,用于数据保存和持久化工作
CMD 指定一个容器启动时要运行的命令
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
ENTRYPOINT 指定一个容器启动时要运行的命令,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数
ONBUILD 当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发

自定义centos镜像

构建步骤:

  1. 编写一个dockerfile文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #获取国内源
    curl -o CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

    vim mydockerfile-centos

    #继承自哪个镜像
    FROM centos:7
    #作者和邮箱
    MAINTAINER fl_6145<fl_6145@163.com>
    #使用 ADD 命令将 CentOS-Base.repo 拷贝到目标基础镜像的目录下
    ADD CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo
    #运行yum makecache生成缓存,便于查找
    RUN yum makecache
    #自定义环境变量
    ENV mypath /usr/local
    #登录容器后的落脚点
    WORKDIR $mypath
    #登录容器后执行的安装命令
    RUN yum -y install vim
    RUN yum -y install net-tools
    #向外暴露的端口
    EXPOSE 80
    #容器运行后执行的命令
    CMD echo "-----end----"
    CMD /bin/bash
  2. docker build 构建称为一个镜像, 最后的. 表示当前目录

    1
    docker build -f mydockerfile-centos -t mycentos:0.1 .
  3. docker run运行镜像

    1
    docker run -it mycentos:0.1
  4. docker push发布镜像(DockerHub 、阿里云仓库)

CMD/ENTRYPOINT

  • 都是指定一个容器启动时要运行的命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    vim dockerfile-test-cmd
    FROM centos
    CMD ["ls","-a"]


    # 构建镜像
    docker build -f dockerfile-test-cmd -t cmd-test:0.1 .
    # 运行镜像
    docker run cmd-test:0.1
  • Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换

    1
    2
    3
    4
    5
    6
    # cmd的情况下 -l 替换了CMD["ls","-l"]。
    # -l 不是命令所有报错
    docker run cmd-test:0.1 -l

    # 如果要ls -al
    docker run cmd-test:0.1 ls -al
  • docker run 之后的参数会被当做参数传递给 ENTRYPOINT,之后形成新的命令组合(追加)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 编写dockerfile文件
    vim dockerfile-test-entrypoint
    FROM centos
    ENTRYPOINT ["ls","-a"]

    # 构建镜像
    docker build -f dockerfile-test-entrypoint -t entrypoint-test:0.1 .
    # 运行镜像
    docker run entrypoint-test:0.1
    # 追加命令 ls -a -l
    docker run entrypoint-test:0.1 -l

发布自己的镜像

这里使用阿里云:创建镜像仓库 centos

  1. 登录阿里云Docker Registry
1
2
# 用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码。
$ docker login --username=***** registry.cn-hangzhou.aliyuncs.com
  1. 从Registry中拉取镜像
1
$ docker pull registry.cn-hangzhou.aliyuncs.com/fulsun/centos:[镜像版本号]
  1. 将镜像推送到Registry
1
2
3
4
$ docker login --username=***** registry.cn-hangzhou.aliyuncs.com
$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/fulsun/centos:[镜像版本号]
$ docker push registry.cn-hangzhou.aliyuncs.com/fulsun/centos:[镜像版本号]
请根据实际镜像信息替换示例中的[ImageId]和[镜像版本号]参数。

Docker网络

安装Docker时,它会自动创建三个网络,默认bridge网桥(创建容器默认连接到此网络)、 nonehost

1
2
3
4
5
6
# 查看docker网络
[root@docker-node01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
ac13ae33e4ad bridge bridge local
b91dae2a3e50 host host local
b07751b1a42c none null local

网络驱动模型

  1. bridge:Docker中默认的网络驱动模型,在启动容器时如果不指定则默认为此驱动类型;
  2. host:打破Docker容器与宿主机之间的网络隔离,直接使用宿主机的网络环境,该模型仅适用于Docker17.6及以上版本;
  3. overlay:可以连接多个docker守护进程或者满足集群服务之间的通信;适用于不同宿主机上的docker容器之间的通信;
  4. macvlan:可以为docker容器分配MAC地址,使其像真实的物理机一样运行;
  5. none:即禁用了网络驱动,需要自己手动自定义网络驱动配置;
  6. plugins:使用第三方网络驱动插件;

在使用docker run创建Docker容器时,可以用 --net 选项指定容器的网络模式,

Docker可以有以下4种网络模式

  1. host模式:使用 --net=host 指定。
  2. none模式:使用 --net=none 指定。
  3. bridge模式:使用 --net=bridge 指定,默认设置。
  4. container模式:使用 --net=container:NAME_or_ID 指定。
1
2
docker run -it -P --name tomcat01 --net=bridge tomcat  # 默认设置
docker run -it -P --name tomcat02 --net=none tomcat

容器通信问题

容器在默认情况下以隔离方式运行,它们完全不知道同一计算机上有其他进程或容器。 那么,如何使容器能够彼此通信? 答案就是网络连接。 如果两个容器在同一网络上,那么它们可彼此通信。 如果没在同一网络上,则没法通信。

  • 由于不同容器通过veth pair连接在虚拟网桥docker0上,所以容器之间可以通过IP互相通信,但是docker0无法通过容器名进行通信
  • 为了实现不同容器通过容器名或别名的互连,docker提供了以下几种:👇
    1. 在启动docker容器时加入--link参数,但是目前已经被废弃,废弃的主要原因是需要在连接的两个容器上都创建–link选项,当互连的容器数量较多时,操作的复杂度会显著增加;
    2. 启动docker容器后进入容器并修改 /etc/hosts 配置文件(本地DNS解析),缺点是手动配置较为繁杂;
    3. 用户自定义bridge网桥,这是目前解决此类问题的主要方法,提供更好的隔离效果和更好的互通性(更好的隔离效果是针对外界网络,而更好的互通性则是指同一bridge下的不同容器之间),用户自定义bridge在容器之间提供了自动DNS解析。

Docker0

  • bridge网络代表docker0,所有Docker安装中存在的网络。除非你使用docker run --network=<NETWORK>选项指定,否则Docker守护程序默认将容器连接到此网络

    查看默认bridge网桥(docker0)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    [root@docker-node01 ~]# docker network inspect ac13ae33e4ad
    [
    {
    "Name": "bridge",
    "Id": "ac13ae33e4ad0fb6e7f0f0b0323d1c02b16f7bd01cd388c58be8bbecf3f873ca",
    "Created": "2023-09-09T16:45:29.969100643+08:00",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
    "Driver": "default",
    "Options": null,
    "Config": [
    {
    "Subnet": "172.17.0.0/16",
    "Gateway": "172.17.0.1"
    }
    ]
    },
    "Internal": false,
    "Attachable": false,
    "Ingress": false,
    "ConfigFrom": {
    "Network": ""
    },
    "ConfigOnly": false,
    "Containers": {
    "1472199ffd7beb82b3b3d9f2dfd025eddfc3a2f22c568a7dcfbf801bc75f084c": {
    "Name": "tomcat01",
    "EndpointID": "4efab10676b37e424e2c184beca375fdfc91573662a97871be7da5e91db0999c",
    "MacAddress": "02:42:ac:11:00:02",
    "IPv4Address": "172.17.0.2/16",
    "IPv6Address": ""
    }
    },
    "Options": {
    "com.docker.network.bridge.default_bridge": "true",
    "com.docker.network.bridge.enable_icc": "true",
    "com.docker.network.bridge.enable_ip_masquerade": "true",
    "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
    "com.docker.network.bridge.name": "docker0",
    "com.docker.network.driver.mtu": "1500"
    },
    "Labels": {}
    }
    ]
  • 启动时,docker默认的bridge网桥,docker0给容器服务自动分配ip

    1
    2
    docker run -it --name centos01  centos /bin/bash
    docker run -it --name centos02 centos /bin/bash
  • 查看运行容器的ip docker inspect 容器ID | grep IPAddress

  • 查看查看宿主机网络,发现docker分配了 77、79 的ip地址

  • 此时,进入centos01容器,ping centos02容器的ip可以ping通,但是ping不通容器名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    [root@0043c61be833 /]#  ping 172.17.0.3
    PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
    64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.128 ms

    # docker0不支持容器名连接访问
    [root@0043c61be833 /]# ping centos02
    ping: centos02: Name or service not known

    # 查看容器的网卡 发现是76
    [root@0043c61be833 /]# ip addr
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    valid_lft forever preferred_lft forever
    76: eth0@if77: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
    valid_lft forever preferred_lft forever

容器通信原理

  • 只要我们安装了docker,就会有一个网卡docker0(相当于一个路由器),每启动一个docker容器,docker都会给docker容器分配一个ip(连接路由器的终端,同一网段下终端可以互相通信),
  • 通过evth-pair技术实现,evth-pair就是一对虚拟设备接口,他们都是成对出现的,一端连着协议,一端彼此相连,evth-pair充当一个桥梁,连接各种虚拟网络设备。
  • Docker网络使用的是Linux桥接,宿主机是docker容器的网桥,docker0最多分配65535个
  • 删除容器之后,虚拟网卡就自动消失了。(虚拟网卡传递效率高!
  • 启动容器不设置网络,容器ip由docker0自动分配情况下,容器间的通信,要经过evth-pair技术实现,并不是直连的。(跟计算机网络通信类似,分层模型,TCP/IP协议数据报封装解封装)

上面docker0不支持容器名连接访问,容器通信只可以通过容器ip通信,docker也无法保证容器重启后的IP地址不变,可通过--link建立连接别名进行互联(官方不推荐使用)

原理分析:

  • 运行容器时,指定参数link,使得源容器与被链接的容器可以进行相互通信,并且接受的容器可以获得源容器的一些数据,比如:环境变量。
  • 与/etc/hosts中的主机条目不同,如果重新启动源容器,则不会自动更新存储在环境变量中的IP地址。我们建议使用主机条目 /etc/hosts来解析链接容器的IP地址。
  • 除了环境变量之外,Docker还将源容器的主机条目添加到/etc/hosts文件中。(本质上就是通过 –link 参数,自动的给容器添加 hosts 配置)

建立连接步骤:✨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启动tomcat01
docker run -it -d -P --name tomcat01 tomcat
# ping不通
docker run -it -d -P --name tomcat02 tomcat

docker exec -it tomcat02 bash
root@c294a750a97d:/usr/local/tomcat# cat /etc/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.3 c294a750a97d

用tomcat03 ping tomcat02 可以ping通

用tomcat02 ping tomcat03 ping不通

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# tomcat02 容器link 到 tomcat03 上
docker run -it -d -P --name tomcat03 --link tomcat02 tomcat
docker exec -it tomcat03 cat /etc/hosts

[root@docker-node01 ~]# docker exec -it tomcat03 cat /etc/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
#✨--link命令配置生成的条目✨
172.17.0.3 tomcat02 c294a750a97d
172.17.0.4 e256178947a0

自定义网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Usage:  docker network COMMAND

Manage networks

Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks

Run 'docker network COMMAND --help' for more information on a command.

查看所有的docker网络

1
2
3
4
5
[root@docker-node01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
ac13ae33e4ad bridge bridge local
b91dae2a3e50 host host local
b07751b1a42c none null local