Статьи

Докер с нуля: создание изображений

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

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

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

Есть два способа создания изображений. Вы можете изменить и существующий контейнер, а затем зафиксировать его как новый образ, или вы можете написать Dockerfile и построить его в образ. Мы рассмотрим оба и объясним плюсы и минусы.

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

Давайте начнем с альпийского образа , который представляет собой очень маленький и спартанский образ на основе Alpine Linux. Мы можем запустить его в интерактивном режиме, чтобы попасть в оболочку. Наша цель — добавить файл с именем «да», содержащий текст «это работает!» в корневой каталог, а затем создайте из него новое изображение под названием «да-альпийский».

Вот так. Отлично, мы уже в корневом каталоге. Посмотрим что там.

1
2
3
> docker run -it alpine /bin/sh
/ # ls
bin dev etc home lib linuxrc media mnt proc root run sbin srv sys tmp usr var

Какой редактор доступен? Нет VIM, нет нано?

1
2
3
4
/ # vim
/bin/sh: vim: not found
/ # nano
/bin/sh: nano: not found

Ну что ж. Мы просто хотим создать файл:

1
2
3
/ # echo «it works!»
/ # cat yeah
it works!

Я вышел из интерактивной оболочки и вижу контейнер с именем «vibrant_spenc» с помощью docker ps --all . Флаг --all важен, потому что контейнер больше не работает.

1
2
3
> docker ps —all
CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
c8faeb05de5f alpine «/bin/sh» 6 minutes ago Exited vibrant_spence

Здесь я создаю новое изображение из контейнера «vibrate_spence». Я добавил сообщение коммита «мой, мой, мой» для хорошей меры.

1
2
> docker commit -m «mine, mine, mine» vibrant_spence yeah-alpine
sha256:e3c98cd21f4d85a1428…e220da99995fd8bf6b49aa

Давайте проверим это. Да, есть новое изображение, и в его истории вы можете увидеть новый слой с комментарием «мой, мой, мой».

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
> docker images
REPOSITORY TAG IMAGE ID SIZE
yeah-alpine latest e3c98cd21f4d 4.8 MB
python latest 775dae9b960e 687 MB
d4w/nsenter latest 9e4f13a0901e 83.8 kB
ubuntu-with-ssh latest 87391dca396d 221 MB
ubuntu latest bd3d4369aebc 127 MB
hello-world latest c54a2cc56cbb 1.85 kB
alpine latest 4e38e38c8ce0 4.8 MB
nsqio/nsq latest 2a82c70fe5e3 70.7 MB
 
> docker history yeah-alpine
IMAGE CREATED SIZE COMMENT
e3c98cd21f4d 40 seconds ago 66 B mine, mine, mine
4e38e38c8ce0 7 months ago 4.8 MB

Теперь для настоящего теста. Давайте удалим контейнер и создадим новый контейнер из изображения. Ожидаемый результат заключается в том, что файл «да» будет присутствовать в новом контейнере.

1
2
3
4
5
6
7
> docker rm vibrant_spence
vibrant_spence
 
> docker run -it yeah-alpine /bin/sh
/ # cat yeah
it works!
/ #

Что я могу сказать? Да, это работает!

Создание изображений из модифицированных контейнеров — это круто, но ответственности нет. Трудно отслеживать изменения и знать, какие были конкретные изменения. Дисциплинированный способ создания изображений — это создание их с помощью Dockerfile.

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

Здесь я просто продемонстрирую пару команд для создания другого образа «oh-yeah-alpine» на основе Dockerfile. В дополнение к созданию печально известного файла «да», давайте также установим vim. В дистрибутиве Alpine Linux используется система управления пакетами под названием «apk». Вот Dockerfile:

1
2
3
4
5
6
7
8
9
FROM alpine
 
# Copy the «yeah» file from the host
COPY yeah /yeah
 
# Update and install vim using apk
RUN apk update && apk add vim
 
CMD cat /yeah

Базовое изображение альпийское. Он копирует файл «да» из того же хост-каталога, где находится Dockerfile (путь к контексту сборки). Затем он запускает apk update и устанавливает vim. Наконец, он устанавливает команду, которая выполняется при запуске контейнера. В этом случае он выведет на экран содержимое файла «да».

