Контейнеризация. Часть 2: Седлаем кита.

Дисклеймер

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

Алсо, Часть 1

Напоминалка

Dockerfile содержит в себе набор инструкций с помощью которых конфигурируется твой будущий контейнер. Чаще всего он лежит в папке с кодом твоей великолепнейшей приложухи и упаковывает ее в красивую коробочку с помощью команды docker build.

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

Шпаргалка со списком инструкций

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

  • RUN — выполняет команду, заданную в качестве аргумента
  • COPY — выполняет копирование файлов в указанное место контейнера
  • ADD — аналогичен COPY,за исключением пары нюансов
  • FROM — задаёт базовый образ контейнера
  • LABEL — служит для записи метаданных. Сюда можно добавить все,что твоей душе угодно: имя автора, версию контейнера,номер мобильного телефона,размер МПХ.Обычно все-таки ограничиваются авторством,версией и датой
  • ENV — устанавливает постоянные переменные среды
  • CMD — описывает команду с аргументами, которую нужно выполнить при запуске контейнера. Одна на файл. Аргументы могут быть переопределены при запуске контейнера
  • WORKDIR — определяет рабочую директорию для следующей инструкции
  • ARG — задаёт переменные для передачи в Docker во время сборки образа
  • ENTRYPOINT — предоставляет команду с аргументами для вызова во время выполнения контейнера, аргументы этой команды НЕ ПЕРЕОПРЕДЕЛЯЮТСЯ
  • USER — определяет пользователя,от чьего имени будут выполняться команды
  • EXPOSE — открывает для взаимодействия указанный порт Docker контейнера
  • VOLUME — указывает путь к постоянному хранилищу

EXEC- и SHELL-формы

Для инструкций Dockerfile обычно принято прописывать команды. И для этого существует как минимум два типа синтаксиса, например, команды для инструкций RUN, CMD, ENTRYPOINT, FROM могут задаваться в двух формах: shell и exec.

Exec-форма позволяет задать команду в виде JSON-массива. Каждая команда в массиве должна выделяться с помощью «, а не ‘.

# Запуск nginx с отключением демонизации в Docker-файле.
RUN ["/usr/sbin/nginx", "-g daemon off;"]

С синтаксисом exec-формы связаны следующие особенности:

  • Она не вызывает командную оболочку, например, в команде, приведенной ниже, в переменную $HOME не подставится значение.
RUN [ "echo", "$HOME" ]
  • Для вызова оболочки в exec-форме нужно исполнить что-то вроде этого:
RUN ["sh", "-c", "echo $HOME"]

# Пример нескольких команд в одной RUN инструкции
RUN ["sh","-c","mkdir -p ~/my/new/directory/ && cd ~/my/new/directory && touch new.file"]
  • В exec-форме также необходимо экранировать «\», это тебе точно пригодится при сборке контейнера с windows-машины.
RUN ["c:\\windows\\system32\\myAwesomeScript.exe"]

Shell-форма по стилю близка к привычным башовским командам. В этой форме поданная на вход команда выполняется с помощью дефолтной программной оболочки контейнера (обычно sh), оболочки передаваемой в качестве параметра или оболочки, задаваемой с помощью инструкции SHELL (не перепутай). В shell-форме с помощью «\» можно передавать несколько команд в оболочку сразу.

# Так
RUN yum install -y epel-release \
    yum install -y nginx

# Или так
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

NOTE: Обязательно объединяй несколько логически связанных команд в одну инструкцию. Зачем — ниже.

We need to go deeper

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

  • FROM — любой Dockerfile начинается с этой команды, потому что она задает имя образа ОС, который будет взят за основу для нового контейнера.
# Задаем в качестве базового образа CentOs седьмой версии
FROM centos:7

# Или Alpine
FROM gliderlabs/alpine:3.4
  • RUN — создает новый слой во время сборки Docker-образа, запускает в нем указанную команду и коммитит изменения после ее выполнения. Чаще всего нужна для загрузки и установки дополнительных пакетов.
# Часто встречаемая комбинация команд для Alpine образов
# обновляет пакеты базового образа и устанавливает bash
RUN apk update && apk upgrade && apk add bash
  • COPY — сообщает Docker о том, что нужно взять файлы и папки из локального контекста сборки и добавить их в текущую рабочую директорию образа. Если целевая директория не существует, эта инструкция её создаст.
  • ADD — аналогична COPY но умеет немного больше. Во-первых, ADD может в добавление файлов из удаленных источников. Во-вторых — умеет распаковывать локальные tar-ники.

NOTE: Dockerfile при своем исполнении знает только про папку, в которой он лежит, и про дочерние относительно первой.

NOTE 2: Обрати свое пристальное внимание на инструкции RUN, ADD, COPY и FROM. Каждая из них создает новый слой в твоем контейнере, тем самым делая его немного больше. Поэтому не злоупотребляй ими.

