> [!info] 章节导航
> [[docker学习路径|Docker 学习索引]] · [[03-快速上手|上一章]] · [[05-Dockerfile 构建镜像|下一章]]

## 4. 运行与管理容器

### 4.1 发布端口（Publishing ports）



您就会明白容器为应用程序的每个组件提供了隔离的进程。每个组件（React 前端、Python API 和 Postgres 数据库）都在其自己的沙盒环境中运行，与主机上的所有其他组件完全隔离。这种隔离对于安全性和管理依赖项非常有益，但也意味着您无法直接访问它们。

通过设置转发规则，发布端口可以突破一些网络隔离。例如，你可以指定将主机端口 `8080` 上的请求转发到容器的端口 `80` 。在创建容器期间，使用 `docker run` 命令的 `-p` （或 `--publish` ）标志发布端口。语法如下：



```console
 docker run -d -p HOST_PORT:CONTAINER_PORT nginx
```

- `HOST_PORT`: The port number on your host machine where you want to receive traffic
  `HOST_PORT` ：主机上您想要接收流量的端口号
- `CONTAINER_PORT`: The port number within the container that's listening for connections
  `CONTAINER_PORT` ：容器内正在监听连接的端口号

For example, to publish the container's port `80` to host port `8080`:
例如，将容器的端口 `80` 发布到主机端口 `8080` ：



```console
 docker run -d -p 8080:80 nginx
```

Now, any traffic sent to port `8080` on your host machine will be forwarded to port `80` within the container.
现在，发送到主机上端口 `8080` 的任何流量都将被转发到容器内的端口 `80` 。

