Основы сетевого взаимодействия в Docker

DockerBeginner
Практиковаться сейчас

Введение

В рамках этой лабораторной работы мы изучим фундаментальные аспекты сетевого взаимодействия в Docker. Сети Docker позволяют контейнерам обмениваться данными друг с другом и с внешним миром. Мы рассмотрим различные типы сетей, научимся создавать собственные сети, подключать к ним контейнеры и управлять сетевыми конфигурациями. Этот практический опыт заложит прочный фундамент для понимания концепций и методов работы с сетями в экосистеме Docker.

Понимание типов сетей Docker

Docker предоставляет несколько встроенных сетевых драйверов. Начнем с изучения стандартных сетей, которые уже присутствуют в вашей системе.

В терминале выполните следующую команду, чтобы вывести список всех доступных сетей Docker:

docker network ls

Эта команда отображает все сети, созданные Docker в вашей системе. Вы должны увидеть вывод, похожий на этот:

NETWORK ID     NAME      DRIVER    SCOPE
79dce413aafd   bridge    bridge    local
91199fc6ad2e   host      host      local
1078d2c781b6   none      null      local

Разберем основные типы сетей:

  1. bridge: Это сетевой драйвер по умолчанию. Если вы запускаете контейнер, не указывая сеть, он автоматически подключается к сети типа bridge. Контейнеры в одной сети bridge могут взаимодействовать друг с другом, используя свои IP-адреса.

  2. host: Этот драйвер убирает сетевую изоляцию между контейнером и хостом Docker. Контейнер использует сетевое пространство имен хоста напрямую, что означает использование IP-адреса и портов хоста. Это бывает полезно для оптимизации производительности в определенных сценариях.

  3. none: Этот драйвер полностью отключает сетевое взаимодействие для контейнера. Контейнеры с таким типом сети не имеют доступа к внешним сетям или другим контейнерам. Это полезно, когда требуется максимальная изоляция.

Столбец SCOPE указывает, ограничена ли сеть одним хостом (local) или может охватывать несколько хостов в кластере Docker Swarm (swarm).

Инспекция стандартной сети Bridge

Теперь, когда мы увидели список сетей, давайте подробнее изучим стандартную сеть bridge. Она создается Docker автоматически и используется всеми контейнерами, если не указано иное.

Выполните следующую команду для детального осмотра сети bridge:

docker network inspect bridge

Эта команда предоставляет подробную информацию о сети, включая ее подсеть, шлюз и подключенные контейнеры. Вы увидите вывод в формате JSON, похожий на этот (сокращено для краткости):

[
  {
    "Name": "bridge",
    "Id": "79dce413aafdd7934fa3c1d0cc97decb823891ce406442b7d51be6126ef06a5e",
    "Created": "2024-08-22T09:58:39.747333789+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": {},
    "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": {}
  }
]

Разберем ключевые параметры из этого вывода:

  • Subnet: Подсеть, используемая контейнерами в этой сети — 172.17.0.0/16. Это означает, что контейнерам будут назначаться IP-адреса из этого диапазона.
  • Gateway: Шлюз для этой сети — 172.17.0.1. Это IP-адрес, который контейнеры используют для связи с внешними сетями.
  • Containers: Это поле сейчас пустое, так как мы еще не запустили ни одного контейнера.
  • Options: Различные параметры конфигурации. Например, enable_icc со значением "true" означает, что межоболочечное взаимодействие (inter-container communication) разрешено.

Понимание этой информации критически важно при поиске сетевых неисправностей или при необходимости настроить взаимодействие контейнеров с конкретными диапазонами IP-адресов.

Создание пользовательской сети Bridge

Хотя стандартная сеть bridge подходит для многих задач, создание собственных сетей обеспечивает лучшую изоляцию и контроль. Пользовательские сети особенно полезны, когда нужно сгруппировать связанные контейнеры или ограничить круг контейнеров, которые могут общаться друг с другом.

Давайте создадим собственную сеть типа bridge под названием my-network:

docker network create --driver bridge my-network

Эта команда создает новую сеть. Флаг --driver bridge здесь не является обязательным, так как bridge используется по умолчанию, но мы указали его для наглядности.

Теперь проверим, что наша новая сеть появилась в системе:

docker network ls

Вы должны увидеть my-network в списке:

NETWORK ID     NAME         DRIVER    SCOPE
1191cb61c989   bridge       bridge    local
91199fc6ad2e   host         host      local
47ac4e684a72   my-network   bridge    local
1078d2c781b6   none         null      local

Появление my-network в списке подтверждает успешное создание. Теперь эта сеть готова для подключения контейнеров.

Подключение контейнеров к сетям

Теперь, когда у нас есть пользовательская сеть, давайте создадим два контейнера и подключим их к ней. В качестве примера мы будем использовать образ nginx — легкий веб-сервер.

Выполните следующие команды для создания двух контейнеров:

docker run -d --name container1 --network my-network nginx
docker run -d --name container2 --network my-network nginx

Разберем используемые флаги:

  • -d: Запускает контейнер в фоновом режиме (detached mode).
  • --name: Присваивает контейнеру имя для удобства обращения к нему.
  • --network: Указывает, к какой сети должен подключиться контейнер.
  • nginx: Имя образа, на основе которого создаются контейнеры.

Эти команды создают два фоновых контейнера с именами container1 и container2, оба подключены к нашей сети my-network.

