Введение
В этом лабе вы запустите свой первый контейнер Docker.
Контейнеры - это просто процесс (или группа процессов), работающий в изоляции. Изоляция достигается с помощью пространств имен Linux, контрольных групп (cgroups), seccomp и SELinux. Обратите внимание, что пространства имен Linux и контрольные группы встроены в ядро Linux! Кроме самого ядра Linux, в контейнерах нет ничего особенного.
То, что делает контейнеры полезными, - это инструментарий, окружающий их. Для этих лаб мы будем использовать Docker, который является широко используемым инструментом для использования контейнеров при построении приложений. Docker предоставляет разработчикам и операторам дружелюбный интерфейс для построения, отправки и запуска контейнеров в любой среде с Docker-enginem. Поскольку Docker-клиент требует Docker-engines, альтернативой является использование Podman, который является демоновым контейнерным движком для разработки, управления и запуска OCI контейнеров и может запускать контейнеры с правами root или в режиме без привилегий root. По этим причинам мы рекомендуем Podman, но из-за его распространенности в этом лабе по-прежнему используется Docker.
В первой части этого лабе мы запустим наш первый контейнер и узнаем, как его инспектировать. Мы сможем увидеть изоляцию пространств имен, которую мы получаем от ядра Linux.
После запуска нашего первого контейнера мы углубимся в другие применения контейнеров. Вы можете найти множество примеров таких на Docker Store, и мы запустим несколько разных типов контейнеров на одном и том же хосте. Это позволит нам увидеть преимущество изоляции - то, что мы можем запускать несколько контейнеров на одном и том же хосте без конфликтов.
В этом лабе мы будем использовать несколько команд Docker. Для полной документации по доступным командам проверьте официальную документацию.
Начало работы
Откройте терминал на виртуальной машине LabEx и запустите docker -h, что покажет вам страницу помощи по CLI Docker.
$ docker -h
Flag shorthand -h has been deprecated, please use --help
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
...
Management Commands:
builder Manage builds
config Manage Docker configs
container Manage containers
engine Manage the docker engine
image Manage images
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
CLI Docker можно использовать для управления несколькими функциями Docker Engine. В этом лабе мы сосредоточимся в основном на команде container.
Установите podman на вашей виртуальной машине LabEx.
sudo apt-get update
sudo apt-get install podman -y
Если podman установлен, вы можете запустить альтернативную команду для сравнения.
sudo podman -h
Вы также можете проверить версию вашей установки Docker с помощью docker version
docker version
Client:
Version: 20.10.21
...
Server:
Engine:
Version: 20.10.21
...
Вы замечаете, что Docker устанавливает как Client, так и Server: Docker Engine. Например, если вы запустите ту же команду для podman, вы увидите только версию CLI, потому что podman работает без демона и зависит от OCI-совместимого контейнерного времени выполнения (runc, crun, runv и т.д.) для взаимодействия с ОС для создания запускаемых контейнеров.
sudo podman version --events-backend=none
Version: 3.4.4
API Version: 3.4.4
Go Version: go1.17.3
Built: Thu Jan 1 08:00:00 1970
OS/Arch: linux/amd64
Запустите свой первый контейнер
Мы будем использовать CLI Docker для запуска нашего первого контейнера.
Откройте терминал на виртуальной машине LabEx.
Запустите команду.
docker container run -t ubuntu top
Используйте команду docker container run для запуска контейнера с изображением ubuntu с использованием команды top. Флаг -t выделяет псевдо-ТTY, который нам нужен для правильной работы top.
$ docker container run -it ubuntu top
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
aafe6b5e13de: Pull complete
0a2b43a72660: Pull complete
18bdd1e546d2: Pull complete
8198342c3e05: Pull complete
f56970a44fd4: Pull complete
Digest: sha256:f3a61450ae43896c4332bda5e78b453f4a93179045f20c8181043b26b5e79028
Status: Downloaded newer image for ubuntu:latest
Команда docker run сначала приведет к выполнению docker pull для загрузки образа ubuntu на ваш хост. После его загрузки контейнер будет запущен. Вывод для запущенного контейнера должен выглядеть так:
top - 20:32:46 up 3 days, 17:40, 0 users, load average: 0.00, 0.01, 0.00
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.1 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2046768 total, 173308 free, 117248 used, 1756212 buff/cache
KiB Swap: 1048572 total, 1048572 free, 0 used. 1548356 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 36636 3072 2640 R 0.3 0.2 0:00.04 top
top - это утилита Linux, которая выводит процессы на системе и упорядочивает их по потреблению ресурсов. Обратите внимание, что в этом выводе есть только один процесс: это сам процесс top. Мы не видим другие процессы с нашего хоста в этом списке из-за изоляции пространства имен PID.
Контейнеры используют пространства имен Linux для обеспечения изоляции системных ресурсов от других контейнеров или хоста. Пространство имен PID обеспечивает изоляцию для идентификаторов процессов. Если вы запустите top внутри контейнера, вы заметите, что он показывает процессы внутри пространства имен PID контейнера, что сильно отличается от того, что вы можете увидеть, если бы вы запустили top на хосте.
Хотя мы используем образ ubuntu, важно помнить, что наш контейнер не имеет собственного ядра. Он использует ядро хоста, а образ ubuntu используется только для предоставления файловой системы и инструментов, доступных в системе Ubuntu.
Проверьте контейнер с помощью docker container exec
Команда docker container exec - это способ "войти" в пространства имен запущенного контейнера с новым процессом.
Откройте новый терминал. Выберите Terminal > New Terminal.
В новом терминале используйте команду docker container ls, чтобы получить идентификатор запущенного контейнера, который вы только что создали.
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b3ad2a23fab3 ubuntu "top" 29 minutes ago Up 29 minutes goofy_nobel
Затем используйте этот идентификатор для запуска bash внутри этого контейнера с помощью команды docker container exec. Поскольку мы используем bash и хотим взаимодействовать с этим контейнером из нашего терминала, используйте флаги -it для запуска в интерактивном режиме, выделяя псевдо-Терминал.
$ docker container exec -it ID < CONTAINER > bash
root@b3ad2a23fab3:/#
И вуаля! Мы только что использовали команду docker container exec, чтобы "войти" в пространства имен нашего контейнера с процессом bash. Использование docker container exec с bash - это распространенный способ проверить контейнер Docker.
Обратите внимание на изменение префикса в терминале. Например, root@b3ad2a23fab3:/. Это означает, что мы запускаем bash "внутри" нашего контейнера.
Примечание: Это не то же самое, что подключаться по SSH к отдельному хосту или виртуальной машине. Мы не нуждаемся в SSH-сервере для подключения к процессу bash. Помните, что контейнеры используют функции уровня ядра для обеспечения изоляции, и контейнеры запускаются поверх ядра. Наш контейнер - это просто группа процессов, работающих в изоляции на том же хосте, и мы можем использовать docker container exec, чтобы войти в эту изоляцию с процессом bash. После выполнения docker container exec группа процессов, работающих в изоляции (то есть наш контейнер), включает top и bash.
Из того же терминала запустите ps -ef, чтобы проверить запущенные процессы.
root@b3ad2a23fab3:/## ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 20:34? 00:00:00 top
root 17 0 0 21:06? 00:00:00 bash
root 27 17 0 21:14? 00:00:00 ps -ef
Вы должны увидеть только процесс top, процесс bash и наш процесс ps.
Для сравнения выйдите из контейнера и запустите ps -ef или top на хосте. Эти команды будут работать на Linux или Mac. Для Windows вы можете проверить запущенные процессы с помощью tasklist.
root@b3ad2a23fab3:/## exit
exit
$ ps -ef
## Lots of processes!
Техническое углубление PID - это только одно из пространств имен Linux, которое обеспечивает контейнерам изоляцию системных ресурсов. Другие пространства имен Linux включают:
- MNT - Монтировать и демонтировать директории без влияния на другие пространства имен
- NET - Контейнеры имеют свою собственную сеть
- IPC - Изолированные механизмы межпроцессного взаимодействия, такие как очередь сообщений.
- User - Изолированное представление пользователей на системе
- UTC - Устанавливать имя хоста и доменное имя для каждого контейнера
Эти пространства имен вместе обеспечивают изоляцию для контейнеров, которая позволяет им безопасно работать вместе и не конфликтовать с другими контейнерами, запускающимися на том же системном узле. Далее мы покажем разные применения контейнеров и преимущество изоляции при запуске нескольких контейнеров на одном и том же хосте.
Примечание: Пространства имен - это функция Linux-ядра. Но Docker позволяет запускать контейнеры на Windows и Mac... как это работает? Секрет заключается в том, что встроенный в продукт Docker или Docker-engines подсистема Linux. Docker открыл исходный код этой подсистемы Linux в новый проект: LinuxKit. Возможность запускать контейнеры на многих разных платформах - это один из преимуществ использования инструментов Docker с контейнерами.
Кроме того, за счет создания примитивов контейнеров в операционной системе Windows теперь возможны нативные контейнеры Windows. Нативные контейнеры Windows можно запускать на Windows 10 или Windows Server 2016 или новее.
Примечание: Если вы выполняете это упражнение в контейнеризованном терминале и выполняете команду ps -ef в терминале, вы по-прежнему увидите ограниченный набор процессов после выхода из команды exec. Вы можете попробовать выполнить команду ps -ef в терминале на вашем локальном компьютере, чтобы увидеть все процессы.
Очистите контейнер, запускающий процессы top, нажав: <ctrl>-c, выведите список всех контейнеров и удалите контейнеры по их идентификатору.
docker ps -a
docker rm <CONTAINER ID>
Запустите несколько контейнеров
Исследуйте Docker Hub
Docker Hub - это центральный публичный реестр Docker-образов, который содержит образов сообщества и официальные образы.
При поиске образов вы найдете фильтры для "Docker Certified", "Verified Publisher" и "Official Images" образов. Выберите фильтр "Docker Certified", чтобы найти образов, которые считаются готовыми для использования в корпоративных условиях и которые были протестированы с использованием продукта Docker Enterprise Edition. При разработке собственных образов, предназначенных для развертывания в рабочей среде, важно избегать использования непроверенных контента из Docker Store. Эти непроверенные образы могут содержать уязвимости безопасности или даже вредоносное ПО.
В шаге 2 этого лабара мы запустим несколько контейнеров, используя некоторые проверенные образы из Docker Hub: веб-сервер Nginx и базу данных MongoDB.
Запустите сервер Nginx
Запустим контейнер, используя официальный образ Nginx из Docker Hub.
docker container run --detach --publish 8080:80 --name nginx nginx
Здесь мы используем несколько новых флагов. Флаг --detach запустит этот контейнер в фоновом режиме. Флаг publish публикует порт 80 в контейнере (стандартный порт для Nginx), через порт 8080 на нашем хосте. Помните, что пространство имен NET дает процессам контейнера свою собственную сеть. Флаг --publish - это функция, которая позволяет нам экспортировать сеть через контейнер на хост.
Как вы знаете, что порт 80 - это стандартный порт для Nginx? Потому что он указан в документации на Docker Hub. В целом, документация по проверенным образам очень хорошая, и вы будете ссылаться на нее при запуске контейнеров с использованием этих образов.
Мы также указываем флаг --name, который дает имя контейнеру. Каждый контейнер имеет имя, если вы не укажете его, Docker случайным образом назначит его для вас. Указание собственного имени упрощает выполнение последующих команд на вашем контейнере, так как вы можете ссылаться на имя вместо идентификатора контейнера. Например: docker container inspect nginx вместо docker container inspect 5e1.
Поскольку это первый раз, когда вы запускаете контейнер Nginx, он загрузит образ Nginx из Docker Store. Последующие контейнеры, созданные из образа Nginx, будут использовать существующий образ, расположенный на вашем хосте.
Nginx - это легковесный веб-сервер. Вы можете получить доступ к серверу Nginx на вкладке Web 8080 виртуальной машины LabEx. Переключитесь на нее и обновите страницу, чтобы увидеть вывод от Nginx.

