Пошук уроків, статей та іншого контенту
Створимо власний образ за допомогою Dockerfile та розберемо шари, інструкції й кешування під час збірки.
Docker-образ — це незмінний шаблон, з якого запускаються контейнери. Він містить:
базову операційну систему або її частину;
файли застосунку;
встановлені залежності;
налаштування запуску.
Образ складається з послідовності шарів. Кожен шар містить зміни порівняно з попереднім:
базовий образ;
встановлені пакети;
файли застосунку;
інші зміни файлової системи.
Шари доступні лише для читання й можуть повторно використовуватися різними образами. Наприклад, кілька образів на основі python:3.12-slim не зберігатимуть копію базового шару для кожного образу.
Коли з образу запускається контейнер, Docker додає до шарів образу окремий записуваний шар контейнера. Зміни в ньому належать конкретному контейнеру й не змінюють сам образ.
Dockerfile — це текстовий файл з інструкціями, за якими Docker створює образ.
Базова структура Dockerfile:
FROM базовий_образ
WORKDIR /робоча/директорія
COPY файли призначення
RUN команда
CMD ["команда", "аргумент"]Найпоширеніші інструкції:
FROM — задає базовий образ;
WORKDIR — встановлює робочу директорію;
COPY — копіює файли з контексту збірки в образ;
RUN — виконує команду під час збірки;
ENV — встановлює змінні середовища;
EXPOSE — документує порт, який використовує застосунок;
USER — задає користувача для наступних інструкцій і запуску;
CMD — команда за замовчуванням під час запуску контейнера.
CMD не запускається під час збірки. Він зберігається в конфігурації образу й виконується, коли створюється контейнер.
Команда збірки має вигляд:
docker build -t demo-web .Крапка в кінці означає, що поточна директорія є контекстом збірки. Docker може використовувати файли з цього контексту в інструкціях COPY і ADD.
Наприклад, така команда копіює файл із контексту:
COPY app.py .Dockerfile не може скопіювати файл, який розташований поза контекстом збірки. Тому важливо запускати docker build із правильної директорії.
Створимо невеликий HTTP-сервер на Flask.
Структура проєкту:
docker-demo/
├── Dockerfile
├── .dockerignore
├── requirements.txt
└── app.pyapp.pyfrom flask import Flask
app = Flask(__name__)
@app.get("/")
def index():
# Повертаємо просту відповідь для перевірки контейнера
return {"message": "Привіт із Docker-контейнера!"}
if __name__ == "__main__":
# Сервер має слухати всі мережеві інтерфейси контейнера
app.run(host="0.0.0.0", port=8000)requirements.txtFlask==3.0.3.dockerignore.git
.gitignore
__pycache__
*.pyc
.venv
.env.dockerignore виключає файли з контексту збірки. Це:
зменшує обсяг даних, які передаються Docker-демону;
прискорює збірку;
не дозволяє випадково додати секрети або локальні файли до образу.
DockerfileFROM python:3.12-slim
WORKDIR /app
RUN useradd --create-home appuser
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=appuser:appuser app.py .
USER appuser
EXPOSE 8000
CMD ["python", "app.py"]Розглянемо Dockerfile по кроках:
FROM python:3.12-slim
Використовує компактний образ із Python.
WORKDIR /app
Створює робочу директорію та робить її поточною для наступних інструкцій.
RUN useradd --create-home appuser
Створює окремого користувача для запуску застосунку.
COPY requirements.txt .
Копіює файл із залежностями в образ.
RUN pip install --no-cache-dir -r requirements.txt
Встановлює залежності. Прапорець --no-cache-dir не зберігає кеш пакетів у фінальному образі.
COPY --chown=appuser:appuser app.py .
Копіює код застосунку та одразу встановлює його власником користувача appuser.
USER appuser
Вказує, що застосунок не повинен запускатися від імені root.
EXPOSE 8000
Документує порт застосунку. Ця інструкція сама по собі не публікує порт на комп’ютері.
CMD ["python", "app.py"]
Визначає команду за замовчуванням для контейнера.
Перейдіть у директорію проєкту й виконайте:
docker build -t demo-web .Після успішної збірки запустіть контейнер:
docker run --rm -p 8000:8000 demo-webПараметри команди:
--rm — автоматично видалити контейнер після його зупинки;
-p 8000:8000 — зв’язати порт 8000 комп’ютера з портом 8000 контейнера;
demo-web — назва образу.
В іншому терміналі перевірте сервер:
curl http://localhost:8000/Очікувана відповідь:
{"message":"Привіт із Docker-контейнера!"}Зупинити контейнер можна комбінацією Ctrl+C.
Під час виконання Dockerfile Docker показує кроки збірки:
[1/8] FROM python:3.12-slim
[2/8] WORKDIR /app
[3/8] RUN useradd --create-home appuser
[4/8] COPY requirements.txt .
[5/8] RUN pip install --no-cache-dir -r requirements.txt
[6/8] COPY app.py .
[7/8] USER appuser
[8/8] EXPOSE 8000Інструкції, які змінюють файлову систему, зокрема RUN і COPY, створюють шари або беруть готовий результат із кешу. Інструкції на кшталт WORKDIR, USER, EXPOSE і CMD переважно змінюють конфігурацію образу.
Переглянути історію шарів можна так:
docker history demo-webУ результаті будуть показані команди, розміри шарів і час їх створення.
Docker кешує результати окремих кроків збірки. Якщо Docker визначає, що інструкція та її вхідні дані не змінилися, він може використати раніше створений результат.
Зберігати кеш можна між збірками:
docker build -t demo-web .Якщо змінити лише app.py, Docker зазвичай повторно використає кеш для:
базового образу;
створення користувача;
копіювання requirements.txt;
встановлення Python-залежностей.
Повторно виконається крок:
COPY --chown=appuser:appuser app.py .і наступні кроки.
Якщо змінити requirements.txt, кеш для цього кроку та всіх наступних кроків стане недійсним. Docker повторно встановить залежності й скопіює код.
Залежності зазвичай змінюються рідше, ніж код застосунку. Тому файл із залежностями копіюють окремо:
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .Невдалий варіант виглядав би так:
COPY . .
RUN pip install --no-cache-dir -r requirements.txtУ такому випадку будь-яка зміна в коді, документації чи іншому файлі контексту може змусити Docker повторно виконати встановлення залежностей.
Правильний порядок:
скопіювати файли, які змінюються рідко;
встановити залежності;
скопіювати код, який змінюється часто.
Це зменшує час повторних збірок.
Кеш кроку може стати недійсним, якщо:
змінилася сама інструкція Dockerfile;
змінилися файли, які використовує COPY;
змінився базовий образ;
попередній крок більше не може бути взятий із кешу.
Після першого кроку без кешу наступні кроки також зазвичай виконуються заново, навіть якщо їхні файли не змінювалися.
Щоб примусово зібрати образ без кешу, використовуйте:
docker build --no-cache -t demo-web .Це корисно, коли потрібно перевірити збірку з нуля або отримати актуальні результати команд RUN.
RUNКожна інструкція RUN, яка змінює файлову систему, може додати окремий шар. Тому пов’язані операції часто виконують в одній інструкції.
Наприклад:
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*Оновлення індексу пакетів, встановлення пакета та очищення тимчасових файлів виконуються в одному шарі. Очищення в наступній інструкції не зменшило б розмір попереднього шару, оскільки старі дані вже залишилися в історії образу.
Для Python у нашому прикладі використано:
RUN pip install --no-cache-dir -r requirements.txtЦе не зберігає локальний кеш pip у створеному шарі.
Список локальних образів:
docker image lsІнформація про образ:
docker image inspect demo-webСписок запущених контейнерів:
docker psСписок усіх контейнерів, включно із зупиненими:
docker ps -aЛоги контейнера:
docker logs <container_id>Для іменованого контейнера можна запустити його так:
docker run --name demo-web-container -p 8000:8000 demo-webПісля цього логи доступні командою:
docker logs demo-web-containerlocalhostТакий код не дозволить звернутися до сервера з-поза контейнера:
app.run(host="127.0.0.1", port=8000)У контейнері сервер має слухати всі інтерфейси:
app.run(host="0.0.0.0", port=8000)EXPOSE і публікацію портуІнструкція:
EXPOSE 8000лише описує порт образу.
Публікація порту виконується під час запуску:
docker run -p 8000:8000 demo-webІнструкція:
COPY app.py .працює лише для файлу, який є в контексті збірки та не виключений через .dockerignore.
Якщо спочатку виконати COPY . ., кеш для встановлення залежностей може часто інвалідуватися. Файл requirements.txt потрібно копіювати окремо перед кодом застосунку.
rootКонтейнер не повинен без потреби запускати застосунок із максимальними привілеями. Для цього в Dockerfile створюють користувача та використовують:
USER appuserФайли .env, ключі та локальні директорії не слід копіювати в образ. Додайте їх до .dockerignore і не записуйте секрети безпосередньо в Dockerfile.
Docker-образ складається з незмінних шарів.
FROM, RUN, COPY та інші інструкції описують процес створення образу.
RUN і COPY зазвичай створюють шари файлової системи, а CMD, USER і EXPOSE налаштовують образ.
Docker кешує результати кроків збірки.
Зміна кроку інвалідує кеш цього та наступних кроків.
Файли, які змінюються рідко, варто копіювати перед файлами застосунку.
.dockerignore зменшує контекст збірки й запобігає потраплянню зайвих файлів до образу.
EXPOSE не публікує порт — для цього потрібен параметр -p під час запуску контейнера.