> 
>
> 端口发布后，默认情况下会发布到所有网络接口。这意味着到达您计算机的任何流量都可以访问已发布的应用程序。请注意不要发布数据库或任何敏感信息。 [在此处了解有关已发布端口的更多信息 ](https://docs.docker.com/engine/network/#published-ports)。



#### 发布到临时端口

At times, you may want to simply publish the port but don’t care which host port is used. In these cases, you can let Docker pick the port for you. To do so, simply omit the `HOST_PORT` configuration.
有时，您可能只想发布端口，而不关心使用哪个主机端口。在这种情况下，您可以让 Docker 为您选择端口。为此，只需省略 `HOST_PORT` 配置即可。

For example, the following command will publish the container’s port `80` onto an ephemeral port on the host:
例如，以下命令将容器的端口 `80` 发布到主机上的临时端口：



```console
 docker run -p 80 nginx
```

Once the container is running, using `docker ps` will show you the port that was chosen:
一旦容器运行，使用 `docker ps` 将显示所选的端口：



```console
docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS          PORTS                    NAMES
a527355c9c53   nginx         "/docker-entrypoint.…"   4 seconds ago    Up 3 seconds    0.0.0.0:54772->80/tcp    romantic_williamson
```

In this example, the app is exposed on the host at port `54772`.
在此示例中，应用程序在主机的端口 `54772` 上公开。

#### 发布所有端口

When creating a container image, the `EXPOSE` instruction is used to indicate the packaged application will use the specified port. These ports aren't published by default.
创建容器镜像时， `EXPOSE` 指令用于指示打包的应用程序将使用指定的端口。默认情况下，这些端口不会发布。

With the `-P` or `--publish-all` flag, you can automatically publish all exposed ports to ephemeral ports. This is quite useful when you’re trying to avoid port conflicts in development or testing environments.
使用 `-P` 或 `--publish-all` 标志，您可以自动将所有公开的端口发布到临时端口。当您尝试避免开发或测试环境中的端口冲突时，这非常有用。

For example, the following command will publish all of the exposed ports configured by the image:
例如，以下命令将发布映像配置的所有公开端口：



```console
 docker run -P nginx
```



此示例将使用 Docker Compose 启动相同的应用程序：

1. Create a new directory and inside that directory, create a `compose.yaml` file with the following contents:
   创建一个新目录，并在该目录内创建一个包含以下内容的 `compose.yaml` 文件：

   

   ```yaml
   services:
     app:
       image: docker/welcome-to-docker
       ports:
         - 8080:80
   ```

   The `ports` configuration accepts a few different forms of syntax for the port definition. In this case, you’re using the same `HOST_PORT:CONTAINER_PORT` used in the `docker run` command.
   `ports` 配置接受几种不同的端口定义语法。在本例中，您使用的 `HOST_PORT:CONTAINER_PORT` 与 `docker run` 命令中的相同。

2. Open a terminal and navigate to the directory you created in the previous step.
   打开终端并导航到您在上一步中创建的目录。

3. Use the `docker compose up` command to start the application.
   使用 `docker compose up` 命令启动应用程序。

4. Open your browser to [http://localhost:8080](http://localhost:8080/).
   打开浏览器访问 [http://localhost:8080](http://localhost:8080/) 。



例如，如果您有一个现有的数据库容器监听标准端口，并且您想要运行同一数据库容器的新实例，那么您可能需要更改新容器监听的端口设置，以使其不与现有容器冲突。有时，如果程序需要更多资源来处理繁重的工作负载，您可能需要增加容器可用的内存，或者设置环境变量以提供程序正常运行所需的特定配置详细信息。

The `docker run` command offers a powerful way to override these defaults and tailor the container's behavior to your liking. The command offers several flags that let you to customize container behavior on the fly.
`docker run` 命令提供了一种强大的方法来覆盖这些默认设置，并根据您的喜好定制容器的行为。该命令提供了几个标志，可让您动态自定义容器行为。

Here's a few ways you can achieve this.
您可以通过以下几种方法实现此目的。



有时，您可能希望使用单独的数据库实例进行开发和测试。在同一端口上运行这些数据库实例可能会发生冲突。您可以在 `docker run` 中使用 `-p` 选项将容器端口映射到主机端口，从而可以同时运行容器的多个实例而不会产生任何冲突。



```console
 docker run -d -p HOST_PORT:CONTAINER_PORT postgres
```

### 4.2 设置环境变量

This option sets an environment variable `foo` inside the container with the value `bar`.
此选项在容器内设置一个环境变量 `foo` ，其值为 `bar` 。



```console
 docker run -e foo=bar postgres env
```

You will see output like the following:
您将看到如下输出：



```console
HOSTNAME=2042f2e6ebe4
foo=bar
```



`.env` 文件可以方便地为 Docker 容器设置环境变量，避免命令行中出现大量的 `-e` 参数。要使用 `.env` 文件，可以在 `docker run` 命令中传递 `--env-file` 选项。



```console
 docker run --env-file .env postgres env
```

### 4.3 数据持久化与文件共享

容器启动时，会使用镜像提供的文件和配置。每个容器都可以创建、修改和删除文件，且不会影响其他容器。删除容器时，这些文件更改也会被删除。

While this ephemeral nature of containers is great, it poses a challenge when you want to persist the data. For example, if you restart a database container, you might not want to start with an empty database. So, how do you persist files?
虽然容器的这种短暂性很棒，但在持久化数据时却带来了挑战。例如，重启数据库容器时，您可能不希望数据库空着。那么，如何持久化文件呢？

#### [容器卷（Container volumes）](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#container-volumes)

Volumes are a storage mechanism that provide the ability to persist data beyond the lifecycle of an individual container. Think of it like providing a shortcut or symlink from inside the container to outside the container.
卷是一种存储机制，它能够在单个容器的生命周期之外持久化数据。可以将其想象成提供从容器内部到容器外部的快捷方式或符号链接。

As an example, imagine you create a volume named `log-data`.
例如，假设您创建一个名为 `log-data` 的卷。



```console
 docker volume create log-data
```

When starting a container with the following command, the volume will be mounted (or attached) into the container at `/logs`:
当使用以下命令启动容器时，卷将被挂载（或附加）到容器的 `/logs` 中：



```console
 docker run -d -p 80:80 -v log-data:/logs docker/welcome-to-docker
```

If the volume `log-data` doesn't exist, Docker will automatically create it for you.
如果卷 `log-data` 不存在，Docker 将自动为您创建它。

When the container runs, all files it writes into the `/logs` folder will be saved in this volume, outside of the container. If you delete the container and start a new container using the same volume, the files will still be there.
容器运行时，它写入 `/logs` 文件夹的所有文件都将保存在此卷中，位于容器外部。如果您删除该容器并使用同一卷启动新容器，这些文件仍将保留在那里。

> **Sharing files using volumes
> 使用卷共享文件**
>
> You can attach the same volume to multiple containers to share files between containers. This might be helpful in scenarios such as log aggregation, data pipelines, or other event-driven applications.
> 您可以将同一个卷附加到多个容器，以便在容器之间共享文件。这在日志聚合、数据管道或其他事件驱动型应用程序等场景中可能很有用。

#### [管理卷（Managing volumes）](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#managing-volumes)

Volumes have their own lifecycle beyond that of containers and can grow quite large depending on the type of data and applications you’re using. The following commands will be helpful to manage volumes:
卷的生命周期与容器不同，并且会根据所使用的数据和应用程序的类型而增长到相当大的规模。以下命令将有助于管理卷：

- `docker volume ls` - list all volumes
  `docker volume ls` 列出所有卷
- `docker volume rm <volume-name-or-id>` - remove a volume (only works when the volume is not attached to any containers)
  `docker volume rm <volume-name-or-id>` - 删除卷（仅当卷未附加到任何容器时才有效）
- `docker volume prune` - remove all unused (unattached) volumes
  `docker volume prune` - 删除所有未使用的（未附加的）卷



每个容器都具备其运行所需的一切，无需依赖主机上任何预安装的依赖项。由于容器独立运行，它们对主机和其他容器的影响极小。这种隔离有一个主要优势：容器可以最大限度地减少与主机系统和其他容器的冲突。然而，这种隔离也意味着容器默认无法直接访问主机上的数据。

Consider a scenario where you have a web application container that requires access to configuration settings stored in a file on your host system. This file may contain sensitive data such as database credentials or API keys. Storing such sensitive information directly within the container image poses security risks, especially during image sharing. To address this challenge, Docker offers storage options that bridge the gap between container isolation and your host machine's data.
假设您有一个 Web 应用程序容器，它需要访问存储在主机系统上的文件中的配置设置。该文件可能包含敏感数据，例如数据库凭据或 API 密钥。将这些敏感信息直接存储在容器镜像中会带来安全风险，尤其是在镜像共享期间。为了应对这一挑战，Docker 提供了一些存储选项，可以弥合容器隔离与主机数据之间的差距。

Docker offers two primary storage options for persisting data and sharing files between the host machine and containers: volumes and bind mounts.
Docker 提供了两种主要存储选项，用于在主机和容器之间持久保存数据和共享文件：卷和绑定挂载。

#### [卷与绑定挂载](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#volume-versus-bind-mounts)

If you want to ensure that data generated or modified inside the container persists even after the container stops running, you would opt for a volume. See [Persisting container data](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/) to learn more about volumes and their use cases.
如果您希望确保容器内部生成或修改的数据在容器停止运行后仍然保留，则可以选择使用卷。请参阅 [保存容器数据](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/)以了解有关卷及其用例的更多信息。

If you have specific files or directories on your host system that you want to directly share with your container, like configuration files or development code, then you would use a bind mount. It's like opening a direct portal between your host and container for sharing. Bind mounts are ideal for development environments where real-time file access and sharing between the host and container are crucial.
如果您希望主机系统上有特定的文件或目录（例如配置文件或开发代码）直接与容器共享，则可以使用绑定挂载。这就像在主机和容器之间打开一个直接的门户进行共享。绑定挂载非常适合开发环境，因为主机和容器之间的实时文件访问和共享至关重要。



#### [在主机和容器之间共享文件](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#sharing-files-between-a-host-and-container)

Both `-v` (or `--volume`) and `--mount` flags used with the `docker run` command let you share files or directories between your local machine (host) and a Docker container. However, there are some key differences in their behavior and usage.
`docker run` 命令中使用的 `-v` （或 `--volume` ）和 `--mount` 参数都允许你在本地计算机（主机）和 Docker 容器之间共享文件或目录。然而，它们的行为和用法存在一些关键差异。

The `-v` flag is simpler and more convenient for basic volume or bind mount operations. If the host location doesn’t exist when using `-v` or `--volume`, a directory will be automatically created.
`-v` 标志对于基本卷或绑定挂载操作来说​​更简单、更方便。如果使用 `-v` 或 `--volume` 时主机位置不存在，则会自动创建一个目录。

Imagine you're a developer working on a project. You have a source directory on your development machine where your code resides. When you compile or build your code, the generated artifacts (compiled code, executables, images, etc.) are saved in a separate subdirectory within your source directory. In the following examples, this subdirectory is `/HOST/PATH`. Now you want these build artifacts to be accessible within a Docker container running your application. Additionally, you want the container to automatically access the latest build artifacts whenever you rebuild your code.
假设您是一位正在开发项目的开发人员。您的开发机器上有一个源目录，用于存放代码。当您编译或构建代码时，生成的构件（编译后的代码、可执行文件、镜像等）会保存在源目录中一个单独的子目录中。在以下示例中，此子目录为 `/HOST/PATH` 。现在，您希望这些构建构件能够在运行应用程序的 Docker 容器中访问。此外，您还希望容器在您重新构建代码时自动访问最新的构建构件。

Here's a way to use `docker run` to start a container using a bind mount and map it to the container file location.
这是一种使用 `docker run` 通过绑定挂载启动容器并将其映射到容器文件位置的方法。



```console
 docker run -v /HOST/PATH:/CONTAINER/PATH -it nginx
```

The `--mount` flag offers more advanced features and granular control, making it suitable for complex mount scenarios or production deployments. If you use `--mount` to bind-mount a file or directory that doesn't yet exist on the Docker host, the `docker run` command doesn't automatically create it for you but generates an error.
`--mount` 标志提供了更高级的功能和更精细的控制，使其适用于复杂的挂载场景或生产部署。如果您使用 `--mount` 绑定挂载 Docker 主机上尚不存在的文件或目录， `docker run` 命令不会自动为您创建它，而是会生成错误。



```console
 docker run --mount type=bind,source=/HOST/PATH,target=/CONTAINER/PATH,readonly nginx
```

> Note 笔记
>
> Docker recommends using the `--mount` syntax instead of `-v`. It provides better control over the mounting process and avoids potential issues with missing directories.
> Docker 建议使用 `--mount` 语法而不是 `-v` 。这样可以更好地控制挂载过程，并避免目录丢失的潜在问题。



#### [Docker 访问主机文件的权限](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#file-permissions-for-docker-access-to-host-files)

When using bind mounts, it's crucial to ensure that Docker has the necessary permissions to access the host directory. To grant read/write access, you can use the `:ro` flag (read-only) or `:rw` (read-write) with the `-v` or `--mount` flag during container creation. For example, the following command grants read-write access permission.
使用绑定挂载时，务必确保 Docker 拥有访问主机目录所需的权限。要授予读/写访问权限，您可以在创建容器时将 `:ro` 标志（只读）或 `:rw` 标志（读写）与 `-v` 或 `--mount` 标志一起使用。例如，以下命令授予读写访问权限。



```console
 docker run -v HOST-DIRECTORY:/CONTAINER-DIRECTORY:rw nginx
```

Read-only bind mounts let the container access the mounted files on the host for reading, but it can't change or delete the files. With read-write bind mounts, containers can modify or delete mounted files, and these changes or deletions will also be reflected on the host system. Read-only bind mounts ensures that files on the host can't be accidentally modified or deleted by a container.
只读绑定挂载允许容器访问主机上已挂载的文件进行读取，但无法更改或删除这些文件。使用读写绑定挂载，容器可以修改或删除已挂载的文件，并且这些更改或删除操作也会反映在主机系统上。只读绑定挂载可确保主机上的文件不会被容器意外修改或删除。

> **Synchronized File Share 同步文件共享**
>
> As your codebase grows larger, traditional methods of file sharing like bind mounts may become inefficient or slow, especially in development environments where frequent access to files is necessary. [Synchronized file shares](https://docs.docker.com/desktop/features/synchronized-file-sharing/) improve bind mount performance by leveraging synchronized filesystem caches. This optimization ensures that file access between the host and virtual machine (VM) is fast and efficient.
> 随着代码库变得越来越大，传统的文件共享方法（如绑定挂载）可能会变得效率低下或缓慢，尤其是在需要频繁访问文件的开发环境中。 [同步文件共享](https://docs.docker.com/desktop/features/synchronized-file-sharing/)利用同步文件系统缓存来提升绑定挂载性能。此优化可确保主机和虚拟机 (VM) 之间的文件访问快速高效。

### [4.4 Docker 网络：在受控网络中运行 Postgres](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#run-postgres-container-in-a-controlled-network)

By default, containers automatically connect to a special network called a bridge network when you run them. This bridge network acts like a virtual bridge, allowing containers on the same host to communicate with each other while keeping them isolated from the outside world and other hosts. It's a convenient starting point for most container interactions. However, for specific scenarios, you might want more control over the network configuration.
默认情况下，容器运行时会自动连接到一个称为桥接网络的特殊网络。此桥接网络的作用类似于虚拟网桥，允许同一主机上的容器相互通信，同时保持它们与外界和其他主机的隔离。对于大多数容器交互来说，这是一个便捷的起点。但是，对于特定场景，您可能需要对网络配置进行更多控制。

Here's where the custom network comes in. You create a custom network by passing `--network` flag with the `docker run` command. All containers without a `--network` flag are attached to the default bridge network.
这就是自定义网络的作用所在。你可以在 `docker run` 命令中传递 `--network` 参数来创建自定义网络。所有未指定 `--network` 参数的容器都会连接到默认的桥接网络。

Follow the steps to see how to connect a Postgres container to a custom network.
按照步骤了解如何将 Postgres 容器连接到自定义网络。

1. Create a new custom network by using the following command:
   使用以下命令创建一个新的自定义网络：

   

   ```console
    docker network create mynetwork
   ```

2. Verify the network by running the following command:
   通过运行以下命令验证网络：

   

   ```console
    docker network ls
   ```

   This command lists all networks, including the newly created "mynetwork".
   此命令列出所有网络，包括新创建的“mynetwork”。

3. Connect Postgres to the custom network by using the following command:
   使用以下命令将 Postgres 连接到自定义网络：

   

   ```console
    docker run -d -e POSTGRES_PASSWORD=secret -p 5434:5432 --network mynetwork postgres
   ```

   This will start Postgres container in the background, mapped to the host port 5434 and attached to the `mynetwork` network. You passed the `--network` parameter to override the container default by connecting the container to custom Docker network for better isolation and communication with other containers. You can use `docker network inspect` command to see if the container is tied to this new bridge network.
   这将在后台启动 Postgres 容器，映射到主机端口 5434 并连接到 `mynetwork` 网络。您传递了 `--network` 参数，通过将容器连接到自定义 Docker 网络来覆盖容器默认设置，以便更好地隔离并与其他容器通信。您可以使用 `docker network inspect` 命令查看容器是否已绑定到这个新的桥接网络。



    默认桥接和自定义网络之间的主要区别

   > 1. DNS resolution: By default, containers connected to the default bridge network can communicate with each other, but only by IP address. (unless you use `--link` option which is considered legacy). It is not recommended for production use due to the various [technical shortcomings](https://docs.docker.com/engine/network/drivers/bridge/#differences-between-user-defined-bridges-and-the-default-bridge). On a custom network, containers can resolve each other by name or alias.
   >    DNS 解析：默认情况下，连接到默认桥接网络的容器可以相互通信，但只能通过 IP 地址进行通信。（除非使用 `--link` 选项，该选项已被视为旧选项）。由于各种原因，不建议在生产环境中使用。 [技术缺陷 ](https://docs.docker.com/engine/network/drivers/bridge/#differences-between-user-defined-bridges-and-the-default-bridge)。在自定义网络上，容器可以通过名称或别名相互解析。
   > 2. Isolation: All containers without a `--network` specified are attached to the default bridge network, hence can be a risk, as unrelated containers are then able to communicate. Using a custom network provides a scoped network in which only containers attached to that network are able to communicate, hence providing better isolation.
   >    隔离性：所有未指定 `--network` 的容器都会连接到默认桥接网络，因此可能存在风险，因为不相关的容器可以进行通信。使用自定义网络可以提供一个限定范围的网络，只有连接到该网络的容器才能进行通信，从而提供更好的隔离性。

### [4.5 限制与监控容器资源](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#restricting-the-container-to-consume-the-resources)

You can use the `--memory` and `--cpus` flags with the `docker run` command to restrict how much CPU and memory a container can use. For example, you can set a memory limit for the Python API container, preventing it from consuming excessive resources on your host. Here's the command:
您可以在 `docker run` 命令中使用 `--memory` 和 `--cpus` 标志来限制容器可以使用的 CPU 和内存量。例如，您可以为 Python API 容器设置内存限制，防止其在主机上消耗过多的资源。命令如下：



```console
 docker run -e POSTGRES_PASSWORD=secret --memory="512m" --cpus="0.5" postgres
```

This command limits container memory usage to 512 MB and defines the CPU quota of 0.5 for half a core.
此命令将容器内存使用量限制为 512 MB，并将半个核心的 CPU 配额定义为 0.5。

> **Monitor the real-time resource usage
> 监控实时资源使用情况**
>
> You can use the `docker stats` command to monitor the real-time resource usage of running containers. This helps you understand whether the allocated resources are sufficient or need adjustment.
> 您可以使用 `docker stats` 命令来监控正在运行的容器的实时资源使用情况，从而了解分配的资源是否充足或是否需要调整。

By effectively using these `docker run` flags, you can tailor your containerized application's behavior to fit your specific requirements.
通过有效使用这些 `docker run` 标志，您可以定制容器化应用程序的行为以满足您的特定要求。
