<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Image-Size on DevOps Way - Практические гайды</title>
    <link>https://devopsway.ru/tags/image-size/</link>
    <description>Recent content in Image-Size on DevOps Way - Практические гайды</description>
    <image>
      <title>DevOps Way - Практические гайды</title>
      <url>https://devopsway.ru/images/devopsway-og.png</url>
      <link>https://devopsway.ru/images/devopsway-og.png</link>
    </image>
    <generator>Hugo -- 0.161.1</generator>
    <language>ru</language>
    <lastBuildDate>Mon, 18 May 2026 13:07:36 -0400</lastBuildDate>
    <atom:link href="https://devopsway.ru/tags/image-size/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Docker Level 04: .dockerignore -- Образ 2GB, внутри .env с паролями</title>
      <link>https://devopsway.ru/posts/docker-04-dockerignore/</link>
      <pubDate>Mon, 18 May 2026 13:00:00 +0300</pubDate>
      <guid>https://devopsway.ru/posts/docker-04-dockerignore/</guid>
      <description>Level 04: почему образ весит 2GB и содержит .env с паролями. Как .dockerignore защищает от утечек и ускоряет сборку.</description>
      <content:encoded><![CDATA[<h2 id="боль">БОЛЬ</h2>
<p>Ревью Docker-образа перед деплоем. <code>docker images</code> показывает 2.1GB для простого Node.js-приложения. Запускаете <code>docker run --rm myapp ls -la</code> и видите: <code>node_modules</code> (800MB), <code>.git</code> (400MB), <code>test-data</code> (500MB). И самое страшное &ndash; <code>.env</code> с <code>DB_PASSWORD</code>, <code>JWT_SECRET</code>, <code>AWS_SECRET_KEY</code>.</p>
<p>Образ уже в Docker Hub. Пароли в открытом доступе. Даже если удалить <code>.env</code> в следующем слое &ndash; он останется в истории образа. <code>docker history</code> покажет всё.</p>
<p>Исправление: <strong><code>.dockerignore</code></strong> &ndash; файл-фильтр, который не пускает лишнее в контекст сборки.</p>
<h2 id="как-устроено">КАК УСТРОЕНО</h2>
<p>Когда вы запускаете <code>docker build .</code>, Docker отправляет в daemon весь текущий каталог &ndash; это <strong>build context</strong>. Всё, что в контексте &ndash; доступно для <code>COPY</code> и <code>ADD</code>. Всё, что не нужно &ndash; замедляет сборку и раздувает образ.</p>
<p><code>.dockerignore</code> работает как <code>.gitignore</code>: указывает файлы и директории, которые НЕ попадут в build context.</p>
<p>Что должно быть в <code>.dockerignore</code> почти всегда:</p>
<table>
  <thead>
      <tr>
          <th>Паттерн</th>
          <th>Зачем</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>.git</code></td>
          <td>История репозитория, сотни мегабайт</td>
      </tr>
      <tr>
          <td><code>node_modules</code></td>
          <td>Зависимости &ndash; ставятся при сборке через <code>npm ci</code></td>
      </tr>
      <tr>
          <td><code>.env</code></td>
          <td>Секреты, пароли, токены</td>
      </tr>
      <tr>
          <td><code>*.log</code></td>
          <td>Логи &ndash; не нужны в образе</td>
      </tr>
      <tr>
          <td><code>Dockerfile</code></td>
          <td>Сам Dockerfile не нужен внутри</td>
      </tr>
      <tr>
          <td><code>.dockerignore</code></td>
          <td>Мета-файл</td>
      </tr>
      <tr>
          <td><code>tests/</code> / <code>__tests__/</code></td>
          <td>Тесты &ndash; не нужны в production</td>
      </tr>
      <tr>
          <td><code>*.md</code></td>
          <td>Документация</td>
      </tr>
      <tr>
          <td><code>.vscode</code> / <code>.idea</code></td>
          <td>Настройки редактора</td>
      </tr>
  </tbody>
</table>
<h2 id="практика">ПРАКТИКА</h2>
<p>Составьте правильный <code>.dockerignore</code> для Node.js-проекта. Расставьте строки в логичном порядке: сначала VCS и среда, затем зависимости, секреты, тесты и документация.</p>
<div class="docker-sort" id="level04-quiz" data-answer="WyIuZ2l0IiwiLmdpdGlnbm9yZSIsIi52c2NvZGUiLCIuaWRlYSIsIm5vZGVfbW9kdWxlcyIsIi5lbnYiLCIuZW52LioiLCJEb2NrZXJmaWxlIiwiLmRvY2tlcmlnbm9yZSIsInRlc3RzLyIsImNvdmVyYWdlLyIsIioubG9nIiwiKi5tZCJd">
  <p class="docker-sort__title">Расставьте .dockerignore в правильном порядке</p>
  <ul class="docker-sort__list"><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">*.md</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">.env</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">.env.*</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">node_modules</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">.git</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">.gitignore</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">Dockerfile</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">.dockerignore</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">tests/</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">coverage/</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">*.log</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">.vscode</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">.idea</span>
    </li></ul>
  <div class="docker-sort__actions">
    <button class="docker-sort__btn docker-sort__btn--check" type="button">Проверить</button>
    <button class="docker-sort__btn docker-sort__btn--reset" type="button">Сбросить</button>
  </div>
  <div class="docker-sort__result"></div><div class="docker-sort__explain">Логичная группировка: (1) VCS и редактор &ndash; <code>.git</code>, <code>.gitignore</code>, <code>.vscode</code>, <code>.idea</code>; (2) зависимости &ndash; <code>node_modules</code>; (3) секреты &ndash; <code>.env</code>, <code>.env.*</code>; (4) мета-файлы Docker &ndash; <code>Dockerfile</code>, <code>.dockerignore</code>; (5) тесты и артефакты &ndash; <code>tests/</code>, <code>coverage/</code>, <code>*.log</code>; (6) документация &ndash; <code>*.md</code>. Порядок внутри <code>.dockerignore</code> технически не важен, но группировка помогает при ревью.</div></div>

<h2 id="разбор">РАЗБОР</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># .dockerignore
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># VCS и настройки редактора
</span></span><span class="line"><span class="cl">.git
</span></span><span class="line"><span class="cl">.gitignore
</span></span><span class="line"><span class="cl">.vscode
</span></span><span class="line"><span class="cl">.idea
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Зависимости (ставятся при сборке)
</span></span><span class="line"><span class="cl">node_modules
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Секреты — НИКОГДА не должны попасть в образ
</span></span><span class="line"><span class="cl">.env
</span></span><span class="line"><span class="cl">.env.*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Docker мета-файлы
</span></span><span class="line"><span class="cl">Dockerfile
</span></span><span class="line"><span class="cl">.dockerignore
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Тесты и покрытие
</span></span><span class="line"><span class="cl">tests/
</span></span><span class="line"><span class="cl">coverage/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Логи и документация
</span></span><span class="line"><span class="cl">*.log
</span></span><span class="line"><span class="cl">*.md
</span></span></code></pre></div><p>Эффект:</p>
<ul>
<li><strong>Размер</strong>: контекст сборки уменьшается с 2GB до десятков мегабайт</li>
<li><strong>Скорость</strong>: <code>docker build</code> не копирует гигабайты в daemon</li>
<li><strong>Безопасность</strong>: <code>.env</code> физически не попадает в образ, даже случайный <code>COPY . .</code> не утянет секреты</li>
</ul>
<p>Проверить что попадает в контекст:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Размер контекста видно в первой строке docker build</span>
</span></span><span class="line"><span class="cl">docker build . 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> head -1
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Sending build context to Docker daemon  45.2MB</span>
</span></span></code></pre></div><h2 id="вопрос-на-собесе">ВОПРОС НА СОБЕСЕ</h2>
<p><strong>Вопрос:</strong> Секрет попал в Docker-образ через <code>COPY</code>. Удаление файла в следующем слое решает проблему?</p>
<details class="expand-container expand-default ">
    <summary class="expand-header">
        <span class="expand-icon">▶</span>
        <span class="expand-title">Показать ответ</span>
        <span class="expand-chevron">⌄</span>
    </summary>
    <div class="expand-content">
        <p>Нет. Docker-образ состоит из слоёв, и каждый слой иммутабелен. Если в слое 3 скопировали <code>.env</code>, а в слое 4 удалили &ndash; файл всё ещё есть в слое 3. Любой, кто скачает образ, может извлечь его:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Посмотреть историю слоёв</span>
</span></span><span class="line"><span class="cl">docker <span class="nb">history</span> myapp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Извлечь файловую систему конкретного слоя</span>
</span></span><span class="line"><span class="cl">docker save myapp <span class="p">|</span> tar -xf - --include<span class="o">=</span><span class="s1">&#39;*/layer.tar&#39;</span>
</span></span></code></pre></div><p>Правильные способы работы с секретами:</p>
<ol>
<li><strong><code>.dockerignore</code></strong> &ndash; секреты не попадают в контекст сборки</li>
<li><strong>BuildKit secrets</strong> &ndash; <code>docker build --secret id=mysecret,src=.env</code> + <code>RUN --mount=type=secret,id=mysecret</code></li>
<li><strong>Переменные окружения</strong> &ndash; передаются при <code>docker run -e</code>, не запекаются в образ</li>
<li><strong>Vault / Secrets Manager</strong> &ndash; приложение получает секреты в runtime</li>
</ol>
<p>Если секрет уже попал в образ &ndash; пересоберите с нуля (<code>docker build --no-cache</code>) и отзовите скомпрометированные credentials.</p>

    </div>
</details>

]]></content:encoded>
    </item>
  </channel>
</rss>
