Статьи

Докер с нуля: работа с контейнерами, часть 1

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

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

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

Прежде чем мы начнем, убедитесь, что Docker правильно установлен в вашей среде. В зависимости от того, как он установлен и от вашего пользователя, вам может потребоваться запустить его как sudo. Я пропущу судо.

Вы запускаете Docker-контейнер, запуская образ. Существует несколько способов запуска контейнера, которые влияют на простоту управления всеми контейнерами. Когда контейнер запускается, он обычно запускает команду, определенную в Dockerfile. Вот Dockerfile для контейнера hello-world:

1
2
3
FROM scratch
COPY hello /
CMD [«/hello»]

Команда просто запускает двоичный файл «hello», который был скопирован в корень контейнера при построении образа.

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

Самый простой способ запустить контейнер: docker run <image id or name> .

Когда вы запускаете контейнер с помощью этой команды, Docker назначит ему имя, состоящее из двух случайных слов. Например: docker run hello-world .

Если у вас уже есть образ hello-world, то Docker запустит его. Если вы этого не сделаете, он извлечет его из официального DockerHub-репозитория Docker и запустит. Вывод должен выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Hello from Docker!
This message shows that your installation appears to be working correctly.
 
To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the «hello-world» image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.
 
To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash
 
Share images, automate work-flows, and more with a free Docker ID:
 https://cloud.docker.com/
 
For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

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

1
2
3
4
docker ps -a —format «table {{.ID}}\t{{.Status}}\t{{.Names}}»
 
CONTAINER ID STATUS NAMES
8e2e491accb5 Exited (0) 2 minutes ago clever_liskov

Позже я объясню, как перечислить контейнеры и все соответствующие параметры. А пока давайте сосредоточимся на разделе «Имена». Docker автоматически сгенерировал имя «clever_liskov», и мне придется использовать его или идентификатор контейнера для ссылки на этот контейнер для любых целей, таких как его перезапуск, удаление или выполнение команды.

Использование идентификаторов контейнеров или автоматически сгенерированных имен иногда неудобно. Если вы часто взаимодействуете с контейнером, который вы часто создаете, он получит другой идентификатор и автоматически сгенерированное имя. Также имя будет случайным.

Docker позволяет вам именовать ваши контейнеры, когда вы запускаете их, предоставляя аргумент командной строки «—name <container name>». В простых случаях, когда у вас есть только один контейнер на изображение, вы можете назвать контейнер в честь вашего изображения: docker run --name hello-world hello-world .

Теперь, если мы посмотрим на процесс (я ранее удалил clever_liskov ), мы увидим, что контейнер называется hello-world:

1
2
3
docker ps -a —format «table {{.ID}}\t{{.Names}}»
CONTAINER ID NAMES
f6fe77b3b6e8 hello-world

У именованного контейнера есть несколько преимуществ:

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

Давайте посмотрим на последний вариант. Если я попытаюсь снова выполнить ту же команду запуска с тем же именем «hello-world», я получу четкое сообщение об ошибке:

1
2
3
4
5
6
7
docker run —name hello-world hello-world
docker: Error response from daemon: Conflict.
«/hello-world» is already in use by container
f6fe77b3b6e8e77ccf346c32c599e67b2982893ca39f0415472c2949cacc4a51.
You have to remove (or rename) that container to be able to reuse
that name.
See ‘docker run —help’.

Контейнеры остаются по умолчанию. Иногда они вам не нужны. Вместо того, чтобы вручную удалять вышедшие контейнеры, вы заставляете контейнер уходить самостоятельно. --rm командной строки --rm помогает: docker run --rm hello-world .

По умолчанию Docker запускает команду, указанную в файле Docker, используемом для создания образа (или непосредственно в точке входа, если команда не найдена). Вы всегда можете переопределить его, указав свою собственную команду в конце команды run. Давайте запустим ls -la для образа busybox (в образе hello-world нет исполняемого файла ls ):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
docker run busybox ls -la
total 44
drwxr-xr-x 18 root root 4096 Mar 18 17:06 .
drwxr-xr-x 18 root root 4096 Mar 18 17:06 ..
-rwxr-xr-x 1 root root 0 Mar 18 17:06 .dockerenv
drwxr-xr-x 2 root root 12288 Mar 9 00:05 bin
drwxr-xr-x 5 root root 340 Mar 18 17:06 dev
drwxr-xr-x 2 root root 4096 Mar 18 17:06 etc
drwxr-xr-x 2 nobody nogroup 4096 Mar 9 00:05 home
dr-xr-xr-x 85 root root 0 Mar 18 17:06 proc
drwxr-xr-x 2 root root 4096 Mar 9 00:05 root
dr-xr-xr-x 13 root root 0 Mar 18 17:06 sys
drwxrwxrwt 2 root root 4096 Mar 9 00:05 tmp
drwxr-xr-x 3 root root 4096 Mar 9 00:05 usr
drwxr-xr-x 4 root root 4096 Mar 9 00:05 var

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

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

01
02
03
04
05
06
07
08
09
10
docker run —rm -it -e ENV_FROM_HOST=»123″ busybox
/ # env
HOSTNAME=8e7672bce5a7
SHLVL=1
HOME=/root
ENV_FROM_HOST=123
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
/ #

Первая строка запускает контейнер busybox, передавая ему переменную ENV_FROM_HOST, а затем внутри контейнера, выполняющего env показывает, что ENV_FROM_HOST установлен правильно.

Вы также можете использовать переменные среды хоста. Это устанавливает пару переменных среды хоста и использует их в команде run:

1
2
3
$ export VAR_1=1
$ export VAR_2=2
$ docker run —rm -it -e VAR_1=»$VAR_1″ -e VAR_2=»$VAR_2″ busybox

Внутри контейнера они теперь видны:

1
2
3
/ # env |
VAR_1=1
VAR_2=2

Одним из наиболее полезных взаимодействий является монтирование директорий хоста. Это позволяет использовать несколько интересных вариантов использования:

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

Здесь я создаю файл на хосте: $ echo "Yeah, it works!" > ~/data/1.txt $ echo "Yeah, it works!" > ~/data/1.txt

Затем я запускаю образ busybox, монтируя каталог ~ / data в / data в контейнере и отображая содержимое файла на экране:

1
2
$ docker run —rm -v ~/data:/data busybox cat /data/1.txt
Yeah, it works!

Я использовал команду cat /data/1.txt здесь.

Если вы выставите порт в вашем Dockerfile с помощью EXPOSE , он будет доступен только для других контейнеров Docker. Чтобы сделать его доступным на хосте, вам нужно использовать аргумент командной строки -p . Синтаксис -p <host port>:<exposed container port> .

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

1
docker run —name nginx —rm -d -p 9000:80 nginx

Обратите внимание, что в отличие от предыдущих команд, которые выполнили некоторые задачи и выполнили их, контейнер nginx будет продолжать работать и прослушивать входящие запросы. Давайте проверим, что nginx действительно работает и отвечает на запросы через порт 9000. Я предпочитаю отличный HTTP-клиент httpie, чем curl, для запуска веб-серверов и служб из командной строки:

01
02
03
04
05
06
07
08
09
10
11
http HEAD localhost:9000
 
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 612
Content-Type: text/html
Date: Sun, 19 Mar 2017 07:35:55 GMT
ETag: «58a323e4-264»
Last-Modified: Tue, 14 Feb 2017 15:36:04 GMT
Server: nginx/1.11.10

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

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