Образы
Образ контейнера содержит исполняемые данные приложения и всех его программных зависимостей. Образы контейнеров — это исполняемые пакеты программного обеспечения, способные автономно работать и дополненные конкретными предположениями о соответствующей среде исполнения.
Как правило, образ контейнера с приложением предварительно собирается и размещается в реестре, после чего его можно использовать в Pod'е.
На этой странице представлено общее описание концепции контейнерных образов.
Названия образов
Образам контейнеров обычно присваивается имя, намекающее на их функционал и цели, например, pause
, example/mycontainer
или kube-apiserver
. Образы также могут включать имя хоста реестра, например, fictional.registry.example/imagename
, и (в некоторых случаях) номер порта, например, fictional.registry.example:10443/imagename
.
Если имя хоста реестра не указано, Kubernetes по умолчанию будет использовать публичный реестр Docker.
После имени образа можно добавить тег (как, например, в командах docker
и podman
). Теги помогают идентифицировать различные версии одной и той же линейки образов.
Теги образов могут состоять из строчных и прописных букв, цифр, знаков подчеркивания (_
), точек (.
) и дефисов (-
).
Кроме того, существуют дополнительные правила размещения символов-разделителей (_
, -
и .
) внутри тега.
Если тег не указан, Kubernetes по умолчанию использует тег latest
.
Обновление образов
При первоначальном создании объекта типа Deployment, StatefulSet, Pod или другого объекта, включающего шаблон Pod'а, политика извлечения всех контейнеров в этом Pod'е будет по умолчанию установлена на IfNotPresent
, если иное не указано явно. В рамках этой политики kubelet не извлекает образ, если тот уже присутствует в кэше.
Политика извлечения образов
Политика imagePullPolicy
контейнера и тег образа определяют поведение kubelet'а при извлечении (загрузке) данного образа.
Вот список возможных значений imagePullPolicy
и их влияние:
IfNotPresent
- образ извлекается только в том случае, если он еще не доступен локально.
Always
- каждый раз при запуске контейнера kubelet запрашивает дайджест образа в реестре образов контейнеров. Если полученный дайджест полностью совпадает с дайджестом кэшированного образа, kubelet использует кэшированный образ; иначе извлекается и используется образ с полученным дайждестом.
Never
- kubelet не пытается скачать образ. Если образ уже присутствует локально, kubelet пытается запустить контейнер; в противном случае запуск завершается неудачей. Для получения более подробной информации обратитесь к разделу о предварительно извлеченных (pre-pulled) образах.
Благодаря семантике кэширования, лежащей в основе механизма поставки образов, даже imagePullPolicy: Always
может быть вполне эффективной (при условии, что реестр надежно доступен). Исполняемая среда для контейнера может обнаружить, что слои образов уже имеются на узле и их не нужно скачивать еще раз.
Примечание:
Избегайте использования тега :latest
при развертывании контейнеров в production, поскольку в этом случае не понятно, какая именно версия образа используется и на какую ее нужно откатить при необходимости.
Всегда указывайте содержательный тег, например v1.42.0
.
Чтобы убедиться, что Pod всегда использует одну и ту же версию образа контейнера, можно указать дайджест образа вместо тега; для этого замените <image-name>:<tag>
на <image-name>@<digest>
(например, image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
).
Изменение кода, к которому привязан некий тег, может привести к тому, что в Pod'ах окажется две версии кода — старая и новая. Дайджест образа однозначно идентифицирует конкретную версию образа, что гарантирует идентичность кода при запуске контейнера с заданным именем образа и дайджестом. Таким образом, изменение кода в реестре уже не может привести к смешению версий.
Существуют сторонние admission-контроллеры, которые модифицируют Pod'ы (и их шаблоны) при создании, из-за чего рабочая нагрузка определяется на основе дайджеста образа, а не тега. Это может быть полезно в случаях, когда необходимо убедиться, что вся рабочая нагрузка использует идентичный код независимо от изменений тегов в реестре.
Политика извлечения образов по умолчанию
Когда информация о новом Pod'е поступает на сервер API, кластер устанавливает поле imagePullPolicy
в соответствии со следующими условиями:
imagePullPolicy
автоматически присваивается значениеAlways
, если полеimagePullPolicy
не задано, а тег для образа контейнера имеет значение:latest
;imagePullPolicy
автоматически присваивается значениеAlways
, если полеimagePullPolicy
не задано, а тег для образа контейнера не указан;imagePullPolicy
автоматически присваивается значениеIfNotPresent
, если полеimagePullPolicy
не задано, а тег для образа контейнера имеет значение, отличное от:latest
.
Примечание:
Значение imagePullPolicy
контейнера всегда устанавливается при первом создании объекта и не обновляется при последующем изменении тега образа.
Например, если в Deployment'е используется образ с тегом, отличным от :latest
, а потом он меняется на :latest
, поле imagePullPolicy
останется прежним (т.е. не будет изменено на Always
). После первоначального создания любого объекта его политику извлечения можно изменить вручную.
Обязательное извлечение образов
Для принудительного извлечения образов можно сделать следующее:
- Установить
imagePullPolicy
контейнера вAlways
; - Не устанавливать
imagePullPolicy
и использовать тег:latest
для образа; Kubernetes автоматически поменяет политику наAlways
, получив информацию о Pod'е; - Не устанавливать
imagePullPolicy
и тег образа; Kubernetes автоматически применит политикуAlways
, получив информацию о Pod'е; - Включить admission-контроллер AlwaysPullImages.
ImagePullBackOff
При создании kubelet'ом контейнеров для Pod'а может возникнуть ситуация, когда контейнер пребывает в состоянии Waiting из-за ImagePullBackOff
.
Статус ImagePullBackOff
означает, что контейнер не может запуститься, поскольку у Kubernetes не получается извлечь его образ (например, из-за ошибки в имени или попытки извлечь образ из приватного репозитория без imagePullSecret
). BackOff
в названии статуса указывает на то, что Kubernetes будет продолжать попытки извлечь образ, постепенно увеличивая интервал между ними.
Так, интервал между попытками будет расти до тех пор, пока не достигнет установленного предела в 300 секунд (5 минут).
Мультиархитектурные образы с индексами
Помимо обычных исполняемых образов реестр контейнеров также может хранить так называемые индексы образов. Индекс образа содержит ссылки на различные манифесты образов, каждый из которых предназначен для определенной архитектуры. Идея здесь в том, чтобы любой пользователь мог получить образ, оптимизированный под конкретную архитектуру, используя его унифицированное, общее для всех архитектур имя (например, pause
, example/mycontainer
, kube-apiserver
).
Сам Kubernetes обычно добавляет суффикс -$(ARCH)
к имени образа. Для обратной совместимости также рекомендуется генерировать образы с суффиксами в названиях. Например, универсальный образ pause
, содержащий манифест для всех архитектур, рекомендуется дополнить образом pause-amd64
для обратной совместимости со старыми конфигурациями или YAML-файлами, в которых могут быть жестко прописаны образы с суффиксами.
Работа с приватным реестром
Для чтения образов из приватных реестров могут потребоваться соответствующие ключи. Доступ к таким реестрам можно получить следующими способами:
- Аутентификация на уровне узлов:
- все Pod'ы имеют доступ ко всем настроенным приватным реестрам;
- требуется конфигурация узлов администратором кластера;
- Предварительно извлеченные образы:
- все Pod'ы могут использовать любые образы, кэшированные на узле;
- для настройки требуется root-доступ ко всем узлам;
- imagePullSecrets на уровне Pod'а:
- доступ к реестру получают только Pod'ы с ключами;
- Специализированные расширения от вендора/пользователя:
- в кастомных конфигурациях могут существовать специализированные механизмы аутентификации узлов в реестре контейнеров, реализованные самим пользователем или поставщиком облачных услуг.
Ниже мы подробнее остановимся на каждом из вариантов.
Аутентификация на уровне узлов
Конкретные инструкции по настройке учетных данных зависят от среды исполнения контейнера и реестра. Для получения наиболее подробной информации следует обратиться к документации используемого решения.
Пример настройки частного реестра образов контейнеров приводится в упражнении Извлекаем образ из частного реестра. В нем используется частный реестр в Docker Hub.
Интерпретация config.json
Интерпретация config.json
отличается в оригинальной Docker-реализации и в Kubernetes. В Docker ключи auths
могут указывать только корневые URL, в то время как Kubernetes позволяет использовать URL с подстановками (globbing) и пути с префиксами. То есть config.json
, подобный этому, вполне допустим:
{
"auths": {
"*my-registry.io/images": {
"auth": "…"
}
}
}
Корневой URL (*my-registry.io
) сопоставляется с помощью следующего синтаксиса:
pattern:
{ term }
term:
'*' соответствует любой последовательности символов, не являющихся разделителями
'?' соответствует любому одиночному символу, не являющемуся разделителем
'[' [ '^' ] { диапазон символов } ']'
класс символов (не может быть пустым)
c соответствует символу c (c != '*', '?', '\\', '[')
'\\' c соответствует символу c
диапазон символов:
c соответствует символу c (c != '\\', '-', ']')
'\\' c соответствует символу c
lo '-' hi соответствует символу c при lo <= c <= hi
Учетные данные теперь будут передаваться в CRI-совместимую исполняемую среду для контейнеров для каждого действительного шаблона. Ниже приведены примеры имен образов, удовлетворяющие требованиям к паттерну:
my-registry.io/images
my-registry.io/images/my-image
my-registry.io/images/another-image
sub.my-registry.io/images/my-image
a.sub.my-registry.io/images/my-image
kubelet последовательно извлекает образы для каждой обнаруженной учетной записи. Это означает, что config.json
может содержать сразу несколько записей:
{
"auths": {
"my-registry.io/images": {
"auth": "…"
},
"my-registry.io/images/subpath": {
"auth": "…"
}
}
}
К примеру, если необходимо извлечь образ my-registry.io/images/subpath/my-image
, kubelet будет пытаться загрузить его из второго источника, если первый не работает.
Предварительно извлеченные образы
Примечание:
Этот подход применим, если имеется доступ к конфигурации узлов. Он не будет надежно работать, если поставщик облачных услуг управляет узлами и автоматически заменяет их.По умолчанию kubelet пытается извлечь каждый образ из указанного реестра. Однако если параметр imagePullPolicy
контейнера установлен на IfNotPresent
или Never
, используется локальный образ (преимущественно или исключительно, соответственно).
Чтобы использовать предварительно извлеченные образы (и не связываться с аутентификацией для доступа к реестру), необходимо убедиться, что они идентичны на всех узлах кластера.
Предварительная загрузка образов позволяет увеличить скорость работы и является альтернативой аутентификации в приватном реестре.
При этом у всех Pod'ов будет доступ на чтение всех предварительно извлеченных образов.
Задаем imagePullSecrets на уровне Pod'а
Примечание:
Это рекомендуемый подход для запуска контейнеров на основе образов в приватных реестрах.Kubernetes поддерживает указание ключей реестра образов на уровне Pod'а.
Создаем Secret с помощью конфигурационного файла Docker
Для аутентификации в реестре необходимо знать имя пользователя, пароль, имя хоста реестра и адрес электронной почты клиента.
Выполните следующую команду, подставив соответствующие значения вместо параметров, выделенных заглавными буквами:
kubectl create secret docker-registry <name> --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
При наличии файла учетных данных Docker можно импортировать их как Secret'ы Kubernetes вместо команды, приведенной выше.
В разделе Создание Secret'а на основе существующих учетных данных Docker рассказывается, как это можно сделать.
Это особенно удобно в случае нескольких приватных реестров контейнеров, так как kubectl create secret docker-registry
создает Secret, который работает только с одним приватным реестром.
Примечание:
Pod'ы могут работать только с Secret'ами в собственном пространстве имен, поэтому данный процесс необходимо повторить для каждого пространства имен.Ссылаемся на imagePullSecrets в Pod'е
Теперь можно создавать Pod'ы, ссылающиеся на данный Secret, добавив раздел imagePullSecrets
в манифест Pod'а.
Например:
cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistrykey
EOF
cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF
Это необходимо проделать для каждого Pod'а, работающего с приватным репозиторием.
Процесс можно автоматизировать, задав imagePullSecrets в ресурсе ServiceAccount.
Подробные инструкции см. в разделе Добавить ImagePullSecrets в Service Account.
Этот подход можно использовать совместно с файлами .docker/config.json
, определяемыми для каждого узла. Учетные данные будут объединены.
Примеры использования
Существует ряд решений для настройки приватных реестров. Вот некоторые распространенные случаи использования и рекомендуемые решения:
- Кластер, работающий только со свободными (например, Open Source) образами. Необходимость скрывать образы отсутствует.
- Используйте общедоступные образы из Docker Hub;
- Настройка не требуется;
- Некоторые облачные провайдеры автоматически кэшируют или зеркалируют публичные образы, что повышает доступность и сокращает время их извлечения.
- Используйте общедоступные образы из Docker Hub;
- В кластере используются закрытые образы. Они должны быть скрыты для всех за пределами компании, но доступны для всех пользователей кластера.
- Используйте приватный репозиторий;
- Может потребоваться ручная настройка на узлах, которым необходим доступ к частному репозиторию;
- В качестве альтернативы можно завести внутренний приватный реестр с доступом на чтение, скрыв его за сетевым экраном;
- Настройка Kubernetes не требуется;
- Используйте сервис для работы с образами, контролирующий доступ к ним;
- Этот подход лучше работает с автомасштабированием кластера, нежели ручная настройка узлов;
- Если изменение конфигурации узлов в кластере затруднено, можно использовать imagePullSecrets.
- Используйте приватный репозиторий;
- Кластер с несвободными образами, некоторые из которых требуют более строгого контроля доступа.
- Убедитесь, что AlwaysPullImages admission-контроллер включен. В противном случае у всех Pod'ов потенциально будет доступ ко всем образам;
- Переместите конфиденциальные данные в Secret вместо того, чтобы упаковывать их в образ.
- Кластер категории multi-tenant (многопользовательский), где каждому пользователю требуется собственный приватный репозиторий.
- Убедитесь, что admission-контроллер AlwaysPullImages включен. В противном случае у всех Pod'ов всех пользователей потенциально будет доступ ко всем образам;
- Создайте приватный реестр с обязательной авторизацией;
- Сгенерируйте учетные данные для доступа к реестру для каждого пользователя, поместите их в Secret и добавьте его в пространство имен каждого пользователя;
- Каждый пользователь должен добавить свой Secret в imagePullSecrets каждого пространства имен.
Если нужен доступ к нескольким реестрам, можно создать по Secret'у для каждого реестра.
Что дальше
- Спецификация манифестов образов OCI.
- Сборка "мусора" в Kubernetes — неиспользуемые контейнеры и образы.
- Извлечение образов из приватных репозиториев.