Docker

- \*\*docker是什么？\*\*[官方文档](https://docs.docker.com/)   [中文学习](https://yeasy.gitbook.io/docker\_practice/introduction/what)

    Docker中文为码头工人，是一个虚拟环境容器，可以将你的开发环境、代码、配置文件等一并打包到这个容器中，并发布和应用到任意平台中。比如，你在本地用Python开发网站后台，开发测试完成后，就可以将Python3及其依赖包、Flask及其各种插件、Mysql、Nginx等打包到一个容器中，然后部署到任意你想部署到的环境。

    ---

- \*\*docker中的三个重要概念简介\*\*

    - \*\*镜像 (Image)\*\*：类似于虚拟机中的镜像，是一个包含有文件系统的面向Docker引擎的只读模板。任何应用程序运行都需要环境，而镜像就是用来提供这种运行环境的。例如一个Ubuntu镜像就是一个包含Ubuntu操作系统环境的模板，同理在该镜像上装上Apache软件，就可以称为Apache镜像。

    - \*\*容器 (Container)\*\*：类似于一个轻量级的沙盒，可以将其看作一个极简的Linux系统环境（包括root权限、进程空间、用户空间和网络空间等），以及运行在其中的应用程序。Docker引擎利用容器来运行、隔离各个应用。容器是镜像创建的应用实例，可以创建、启动、停止、删除容器，各个容器之间是是相互隔离的，互不影响。注意：镜像本身是只读的，容器从镜像启动时，Docker在镜像的上层创建一个可写层，镜像本身不变。

    - \*\*仓库 (Repository)\*\*：类似于代码仓库，这里是镜像仓库，是Docker用来集中存放镜像文件的地方。注意与注册服务器（Registry）的区别：注册服务器是存放仓库的地方，一般会有多个仓库；而仓库是存放镜像的地方，一般每个仓库存放一类镜像，每个镜像利用tag进行区分，比如Ubuntu仓库存放有多个版本（12.04、14.04等）的Ubuntu镜像。

    ---

- Docker的安装与卸载

    详见 [Install Docker](https://docs.docker.com/engine/install/)

    安装成功后可以在命令行输入docker version来测试是否安装成功

    ---

- 提问部分(Answer by ChatGPT)

    <aside>

    ❓ \*\*\*疑问：Docker是否和虚拟机是同类产品\*\*\*

    Docker和虚拟机是两种不同的技术和应用。虽然它们都可以用于创建和运行隔离的计算环境，但其实现方式和使用方式有所不同。

    虚拟机是一种软件仿真技术，它在物理计算机上创建一个完整的虚拟计算机，包括虚拟的硬件资源和操作系统。虚拟机管理程序（如VMware或VirtualBox）会加载虚拟机镜像，然后虚拟机在其中运行一个完整的操作系统实例。因为虚拟机模拟了完整的硬件和操作系统，所以每个虚拟机都是独立和隔离的，可以运行不同的操作系统，像是在物理计算机上运行多个独立的计算机。

    而Docker是一种容器化技术，它利用操作系统级别的虚拟化来隔离应用程序的运行环境。Docker使用Docker容器来打包应用程序及其所有依赖，通过共享主机操作系统的内核资源，实现了高效的应用程序隔离和共享。与虚拟机不同，Docker容器只包含应用程序和其依赖，而不需要运行完整的操作系统。这使得Docker容器相对较轻量、启动速度快，并且可以更高效地利用计算资源。

    因此，尽管Docker和虚拟机都用于创建隔离的计算环境，但它们的实现方式和使用方式有着本质上的不同。虚拟机提供了更高度的隔离和灵活性，适用于运行不同操作系统的应用程序，而Docker容器则更轻量、更高效，适用于部署和管理应用程序及其依赖。

    </aside>

    <aside>

    ❓ \*\*\*Docker打包之后产生的结果是什么呢\*\*\*

    当使用Docker进行打包和构建后，最终会生成一个称为Docker镜像的结果。Docker镜像是一个轻量级、可执行的文件，其中包含了应用程序及其所有依赖的文件系统和配置信息。

    Docker镜像是只读的，可重复部署的，并且可在不同的环境中使用。它包含了应用程序的代码、运行时依赖、系统库、配置文件和其他必要的组件。通过使用Docker镜像，可以实现应用程序在不同的机器和环境中的一致性。

    Docker镜像是分层的，每一层代表着一个文件系统的更改。这种分层结构使得镜像可以共享和重用底层的文件系统层。当我们构建和修改镜像时，只需要添加、删除或修改相应的层，而无需重新构建整个镜像。

    使用Docker镜像，可以通过Docker容器来创建运行实例。容器是基于镜像运行的一个独立的运行时实体，可以启动、停止、暂停、删除和管理。容器是轻量级、隔离的运行环境，具有自己的文件系统、进程空间和网络配置，可以在不同的主机上运行，并且与其他容器和主机之间相互隔离。

    因此，通过Docker打包后产生的结果就是一个Docker镜像，它是应用程序及其依赖的可执行文件，方便进行部署和管理。

    </aside>

    <aside>

    ❓ \*\*\*那我可以理解为使用docker打包相对于普通的打包只不过是在打包过程中也将运行的环境与依赖等也一同打包了进去，而且运行的时候也需要docker环境来运行么\*\*\*

    是的，你的理解是正确的。

    使用传统的打包方法，通常只会打包应用程序的源代码和必要的文件，而不包括完整的运行时环境和依赖项。这意味着部署该应用程序时，需要在目标环境中手动配置和安装所需的运行时依赖。这可能会导致环境配置的不一致，依赖冲突或版本不匹配等问题。

    而使用Docker进行打包时，除了应用程序本身之外，还将应用程序的运行时依赖、系统库和配置文件等，一同打包进了Docker镜像中。这样，创建的Docker镜像就成为了一个独立的、自包含的运行环境，可以在任何支持Docker的主机上运行，而不需要手动安装和配置依赖项。

    当需要运行打包好的Docker镜像时，确实需要在运行环境中安装和配置Docker引擎。Docker引擎是Docker的运行时环境，负责管理和运行Docker镜像。通过使用Docker命令行工具或Docker API，可以创建、启动和管理Docker容器，从而在Docker环境中运行打包好的应用程序。

    值得注意的是，Docker的跨平台性使得应用程序可以在不同的操作系统和主机上进行部署和运行。无论是在开发环境、测试环境还是生产环境，只要主机支持Docker，就可以轻松地运行和管理应用程序。

    </aside>

    ---

- \*\*镜像(Image)\*\*

    - 我们都知道，操作系统分为 \*\*内核\*\* 和 \*\*用户空间\*\*。对于 `Linux` 而言，内核启动后，会挂载 `root` 文件系统为其提供用户空间支持。而 \*\*Docker 镜像\*\*（`Image`），就相当于是一个 `root` 文件系统。比如官方镜像 `ubuntu:18.04` 就包含了完整的一套 Ubuntu 18.04 最小系统的 `root` 文件系统。

    - \*\*Docker 镜像\*\* 是一个特殊的文件系统，除了提供容器运行时所需的程序、库、资源、配置等文件外，还包含了一些为运行时准备的一些配置参数（如匿名卷、环境变量、用户等）。镜像 \*\*不包含\*\* 任何动态数据，其内容在构建之后也不会被改变。

    - \*\*\*镜像的分层存储\*\*\*

        - 因为镜像包含操作系统完整的 `root` 文件系统，其体积往往是庞大的，因此在 Docker 设计时，就充分利用 [Union FS](https://en.wikipedia.org/wiki/Union\_mount) 的技术，将其设计为分层存储的架构。所以严格来说，镜像并非是像一个 `ISO` 那样的打包文件，镜像只是一个虚拟的概念，其实际体现并非由一个文件组成，而是由一组文件系统组成，或者说，由多层文件系统联合组成。

        - 镜像构建时，会一层层构建，前一层是后一层的基础。每一层构建完就不会再发生改变，后一层上的任何改变只发生在自己这一层。比如，删除前一层文件的操作，实际不是真的删除前一层的文件，而是仅在当前层标记为该文件已删除。在最终容器运行的时候，虽然不会看到这个文件，但是实际上该文件会一直跟随镜像。因此，在构建镜像的时候，需要额外小心，每一层尽量只包含该层需要添加的东西，任何额外的东西应该在该层构建结束前清理掉。

        - 分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层，然后进一步添加新的层，以定制自己所需的内容，构建新的镜像。

    ---

- \*\*容器(Container)\*\*

    - 镜像（`Image`）和容器（`Container`）的关系，就像是面向对象程序设计中的 `类` 和 `实例` 一样，镜像是静态的定义，容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

    - 容器的实质是进程，但与直接在宿主执行的进程不同，容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux\_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间，甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里，使用起来，就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性，很多人初学 Docker 时常常会混淆容器和虚拟机。

    - 前面讲过镜像使用的是分层存储，容器也是如此。每一个容器运行时，是以镜像为基础层，在其上创建一个当前容器的存储层，我们可以称这个为容器运行时读写而准备的存储层为 \*\*容器存储层\*\*。

    - 容器存储层的生存周期和容器一样，容器消亡时，容器存储层也随之消亡。因此，任何保存于容器存储层的信息都会随容器删除而丢失。

    - 按照 Docker 最佳实践的要求，容器不应该向其存储层内写入任何数据，容器存储层要保持无状态化。所有的文件写入操作，都应该使用 [数据卷（Volume）](notion://www.notion.so/docker\_practice/data\_management/volume)、或者 [绑定宿主目录](notion://www.notion.so/docker\_practice/data\_management/bind-mounts)，在这些位置的读写会跳过容器存储层，直接对宿主（或网络存储）发生读写，其性能和稳定性更高。

    - 数据卷的生存周期独立于容器，容器消亡，数据卷不会消亡。因此，使用数据卷后，容器删除或者重新运行之后，数据却不会丢失。

    ---

- \*\*仓库(Repository)\*\*

    - 镜像构建完成后，可以很容易的在当前宿主机上运行，但是，如果需要在其它服务器上使用这个镜像，我们就需要一个集中的存储、分发镜像的服务，[Docker Registry](notion://www.notion.so/docker\_practice/repository/registry) 就是这样的服务。

    - 一个 \*\*Docker Registry\*\* 中可以包含多个 \*\*仓库\*\*（`Repository`）；每个仓库可以包含多个 \*\*标签\*\*（`Tag`）；每个标签对应一个镜像。

    - 通常，一个仓库会包含同一个软件不同版本的镜像，而标签就常用于对应该软件的各个版本。我们可以通过 `<仓库名>:<标签>` 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签，将以 `latest` 作为默认标签。

    - 以 [Ubuntu 镜像](https://hub.docker.com/\_/ubuntu) 为例，`ubuntu` 是仓库的名字，其内包含有不同的版本标签，如，`16.04`, `18.04`。我们可以通过 `ubuntu:16.04`，或者 `ubuntu:18.04` 来具体指定所需哪个版本的镜像。如果忽略了标签，比如 `ubuntu`，那将视为 `ubuntu:latest`。

    - 仓库名经常以 \*两段式路径\* 形式出现，比如 `jwilder/nginx-proxy`，前者往往意味着 Docker Registry 多用户环境下的用户名，后者则往往是对应的软件名。但这并非绝对，取决于所使用的具体 Docker Registry 的软件或服务。

    - \*\*\*Docker Registry 公开服务\*\*\*

        - Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像，并可能提供收费服务供用户管理私有镜像。

        - 最常使用的 Registry 公开服务是官方的 [Docker Hub](https://hub.docker.com/)，这也是默认的 Registry，并拥有大量的高质量的 [官方镜像](https://hub.docker.com/search?q=&type=image&image\_filter=official)。除此以外，还有 Red Hat 的 [Quay.io](https://quay.io/repository/)；Google 的 [Google Container Registry](https://cloud.google.com/container-registry/)，[Kubernetes](https://kubernetes.io/) 的镜像使用的就是这个服务；代码托管平台 [GitHub](https://github.com/) 推出的 [ghcr.io](https://docs.github.com/cn/packages/working-with-a-github-packages-registry/working-with-the-container-registry)。

        - 由于某些原因，在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务（`Registry Mirror`），这些镜像服务被称为 \*\*加速器\*\*。常见的有 [阿里云加速器](https://www.aliyun.com/product/acr?source=5176.11533457&userCode=8lx5zmtu)、[DaoCloud 加速器](https://www.daocloud.io/mirror#accelerator-doc) 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像，比直接从 Docker Hub 下载速度会提高很多。在 [安装 Docker](notion://www.notion.so/docker\_practice/install/mirror) 一节中有详细的配置方法。

        - 国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 [网易云镜像服务](https://c.163.com/hub#/m/library/)、[DaoCloud 镜像市场](https://hub.daocloud.io/)、[阿里云镜像库](https://www.aliyun.com/product/acr?source=5176.11533457&userCode=8lx5zmtu) 等。

    - \*\*\*私有 Docker Registry\*\*\*

        - 除了使用公开服务外，用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 [Docker Registry](https://hub.docker.com/\_/registry/) 镜像，可以直接使用做为私有 Registry 服务。在 [私有仓库](notion://www.notion.so/docker\_practice/repository/registry) 一节中，会有进一步的搭建私有 Registry 服务的讲解。

        - 开源的 Docker Registry 镜像只提供了 [Docker Registry API](https://docs.docker.com/registry/spec/api/) 的服务端实现，足以支持 `docker` 命令，不影响使用。但不包含图形界面，以及镜像维护、用户管理、访问控制等高级功能。

        - 除了官方的 Docker Registry 外，还有第三方软件实现了 Docker Registry API，甚至提供了用户界面以及一些高级功能。比如，[Harbor](https://github.com/goharbor/harbor) 和 [Sonatype Nexus](notion://www.notion.so/docker\_practice/repository/nexus3\_registry)。

    ---

- 安装docker

    [macOS](https://yeasy.gitbook.io/docker\_practice/install/mac)

    ---

- 使用镜像

    - 获取镜像

        ```bash

        $ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

        ```

    - 列出镜像

        ```bash

        $ docker image ls

        ```

    - 删除本地镜像

        ```bash

        $ docker image rm [选项] <镜像1> [<镜像2> ...]

        ```

    - 利用commit理解镜像构成

    - 利用 dockerfile 定制镜像

        - Dockerfile 是一个文本文件，其内包含了一条条的 \*\*指令(Instruction)\*\*，每一条指令构建一层，因此每一条指令的内容，就是描述该层应当如何构建。

        - FROM 指定的基础镜像

        - RUN 执行命令

    - 构建镜像

        - 在dockerfile文件所在目录执行

        - docker  build  [选项]  < 上下文路径/URL/- >

    - 其余内容略

    ---

- \*\*操作容器\*\*

    - \*\*启动\*\*

        - 启动容器有两种方式，一种是基于镜像新建一个容器并启动，另外一个是将在终止状态（`exited`）的容器重新启动。因为 Docker 的容器实在太轻量级了，很多时候用户都是随时删除和新创建容器。

        - 新建并启动

            - 所需要的命令主要为 `docker run`。

            例如，下面的命令输出一个 “Hello World”，之后终止容器。

                ```bash

                $ docker run ubuntu:18.04 /bin/echo 'Hello world'

                Hello world

                ```

            - 这跟在本地直接执行 `/bin/echo 'hello world'` 几乎感觉不出任何区别。

            下面的命令则启动一个 bash 终端，允许用户进行交互。

                ```bash

                $ docker run -t -i ubuntu:18.04 /bin/bash

                root@af8bae53bdd3:/#

                ```

            - 其中，`-t` 选项让Docker分配一个伪终端（pseudo-tty）并绑定到容器的标准输入上， `-i` 则让容器的标准输入保持打开。

            在交互模式下，用户可以通过所创建的终端来输入命令，例如

                ```bash

                root@af8bae53bdd3:/# pwd

                /

                root@af8bae53bdd3:/# ls

                bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

                ```

            - 当利用 `docker run` 来创建容器时，Docker 在后台运行的标准操作包括：

                - 检查本地是否存在指定的镜像，不存在就从 [registry](notion://www.notion.so/docker\_practice/repository) 下载

                - 利用镜像创建并启动一个容器

                - 分配一个文件系统，并在只读的镜像层外面挂载一层可读写层

                - 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去

                - 从地址池配置一个 ip 地址给容器

                - 执行用户指定的应用程序

                - 执行完毕后容器被终止

                ---

        - 启动已终止容器

            - 可以利用 `docker container start` 命令，直接将一个已经终止（`exited`）的容器启动运行。

            容器的核心为所执行的应用程序，所需要的资源都是应用程序运行所必需的。除此之外，并没有其它的资源。可以在伪终端中利用 `ps` 或 `top` 来查看进程信息。

                ```bash

                root@ba267838cc1b:/# ps

                PID      TTY            TIME     CMD

                  1        ?        00:00:00     bash

                 11        ?        00:00:00     ps

                ```

            - 可见，容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高，是货真价实的轻量级虚拟化。

        ---

    - \*\*守护态运行：\*\*

        [守护态运行](https://yeasy.gitbook.io/docker\_practice/container/daemon)

        ---

    - \*\*终止：\*\*

        ```bash

        docker container stop

        ```

        ---

    - \*\*进入容器：\*\*

        [进入容器](https://yeasy.gitbook.io/docker\_practice/container/attach\_exec)

        ---

    - 导入和导出

        [导出和导入](https://yeasy.gitbook.io/docker\_practice/container/import\_export)

        ---

    - 删除容器

        [删除](https://yeasy.gitbook.io/docker\_practice/container/rm)

    ---

- 访问仓库：[https://yeasy.gitbook.io/docker\_practice/repository](https://yeasy.gitbook.io/docker\_practice/repository)

- 数据管理：[https://yeasy.gitbook.io/docker\_practice/data\_management](https://yeasy.gitbook.io/docker\_practice/data\_management)

- 使用网络：[https://yeasy.gitbook.io/docker\_practice/network](https://yeasy.gitbook.io/docker\_practice/network)

- 高级网络配置：[https://yeasy.gitbook.io/docker\_practice/advanced\_network](https://yeasy.gitbook.io/docker\_practice/advanced\_network)

- …