Запустите сервер mongo DB
Теперь запустим сервер MongoDB. Мы будем использовать официальный образ MongoDB из Docker Hub. Вместо использования тега latest (который является стандартным, если тег не указан), мы будем использовать конкретную версию образа mongo: 4.4.
docker container run --detach --publish 8081:27017 --name mongo mongo:4.4
Опять же, поскольку это первый раз, когда мы запускаем контейнер mongo, мы загрузим образ mongo из Docker Store. Мы используем флаг --publish, чтобы экспортировать порт 27017 mongo на нашем хосте. Мы должны использовать другой порт, кроме 8080, для маппинга на хост, так как этот порт уже экспортируется на нашем хосте. Опять же, обратитесь к официальным документам на Docker Hub, чтобы получить более подробную информацию о использовании образа mongo.
Посмотрите вывод от MongoDB, используя 0.0.0.0:8081 в веб-браузере. Вы должны увидеть сообщение, которое вернет предупреждение от MongoDB.

Проверьте запущенные контейнеры с помощью docker container ls
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d6777df89fea nginx "nginx -g 'daemon..." Less than a second ago Up 2 seconds 0.0.0.0:8080- nginx > 80/tcp
ead80a0db505 mongo "docker-entrypoint..." 17 seconds ago Up 19 seconds 0.0.0.0:8081- mongo > 27017/tcp
af549dccd5cf ubuntu "top" 5 minutes ago Up 5 minutes priceless_kepler
Вы должны увидеть, что у вас есть контейнер веб-сервера Nginx и контейнер MongoDB, работающий на вашем хосте. Обратите внимание, что мы не настроили эти контейнеры для взаимодействия друг с другом.
Вы можете увидеть имена "nginx" и "mongo", которые мы дали нашим контейнерам, и случайное имя (в моем случае "priceless_kepler"), которое было сгенерировано для контейнера ubuntu. Вы также можете увидеть маппинги портов, которые мы указали с помощью флага --publish. Для получения более подробной информации о этих запущенных контейнерах вы можете использовать команду docker container inspect [container id.
Одно из вещей, которое вы, возможно, заметите, - это то, что контейнер mongo запускает команду docker-entrypoint. Это имя исполняемого файла, которое запускается при запуске контейнера. Образ mongo требует некоторой предварительной настройки перед запуском процесса базы данных. Вы можете точно увидеть, что делает этот скрипт, посмотрев на него на github. Как правило, вы можете найти ссылку на исходный код на github из страницы с описанием образа на сайте Docker Store.
Контейнеры автономны и изолированы, что означает, что мы можем избежать потенциальных конфликтов между контейнерами с разными системными или зависимостями времени выполнения. Например: развертывание приложения, использующего Java 7, и другого приложения, использующего Java 8, на одном и том же хосте. Или запуск нескольких контейнеров Nginx, все они имеют порт 80 в качестве стандартного порта прослушивания (если экспортировать на хост с использованием флага --publish, выбранные порты для хоста должны быть уникальными). Преимущества изоляции возможны благодаря пространствам имен Linux.
Примечание: Вам не нужно было ничего устанавливать на вашем хосте (кроме Docker), чтобы запустить эти процессы! Каждый контейнер включает в себя зависимости, которые ему нужны внутри контейнера, поэтому вы не должны устанавливать ничего на вашем хосте напрямую.
Запуск нескольких контейнеров на одном и том же хосте позволяет нам полностью использовать ресурсы (CPU, память и т.д.), доступные на одном хосте. Это может привести к значительным экономиям затрат для предприятия.
В то время как запуск образов напрямую из Docker Hub может быть полезным иногда, гораздо более полезно создавать собственные образы и ссылаться на официальные образы в качестве начальной точки для этих образов. Мы углубимся в создание собственных пользовательских образов в Лаборатории 2.
Очистка
Завершение этого лабара приводит к появлению большого количества запущенных контейнеров на вашем хосте. Очистим их.
Во - первых, получите список запущенных контейнеров с помощью docker container ls.
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d6777df89fea nginx "nginx -g 'daemon..." 3 minutes ago Up 3 minutes 0.0.0.0:8080- nginx > 80/tcp
ead80a0db505 mongo "docker-entrypoint..." 3 minutes ago Up 3 minutes 0.0.0.0:8081- mongo > 27017/tcp
af549dccd5cf ubuntu "top" 8 minutes ago Up 8 minutes priceless_kepler
Далее, для каждого контейнера в списке запустите docker container stop [container id]. Вы также можете использовать имена контейнеров, которые вы указали ранее.
$ docker container stop d67 ead af5
d67
ead
af5
Примечание: Вам нужно только указать достаточно цифр из идентификатора, чтобы он был уникальным. Три цифры几乎 всегда достаточно.
Удалите остановленные контейнеры
docker system prune - это очень удобная команда для очистки системы. Она удалит все остановленные контейнеры, неиспользуемые тома и сети, а также "висячие" образы.
$ docker system prune
WARNING! This will remove:
- all stopped containers
- all volumes not used by at least one container
- all networks not used by at least one container
- all dangling images
Are you sure you want to continue? [y/N] y
Deleted Containers:
7872fd96ea4695795c41150a06067d605f69702dbcb9ce49492c9029f0e1b44b
60abd5ee65b1e2732ddc02b971a86e22de1c1c446dab165462a08b037ef7835c
31617fdd8e5f584c51ce182757e24a1c9620257027665c20be75aa3ab6591740
Total reclaimed space: 12B
Резюме
В этом лабе вы создали свои первые контейнеры Ubuntu, Nginx и MongoDB.
Основные выводы
- Контейнеры состоят из пространств имен Linux и групп управления, которые обеспечивают изоляцию от других контейнеров и хоста.
- Из-за свойств изоляции контейнеров вы можете планировать запуск многих контейнеров на одном хосте, не беспокоясь о конфликтующих зависимостях. Это упрощает запуск нескольких контейнеров на одном хосте: позволяет полностью использовать ресурсы, выделенные для этого хоста, и в конечном итоге сэкономить деньги на затратах на сервера.
- Избегайте использования непроверенного контента из Docker Store при разработке собственных образов, так как эти образы могут содержать уязвимости безопасности или даже вредоносное ПО.
- Контейнеры включают в себя все, что нужно для запуска процессов внутри них, поэтому нет необходимости устанавливать дополнительные зависимости непосредственно на вашем хосте.