Убедимся, что контейнеры запущены и подключены:

docker ps

Эта команда выводит список всех запущенных контейнеров:

CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
1234567890ab   nginx     "/docker-entrypoint.…"   10 seconds ago   Up 9 seconds    80/tcp    container2
abcdef123456   nginx     "/docker-entrypoint.…"   20 seconds ago   Up 19 seconds   80/tcp    container1

Как видно из вывода, оба контейнера работают на базе образа Nginx и прослушивают порт 80 внутри себя.

Тестирование связи между контейнерами

Одним из главных преимуществ сетей Docker является то, что контейнеры в одной сети могут общаться друг с другом, используя свои имена в качестве имен хостов (hostnames). Это упрощает настройку взаимодействия между сервисами, так как вам не нужно знать их динамические IP-адреса.

Проверим это, отправив запрос из container1 к container2:

docker exec container1 curl -s container2

Разбор команды:

  • docker exec: Позволяет выполнить команду внутри запущенного контейнера.
  • container1: Имя контейнера, внутри которого мы запускаем команду.
  • curl -s container2: Сама команда. Она отправляет GET-запрос к container2, а флаг -s (silent) скрывает лишнюю служебную информацию.

В результате вы должны увидеть HTML-код стандартной приветственной страницы Nginx:

<!doctype html>
<html>
  <head>
    <title>Welcome to nginx!</title>
    <style>
      html {
        color-scheme: light dark;
      }
      body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
      }
    </style>
  </head>
  <body>
    <h1>Welcome to nginx!</h1>
    <p>
      If you see this page, the nginx web server is successfully installed and
      working. Further configuration is required.
    </p>

    <p>
      For online documentation and support please refer to
      <a href="http://nginx.org/">nginx.org</a>.<br />
      Commercial support is available at
      <a href="http://nginx.com/">nginx.com</a>.
    </p>

    <p><em>Thank you for using nginx.</em></p>
  </body>
</html>

Успешный ответ подтверждает, что container1 может связаться с container2 по его имени. Встроенный DNS-сервер Docker автоматически разрешает имя контейнера в его IP-адрес внутри сети.

Проброс портов контейнера

По умолчанию контейнеры в пользовательской сети могут общаться между собой, но они недоступны извне хоста Docker. Чтобы сделать контейнер доступным для хоста или внешних сетей, нам нужно «пробросить» (экспонировать) его порты.

Создадим новый контейнер с открытым портом:

docker run -d --name exposed-container -p 8080:80 --network my-network nginx

Разбор команды:

  • -d: Запуск в фоновом режиме.
  • --name exposed-container: Имя контейнера.
  • -p 8080:80: Сопоставление порта 80 внутри контейнера с портом 8080 на хост-машине.
  • --network my-network: Подключение к нашей сети.
  • nginx: Используемый образ.

Теперь вы можете получить доступ к серверу Nginx этого контейнера прямо с вашей хост-машины, открыв браузер по адресу http://localhost:8080 или воспользовавшись curl:

curl localhost:8080

Вы увидите тот же HTML-код приветствия Nginx. Однако на этот раз мы обращаемся к контейнеру напрямую с хоста, а не из другого контейнера.

Использование хост-сети (Host Networking)

В сценариях, где требуется, чтобы контейнер разделял сетевой стек хоста, можно использовать драйвер host. Это убирает сетевую изоляцию, что полезно для некоторых высокопроизводительных приложений, но требует осторожности из-за возможных конфликтов портов.

Создадим контейнер с использованием хост-сети:

docker run -d --name host-networked --network host nginx

Обратите внимание, что при использовании --network host флаг -p не применяется, так как контейнер уже использует сетевые интерфейсы хоста напрямую.

Чтобы убедиться, что контейнер использует хост-сеть, проверим его настройки:

docker inspect --format '{{.HostConfig.NetworkMode}}' host-networked

Команда должна вывести host.

В этом режиме контейнер имеет тот же IP-адрес, что и хост. Это максимизирует производительность, но любые порты, которые открывает приложение в контейнере, открываются непосредственно на хосте.

Резюме

В ходе этой лабораторной работы мы изучили основы сетевого взаимодействия в Docker. Мы рассмотрели различные типы сетей, научились создавать пользовательские сети, подключать к ним контейнеры, проверять связь между ними, пробрасывать порты и использовать хост-режим. Эти концепции являются фундаментом для проектирования и управления контейнеризированными приложениями.

Мы научились:

  • Выводить список и инспектировать сети Docker.
  • Создавать пользовательские мостовые (bridge) сети.
  • Подключать контейнеры к сетям.
  • Тестировать связь между контейнерами по их именам.
  • Открывать доступ к портам контейнера для хост-системы.
  • Использовать режим host для сетевого взаимодействия.

Понимание этих принципов поможет вам создавать более надежные и безопасные приложения. По мере углубления в Docker вы столкнетесь с более сложными сетевыми сценариями, но знания, полученные здесь, станут надежной базой для изучения оркестрации контейнеров и микросервисной архитектуры.

Помните, что сети Docker — это мощный инструмент, позволяющий изолировать среды приложений, сохраняя при этом контролируемую связь между компонентами и внешним миром. Практикуйтесь и изучайте продвинутые темы, чтобы стать экспертом в управлении сетями Docker.