Например,вот такой кусок кода:

FROM gliderlabs/alpine:3.4
RUN apk update
RUN apk upgrade
RUN apk add bash

Создаст вот такую структуру контейнера:

Не надо так
  • ENV — задает переменные и их значения, которые доступны как на этапе сборки контейнера, так и во время его работы (в отличие от ARG-переменных). ENV-переменные могут быть перезаписаны при запуске контейнера. Рекомендуется использовать для задания констант, не содержащих чувствительной информации.
ENV MY_AWESOME_VARIABLE="toadsAreAwesome"

RUN if [ "$MY_AWESOME_VARIABLE" = "toadsAreAwesome" ] ; then echo "agreed"; fi
  • ENTRYPOINT — задает команду, с которой запустится контейнер. Запускается в exec- и shell-формах, имеет статус «все очень сложно» с аргументами CMD.
FROM ubuntu

# Запустит процесс с командой top при запуске контейнера
ENTRYPOINT ["top", "-b"]
Entrypointscreen
Те самые сложные отношения
  • EXPOSE — показывает,какие порты откроются,если запустить контейнер с помощью команды docker run -P. Позволяет открывать TCP- и UDP-порты, TCP-порты являются дефолтными.
# Дефолтный TCP
EXPOSE 80

# Пытаемся поумничать (зря)
EXPOSE 80/tcp

# Пытаемся поумничать (не зря)
EXPOSE 80/udp

# Маппинг двух типов на один порт
EXPOSE 80/tcp
EXPOSE 80/udp
  • USER — определяет пользователя/пользователя и группу от имени которого будут выполняться последующие RUN / CMD / ENTRYPOINT команды.
# Можно задать имена
USER <user>[:<group>]

# Или идентификаторы
USER <UID>[:<GID>]

NOTE: Если ты задал группу у пользователя — он будет принадлежать ТОЛЬКО ей. Другие РАНЕЕ сконфигурированные группы будут игнорироваться. Дефолтная группа — рутовская.

  • LABEL — инструкция для творческих людей.Позволяет выражать себя с помощью печатных символов на любом языке мира. LABEL-ы родительских образов наследуются дочерними. Опции задаются в формате ключ = значение.
LABEL version="1.0"
LABEL author="megamind"
  • WORKDIR — задает папку в которой будет исполняться следующая за ней RUN / CMD / ENTRYPOINT / COPY / ADD команда. Можно извращаться по всякому:
# Вот так
WORKDIR /path/to/mydir
RUN pwd -> /path/to/mydir

# Или так
ENV CUSTOMPATH /path
WORKDIR $CUSTOMPATH/mydir
RUN pwd -> /path/mydir

# И даже так
WORKDIR /path
WORKDIR mydir
RUN pwd -> /path/mydir

Примеры

Рассуждать по поводу инструкций можно еще долго и подробно, но без примеров ты все равно хрен что запомнишь. А запоминать нужно, иначе я вообще не понимаю, зачем ты это читаешь. Тащемта, примеры:

  • Бесполезный, но разъясняющий
# Качаем питон3 и Alpine с Dockerhub
FROM python:3.7.2-alpine3.8

# Ну а кто еще
LABEL clitor_commander="ME"

# Обновляет локальные файлы Alpine и скачивает bash
RUN apk update && apk upgrade && apk add bash

# Копирует папку, в которой лежит Dockerfile на локальной машине в папку /app в образе.
COPY . ./app

# Качаем файл с удаленного источника в папку /my_dir (подумой)
ADD https://yadi.sk/i/C_RhRN7YRbbqgw \
/my_dir

# Просто создаем еще папку,а почему бы и нет
RUN ["mkdir", "/a_directory"]
# Собираем контейнер и называем его ma_picture и задаем тег precious
# Запусти в папке с Dockerfile
sudo docker build -t ma_picture:precious .
  • Чуть более полезный, даже Nginx есть
# Создаем невероятной сложности сервер и в той же папке Dockefile
mkdir http_server
cd http_server/
echo '<HTML><H1>My first http server in docker</H1></HTML>' > index.html
touch Dockerfile
# Тащемта, то,что в этом файле будет
FROM centos:7

LABEL master="ME"

# Грузим репозиторий nginx
RUN yum install -y epel-release

# Грузим nginx
RUN yum install -y nginx

# Магия
ADD index.html /usr/share/nginx/html/index.html

# Открываем TCP на 80-м порту
EXPOSE 80

# Запускаем процесс nginx и выключаем его демонизацию (иначе контейнер сразу помрет)
CMD ["/usr/sbin/nginx", "-g daemon off;"]
# Собираем контейнер, называем его http_server и задаем тег best
# Запусти в папке с Dockerfile
sudo docker build -t http_server:best .

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

Возможно, Вам понравится:

guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x
()
x