<?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>Build on DevOps Way - Практические гайды</title>
    <link>https://devopsway.ru/tags/build/</link>
    <description>Recent content in Build 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/build/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Docker Level 02: Dockerfile -- Работает у меня, а у коллеги нет</title>
      <link>https://devopsway.ru/posts/docker-02-dockerfile/</link>
      <pubDate>Mon, 18 May 2026 11:00:00 +0300</pubDate>
      <guid>https://devopsway.ru/posts/docker-02-dockerfile/</guid>
      <description>Level 02: от &amp;#39;работает у меня&amp;#39; до воспроизводимой сборки через Dockerfile. Квиз по порядку инструкций и вопрос на собеседование.</description>
      <content:encoded><![CDATA[<h2 id="боль">БОЛЬ</h2>
<p>Джуниор присылает в чат: &ldquo;У меня всё работает&rdquo;. Вы клонируете репозиторий, запускаете <code>npm install</code> &ndash; ошибка. У него Node 18, у вас Node 22. У него Ubuntu, у вас macOS. У него <code>python3</code> указывает на 3.10, у вас &ndash; на 3.12.</p>
<p>&ldquo;Работает у меня&rdquo; &ndash; это не баг, это отсутствие воспроизводимой среды. Dockerfile решает проблему: он описывает среду декларативно. Кто бы ни собрал образ &ndash; результат будет одинаковый.</p>
<h2 id="как-устроено">КАК УСТРОЕНО</h2>
<p>Dockerfile &ndash; это текстовый файл с инструкциями для сборки образа. Каждая инструкция создаёт слой (layer) в образе.</p>
<p>Основные инструкции:</p>
<table>
  <thead>
      <tr>
          <th>Инструкция</th>
          <th>Назначение</th>
          <th>Пример</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>FROM</code></td>
          <td>Базовый образ</td>
          <td><code>FROM node:22-alpine</code></td>
      </tr>
      <tr>
          <td><code>WORKDIR</code></td>
          <td>Рабочая директория</td>
          <td><code>WORKDIR /app</code></td>
      </tr>
      <tr>
          <td><code>COPY</code></td>
          <td>Копировать файлы из контекста</td>
          <td><code>COPY . .</code></td>
      </tr>
      <tr>
          <td><code>RUN</code></td>
          <td>Выполнить команду при сборке</td>
          <td><code>RUN npm install</code></td>
      </tr>
      <tr>
          <td><code>ENV</code></td>
          <td>Переменная окружения</td>
          <td><code>ENV NODE_ENV=production</code></td>
      </tr>
      <tr>
          <td><code>EXPOSE</code></td>
          <td>Документация порта</td>
          <td><code>EXPOSE 3000</code></td>
      </tr>
      <tr>
          <td><code>CMD</code></td>
          <td>Команда запуска контейнера</td>
          <td><code>CMD [&quot;node&quot;, &quot;app.js&quot;]</code></td>
      </tr>
  </tbody>
</table>
<p>Порядок инструкций важен: Docker кеширует слои, и если слой не менялся &ndash; он берётся из кеша. Об этом подробнее в Level 03.</p>
<h2 id="практика">ПРАКТИКА</h2>
<p>Вам нужно собрать образ для Node.js-приложения: взять базовый образ, задать рабочую директорию, скопировать исходники, установить зависимости, указать порт и команду запуска.</p>
<div class="docker-sort" id="level02-quiz" data-answer="WyJGUk9NIG5vZGU6MjItYWxwaW5lIiwiV09SS0RJUiAvYXBwIiwiQ09QWSAuIC4iLCJSVU4gbnBtIGluc3RhbGwiLCJFWFBPU0UgMzAwMCIsIkNNRCBbXCJub2RlXCIsIFwiYXBwLmpzXCJdIl0=">
  <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">CMD [&#34;node&#34;, &#34;app.js&#34;]</span>
    </li><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 install</span>
    </li><li class="docker-sort__item">
      <span class="docker-sort__grip">&#10303;</span>
      <span class="docker-sort__text">EXPOSE 3000</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></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>FROM</code> всегда первая инструкция &ndash; определяет базовый образ. <code>WORKDIR</code> задаёт директорию для всех последующих команд. <code>COPY</code> копирует исходники, <code>RUN</code> устанавливает зависимости. <code>EXPOSE</code> документирует порт (не открывает его!). <code>CMD</code> &ndash; команда при запуске контейнера, всегда последняя.</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"># Базовый образ: Node.js 22 на Alpine Linux (маленький)</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"># Все команды выполняются в /app</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"># Копируем всё из текущей директории в /app контейнера</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"># Устанавливаем зависимости</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> npm install<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"># Документируем порт (для docker run -P)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">EXPOSE</span><span class="w"> </span><span class="s">3000</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"># Команда запуска -- выполнится при docker run</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;app.js&#34;</span><span class="p">]</span><span class="err">
</span></span></span></code></pre></div><p>Этот Dockerfile работает, но не оптимален. Каждое изменение кода инвалидирует кеш <code>npm install</code>. Как это исправить &ndash; в Level 03.</p>
<p>Сборка и запуск:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t myapp .
</span></span><span class="line"><span class="cl">docker run -d -p 3000:3000 myapp
</span></span></code></pre></div><h2 id="вопрос-на-собесе">ВОПРОС НА СОБЕСЕ</h2>
<p><strong>Вопрос:</strong> В чём разница между <code>CMD</code> и <code>ENTRYPOINT</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><code>CMD</code> задаёт команду по умолчанию, которую можно полностью переопределить при <code>docker run</code>:</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">CMD</span> <span class="p">[</span><span class="s2">&#34;node&#34;</span><span class="p">,</span> <span class="s2">&#34;app.js&#34;</span><span class="p">]</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># docker run myapp           -&gt; node app.js</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># docker run myapp bash      -&gt; bash (CMD заменён)</span><span class="err">
</span></span></span></code></pre></div><p><code>ENTRYPOINT</code> задаёт фиксированную команду, а аргументы из <code>docker run</code> дописываются к ней:</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">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;node&#34;</span><span class="p">]</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;app.js&#34;</span><span class="p">]</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># docker run myapp           -&gt; node app.js</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="c"># docker run myapp server.js -&gt; node server.js (CMD заменён, ENTRYPOINT нет)</span><span class="err">
</span></span></span></code></pre></div><p>На практике: <code>CMD</code> &ndash; для приложений, где пользователь может захотеть запустить shell. <code>ENTRYPOINT</code> &ndash; для утилит, где команда фиксирована (например, <code>curl</code>, <code>psql</code>). Комбинация <code>ENTRYPOINT</code> + <code>CMD</code> даёт фиксированную команду с дефолтными аргументами.</p>

    </div>
</details>

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