<?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>Cache on DevOps Way - Практические гайды</title>
    <link>https://devopsway.ru/tags/cache/</link>
    <description>Recent content in Cache 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/cache/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Docker Level 03: Layer Cache -- Почему сборка 8 минут?</title>
      <link>https://devopsway.ru/posts/docker-03-layer-cache/</link>
      <pubDate>Mon, 18 May 2026 12:00:00 +0300</pubDate>
      <guid>https://devopsway.ru/posts/docker-03-layer-cache/</guid>
      <description>Level 03: почему CI build занимает 8 минут из-за одной строки. Механика слоёв Docker, кеширование и правильный порядок COPY/RUN.</description>
      <content:encoded><![CDATA[<h2 id="боль">БОЛЬ</h2>
<p>CI pipeline: 47 коммитов в день, каждый запускает <code>docker build</code>. Сборка занимает 8 минут, из них 7 &ndash; <code>npm ci</code>. Вы поменяли одну строку в <code>src/index.ts</code>, но Docker заново устанавливает все 1200 зависимостей. Каждый. Раз.</p>
<p>За день это 47 * 7 = <strong>329 минут впустую</strong>. За месяц &ndash; 110 часов ожидания. И всё потому, что <code>COPY . .</code> стоит перед <code>RUN npm ci</code>.</p>
<h2 id="как-устроено">КАК УСТРОЕНО</h2>
<p>Docker собирает образ слоями. Каждая инструкция <code>FROM</code>, <code>COPY</code>, <code>RUN</code> создаёт слой. Слои кешируются.</p>
<p>Правило кеша: если инструкция и всё, что было до неё, не менялись &ndash; Docker берёт слой из кеша. Но если слой инвалидирован &ndash; <strong>все последующие слои тоже пересобираются</strong>.</p>
<p>Плохой порядок:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">COPY</span> . .          <span class="c1"># &lt;-- любое изменение файла инвалидирует кеш</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> npm ci        <span class="c1"># &lt;-- пересобирается каждый раз</span><span class="err">
</span></span></span></code></pre></div><p>Хороший порядок:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">COPY</span> package.json package-lock.json ./   <span class="c1"># &lt;-- меняется редко</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> npm ci                                <span class="c1"># &lt;-- кешируется!</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> . .                                  <span class="c1"># &lt;-- только исходники</span><span class="err">
</span></span></span></code></pre></div><p>Принцип: <strong>от редко меняющегося к часто меняющемуся</strong>. Зависимости меняются раз в неделю, код &ndash; 47 раз в день. Разнесите их по разным слоям.</p>
<h2 id="практика">ПРАКТИКА</h2>
<p>Dockerfile для Node.js-приложения тормозит CI. Расставьте инструкции так, чтобы <code>npm ci</code> кешировался при изменении исходников.</p>
<div class="docker-sort" id="level03-quiz" data-answer="WyJGUk9NIG5vZGU6MjItYWxwaW5lIiwiV09SS0RJUiAvYXBwIiwiQ09QWSBwYWNrYWdlLmpzb24gcGFja2FnZS1sb2NrLmpzb24gLi8iLCJSVU4gbnBtIGNpIiwiQ09QWSAuIC4iLCJSVU4gbnBtIHJ1biBidWlsZCIsIkNNRCBbXCJub2RlXCIsIFwiZGlzdC9pbmRleC5qc1wiXSJd">
  <p class="docker-sort__title">Расставьте Dockerfile для максимального кеша</p>
  <ul class="docker-sort__list"><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">COPY . .</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">RUN npm ci</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">COPY package.json package-lock.json ./</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">FROM node:22-alpine</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">WORKDIR /app</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">RUN npm run build</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">CMD [&#34;node&#34;, &#34;dist/index.js&#34;]</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">Сначала <code>package.json</code> и <code>package-lock.json</code> &ndash; они меняются редко. <code>npm ci</code> устанавливает зависимости и кешируется, пока lock-файл не изменился. <code>COPY . .</code> копирует исходники &ndash; они меняются часто, но инвалидируют только слои ниже (build, CMD), а не npm ci.</div></div>

<h2 id="разбор">РАЗБОР</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># 1. Базовый образ -- меняется раз в квартал</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">node:22-alpine</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 2. Рабочая директория -- никогда не меняется</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 3. Lock-файлы -- меняются раз в неделю</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> package.json package-lock.json ./<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 4. Установка зависимостей -- кешируется пока lock-файл тот же</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> npm ci<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 5. Исходники -- меняются каждый коммит</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> . .<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 6. Сборка -- пересобирается при изменении кода (это ок)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> npm run build<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># 7. Запуск</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;node&#34;</span><span class="p">,</span> <span class="s2">&#34;dist/index.js&#34;</span><span class="p">]</span><span class="err">
</span></span></span></code></pre></div><p>Результат: при изменении кода Docker использует кеш для шагов 1&ndash;4 (FROM, WORKDIR, COPY lock-файлов, npm ci) и пересобирает только шаги 5&ndash;7. Вместо 8 минут &ndash; 30 секунд.</p>
<p>Проверить кеширование можно в выводе <code>docker build</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">=&gt; CACHED [2/6] WORKDIR /app
</span></span><span class="line"><span class="cl">=&gt; CACHED [3/6] COPY package.json package-lock.json ./
</span></span><span class="line"><span class="cl">=&gt; CACHED [4/6] RUN npm ci
</span></span><span class="line"><span class="cl">=&gt; [5/6] COPY . .
</span></span></code></pre></div><p><code>CACHED</code> &ndash; значит слой взят из кеша.</p>
<h2 id="вопрос-на-собесе">ВОПРОС НА СОБЕСЕ</h2>
<p><strong>Вопрос:</strong> Как работает кеширование слоёв Docker и когда кеш инвалидируется?</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 кеширует каждый слой (результат инструкции). При повторной сборке проверяет:</p>
<ol>
<li><strong>FROM</strong>: совпадает ли базовый образ (тег + digest)</li>
<li><strong>RUN</strong>: совпадает ли текст команды (посимвольно)</li>
<li><strong>COPY/ADD</strong>: совпадают ли checksum&rsquo;ы копируемых файлов</li>
</ol>
<p>Если на каком-то шаге кеш инвалидирован &ndash; <strong>все последующие шаги пересобираются</strong>, даже если сами не менялись. Это называется &ldquo;cache bust&rdquo;.</p>
<p>Типичные причины инвалидации:</p>
<ul>
<li><code>COPY . .</code> перед <code>RUN npm install</code> &ndash; любой файл в контексте инвалидирует кеш</li>
<li><code>RUN apt-get update &amp;&amp; apt-get install -y curl</code> без <code>--no-cache</code> &ndash; текст не менялся, кеш работает, но пакеты могут устареть</li>
<li><code>ARG VERSION=1.0</code> &ndash; изменение ARG инвалидирует все слои после его использования</li>
</ul>
<p>Лучшие практики: сортировать инструкции от редко меняющихся к часто меняющимся. Lock-файлы отдельно от исходников. Объединять RUN-команды с <code>&amp;&amp;</code> чтобы уменьшить количество слоёв.</p>

    </div>
</details>

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