OK. Теперь, когда мы знаем, во что ввязываемся, давайте создадим эту вещь. Опция «-t» устанавливает хранилище. Я не указал тег, поэтому он будет по умолчанию «последним».

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
> docker build -t oh-yeah-alpine .
Sending build context to Docker daemon 3.072 kB
Step 1/4 : FROM alpine
 —> 4e38e38c8ce0
Step 2/4 : COPY yeah /yeah
 —> 1b2a228cc2a5
Removing intermediate container a6221f725845
Step 3/4 : RUN apk update && apk add vim
 —> Running in e2c0524bd792
fetch http://dl-cdn.alpinelinux.org/…/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org…/x86_64/APKINDEX.tar.gz
v3.4.6-60-gc61f5bf [http://dl-cdn.alpinelinux.org/alpine/v3.4/main]
v3.4.6-33-g38ef2d2 [http://dl-cdn.alpinelinux.org/…/v3.4/community]
OK: 5977 distinct packages available
(1/5) Installing lua5.2-libs (5.2.4-r2)
(2/5) Installing ncurses-terminfo-base (6.0-r7)
(3/5) Installing ncurses-terminfo (6.0-r7)
(4/5) Installing ncurses-libs (6.0-r7)
(5/5) Installing vim (7.4.1831-r2)
Executing busybox-1.24.2-r9.trigger
OK: 37 MiB in 16 packages
 —> 7fa4cba6d14f
Removing intermediate container e2c0524bd792
Step 4/4 : CMD cat /yeah
 —> Running in 351b4f1c1eb1
 —> e124405f28f4
Removing intermediate container 351b4f1c1eb1
Successfully built e124405f28f4

Выглядит хорошо. Давайте проверим, что изображение было создано:

1
2
3
> docker images |
 
oh-yeah-alpine latest e124405f28f4 About a minute ago 30.5 MB

Обратите внимание, как при установке vim и его зависимостей размер контейнера увеличился с 4,8 МБ базового альпийского образа до огромных 30,5 МБ!

Это все очень мило. Но работает ли это?

1
2
> docker run oh-yeah-alpine
it works!

Ах да, это работает!

Если у вас все еще есть подозрения, давайте зайдем в контейнер и изучим файл «да» с нашим недавно установленным vim.

01
02
03
04
05
06
07
08
09
10
11
> docker run -it oh-yeah-alpine /bin/sh
/ # vim yeah
 
it works!
~
~
.
.
.
~
«yeah» 1L, 10C

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

Докер не пытается быть умным и анализировать ваши команды COPY. Это просто упаковывает все это. Обратите внимание, что содержимое сборки не заканчивается на вашем образе, но оно замедлит вашу команду сборки, если ваш контекст сборки слишком велик.

В этом случае я просто скопировал Dockerfile и «yes» в подкаталог и выполнил команду сборки docker в этом подкаталоге. Но иногда у вас есть сложное дерево каталогов, из которого вы хотите скопировать определенные подкаталоги и файлы и игнорировать другие. Введите файл .dockerignore.

Этот файл позволяет вам точно контролировать то, что входит в контекст сборки. Мой любимый трюк состоит в том, чтобы сначала исключить все, а затем начать включать фрагменты, которые мне нужны. Например, в этом случае я мог бы создать следующий файл .dockerignore и сохранить файл Docker и «да» в моем домашнем каталоге:

1
2
3
4
5
# Exclude EVERYTHING first
*
 
# Now selectively include stuff
!yeah

Нет необходимости включать сам файл Dockerfile или файл .dockerignore в контекст сборки.

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

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

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

Вы уже видели по умолчанию «последний» тег. Иногда имеет смысл добавить другие теги, такие как «test», «release-1.4» или git commit, соответствующий изображению.

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

1
2
3
4
5
6
7
> docker tag oh-yeah-alpine oh-yeah-alpine:cool-tag
> docker tag oh-yeah-alpine oh-yeah-alpine-2
 
> docker images |
oh-yeah-alpine-2 latest e124405f28f4 30.5 MB
oh-yeah-alpine cool-tag e124405f28f4 30.5 MB
oh-yeah-alpine latest e124405f28f4 30.5 MB

Вы также можете удалить тег, удалив изображение по имени тега. Это немного страшно, потому что если вы удалите последний тег случайно, вы потеряете изображение. Но если вы создаете образы из Dockerfile, вы можете просто перестроить образ.

1
2
3
4
5
> docker rmi oh-yeah-alpine-2
Untagged: oh-yeah-alpine-2:latest
 
> docker rmi oh-yeah-alpine:cool-tag
Untagged: oh-yeah-alpine:cool-tag

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

1
2
3
4
5
> docker rmi oh-yeah-alpine
 
Error response from daemon: conflict: unable to remove repository
reference «oh-yeah-alpine» (must force) —
container a1443a7ca9d2 is using its referenced image e124405f28f4

Но если я уберу контейнер …

1
2
3
4
5
6
7
8
9
> docker rmi oh-yeah-alpine
Untagged: oh-yeah-alpine:latest
Deleted: sha256:e124405f28f48e…441d774d9413139e22386c4820df
Deleted: sha256:7fa4cba6d14fdf…d8940e6c50d30a157483de06fc59
Deleted: sha256:283d461dadfa6c…dbff864c6557af23bc5aff9d66de
Deleted: sha256:1b2a228cc2a5b4…23c80a41a41da4ff92fcac95101e
Deleted: sha256:fe5fe2290c63a0…8af394bb4bf15841661f71c71e9a
 
> docker images |

Ага. Это ушло Но не волнуйся. Мы можем восстановить это:

1
2
3
4
> docker build -t oh-yeah-alpine .
 
> docker images |
oh-yeah-alpine latest 1e831ce8afe1 1 minutes ago 30.5 MB

Да, он вернулся. Dockerfile для победы!

Изображения в некоторых отношениях очень похожи на репозитории git. Они также построены из упорядоченного набора коммитов. Вы можете думать о двух изображениях, которые используют одни и те же базовые изображения как ветви (хотя в Docker нет слияния или перебазировки). Реестр изображений является эквивалентом центрального хостинга Git, такого как GitHub. Угадайте, как называется официальный реестр изображений Docker? Это верно, Docker Hub .

Когда вы запускаете образ, если он не существует, Docker попытается извлечь его из одного из ваших настроенных реестров образов. По умолчанию он переходит в Docker Hub, но вы можете управлять им в файле «~ / .docker / config.json». Если вы используете другой реестр, вы можете следовать их инструкциям, которые обычно включают вход в систему с использованием их учетных данных.

Давайте удалим образ «hello-world» и снова docker pull его, используя команду docker pull .

1
2
3
4
5
> dockere images |
hello-world latest c54a2cc56cbb 7 months ago 1.85 kB
 
> docker rmi hello-world
hello-world

Это ушло Давайте потянем сейчас.

1
2
3
4
5
6
7
8
9
> docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e…07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest
 
> dockere images |
hello-world latest 48b5124b2768 2 weeks ago 1.84 kB

Последний привет мир был заменен более новой версией.

Нажатие на изображения немного сложнее. Сначала вам нужно создать учетную запись на Docker Hub (или другом реестре). Затем вы входите в систему. Затем вам нужно пометить изображение, которое вы хотите нажать, в соответствии с именем вашей учетной записи («g1g1» в моем случае).

1
2
3
4
5
6
7
8
9
> docker login -u g1g1 -p <password>
Login Succeeded
 
> docker tag hello-world g1g1/hello-world
 
> docker images |
 
g1g1/hello-world latest 48b5124b2768 2 weeks ago 1.84 kB
hello-world latest 48b5124b2768 2 weeks ago 1.84 kB

Теперь я могу нажать на изображение с тегом g1g1 / hello-world.

1
2
3
4
> docker push g1g1/hello-world
The push refers to a repository [docker.io/g1g1/hello-world]
98c944e98de8: Mounted from library/hello-world
latest: digest: sha256:c5515758d4c5e…f6d12a1ac8d7 size: 524

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

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