<?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>Nvidia on DevOps Way - Практические гайды</title>
    <link>https://devopsway.ru/tags/nvidia/</link>
    <description>Recent content in Nvidia 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.164.0</generator>
    <language>ru</language>
    <lastBuildDate>Fri, 24 Jul 2026 11:25:49 +0300</lastBuildDate>
    <atom:link href="https://devopsway.ru/tags/nvidia/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Тихий алерт и три пожара: как один ночной apt уронил GPU-сервер</title>
      <link>https://devopsway.ru/posts/incident-nvidia-driver-mismatch/</link>
      <pubDate>Fri, 24 Jul 2026 09:15:00 +0300</pubDate>
      <guid>https://devopsway.ru/posts/incident-nvidia-driver-mismatch/</guid>
      <description>Разбор инцидента: unattended-upgrades ночью обновил драйвер NVIDIA, userspace уехал вперёд работающего модуля ядра, и warning про cdi-refresh обернулся перегревом CPU до 96 C. Диагностика, лечение без ребута и лестница профилактики простое → правильное.</description>
      <content:encoded><![CDATA[<p>Утренний шторм в инфраструктуре не начинается с громких взрывов. Он стартует с тихого шёпота в алертах – с одного предупреждения, на которое глаз скользит мимо. Этот разбор про то, как рядовой <code>warning</code> про упавший systemd-юнит оказался единственным свидетелем зарождающейся катастрофы, которая через несколько минут уронила GPU-сервер в софтверный нокаут с перегревом процессора до 96 C.</p>
<p>Всё, что ниже – реальный инцидент. Стек обезличен, но версии драйвера, команды и логика – настоящие и воспроизводимые на любом хосте с NVIDIA.</p>
<h2 id="0615--шёпот">06:15 – шёпот</h2>
<p>Мониторинг фиксирует рядовой сбой:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">SystemdServiceFailed: nvidia-cdi-refresh.service в состоянии failed
</span></span></code></pre></div><p><code>nvidia-cdi-refresh</code> – служба, которая пересобирает спецификацию CDI (Container Device Interface) для доступа контейнеров к GPU. Падает – ну упала, перезапустится. На этот писк легко махнуть рукой.</p>
<p>Через 6 минут прилетает второй алерт (тот же юнит, теперь &quot;висит в failed больше 10 минут&quot;). Ещё через минуту – третий, и вот он уже не писк, а сирена:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">CPU CRITICAL: 96 C. Sensor выше 90 C – тепловой троттлинг неминуем (Tjmax=95 C).
</span></span></code></pre></div><p>Три разных алерта. Один корень. И корень – не тот, на кого показывал первый алерт.</p>
<h2 id="что-произошло-на-самом-деле">Что произошло на самом деле</h2>
<p>Ночью <code>unattended-upgrades</code> накатил новую минорную версию драйвера NVIDIA. Здесь и кроется мина хоста с активным GPU:</p>
<table>
	<thead>
			<tr>
					<th>Компонент</th>
					<th>До</th>
					<th>После apt</th>
					<th>Статус</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Userspace-библиотеки (NVML, <code>libnvidia-ml</code>)</td>
					<td>580.159</td>
					<td><strong>580.173</strong></td>
					<td>обновились сразу</td>
			</tr>
			<tr>
					<td>Пересобранный модуль на диске (<code>.ko</code>)</td>
					<td>580.159</td>
					<td><strong>580.173</strong></td>
					<td>лежит, готов</td>
			</tr>
			<tr>
					<td><strong>Работающий модуль в ядре</strong></td>
					<td>580.159</td>
					<td><strong>580.159</strong></td>
					<td><strong>не сменился</strong></td>
			</tr>
	</tbody>
</table>
<p>Модуль ядра нельзя заменить на живую, пока GPU держат активные процессы – его нельзя выгрузить (<code>rmmod</code>), у него ненулевой счётчик ссылок. Апгрейд обновил всё, кроме единственного, что реально исполняется. Получаем рассинхрон версий (version mismatch): userspace 580.173 разговаривает с ядром 580.159.</p>
<p>Диагностический якорь занимает две команды – сравнить работающий модуль с установленным:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat /proc/driver/nvidia/version <span class="p">|</span> head -1
</span></span><span class="line"><span class="cl"><span class="c1">#   NVRM version: NVIDIA UNIX x86_64 Kernel Module  580.159.03 ...</span>
</span></span><span class="line"><span class="cl">modinfo -F version nvidia
</span></span><span class="line"><span class="cl"><span class="c1">#   580.173.02</span>
</span></span></code></pre></div><p>Разные числа – это и есть мина. <code>nvidia-smi</code> в таком состоянии не запускается вовсе:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Failed to initialize NVML: Driver/library version mismatch
</span></span></code></pre></div><p>Ключевая мысль: апгрейд драйвера на работающем GPU-хосте – это отложенный отказ (deferred failure). Ломается не в момент <code>apt</code>, а когда до GPU дотянется первый новый процесс. Ночью система выглядела здоровой. Пожар начался утром, с первым запросом.</p>
<h2 id="почему-три-алерта-а-корень-один">Почему три алерта, а корень один</h2>
<p>Домино от одного рассинхрона:</p>
<table>
	<thead>
			<tr>
					<th>Время</th>
					<th>Что происходит</th>
					<th>Итог</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>06:09</td>
					<td><code>apt</code> обновляет драйвер 580.159 → 580.173 (userspace + <code>.ko</code>); модуль в ядре остаётся 580.159</td>
					<td>мина взведена</td>
			</tr>
			<tr>
					<td>06:10</td>
					<td><code>nvidia-cdi-refresh</code> не может в NVML</td>
					<td>падает → <strong>алерт №1</strong> (тот самый &quot;мелкий&quot;)</td>
			</tr>
			<tr>
					<td>06:20</td>
					<td><code>ollama</code> не может в CUDA, уходит на CPU: <code>offloaded 0/37 layers to GPU</code>, 16 потоков, 1579%</td>
					<td>96 C → <strong>алерт №3</strong></td>
			</tr>
			<tr>
					<td>06:21</td>
					<td>юнит в <code>failed</code> больше 10 минут</td>
					<td>→ <strong>алерт №2</strong></td>
			</tr>
	</tbody>
</table>
<p>Локальный LLM-раннер (в нашем случае ollama) при загрузке модели не смог инициализировать CUDA против рассинхронённого ядра и сделал то, что для инференса хуже всего, – тихо свалился на CPU. Модель в 37 слоёв, посчитанная на 16 потоках процессора, – вот кто раскалил CPU почти до Tjmax.</p>
<p>Симптомы кричали про systemd и температуру. Причина – рассинхрон драйвера, про который не алертил никто. Три алерта из одного корня – это не три проблемы, это отсутствие корреляции.</p>
<h2 id="ловушка-расследования-рантайм-врёт">Ловушка расследования: рантайм врёт</h2>
<p>Первый инстинкт – &quot;загасить всё, что держит GPU, и перезагрузить модуль&quot;. Спрашиваем Docker, кто использует девайс:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker inspect -f <span class="s1">&#39;{{.HostConfig.Runtime}}&#39;</span> &lt;container&gt;
</span></span></code></pre></div><p>И получаем ответ &quot;nvidia&quot; у <strong>всех</strong> контейнеров на хосте. По этой &quot;правде&quot; под снос идёт пол-сервера.</p>
<p>Реальность – в ядре, а не в манифесте. На этом хосте <code>default-runtime</code> в Docker был выставлен в <code>nvidia</code>, поэтому поле честно возвращало &quot;nvidia&quot; для каждого контейнера, независимо от того, трогает он GPU или нет. Кто держит девайс на самом деле, показывает файловая система процессов:</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"># кто реально открыл /dev/nvidia*</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> p in /proc/<span class="o">[</span>0-9<span class="o">]</span>*<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  grep -qE <span class="s1">&#39;/dev/nvidia&#39;</span> <span class="s2">&#34;</span><span class="nv">$p</span><span class="s2">/maps&#34;</span> 2&gt;/dev/null <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">&#34;</span><span class="k">$(</span>cat <span class="nv">$p</span>/comm<span class="k">)</span><span class="s2"> </span><span class="k">$(</span>basename <span class="nv">$p</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span></code></pre></div><p>Из сорока контейнеров устройство держали два. Декларация конфига – не то же самое, что факт на диске. Универсальный вывод SRE: смотри, кто реально открыл файл девайса (<code>/proc/*/fd</code>, <code>/proc/*/maps</code>, <code>fuser</code>), а не что написано в поле рантайма. Задекларированное поведение и реально работающее – разные вещи, и в инциденте доверять можно только второму.</p>
<h2 id="лечение-без-ребута">Лечение без ребута</h2>
<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"># 1. Снять держателей GPU (это же гасит и CPU-пожар)</span>
</span></span><span class="line"><span class="cl">systemctl stop ollama
</span></span><span class="line"><span class="cl">docker stop &lt;два-реальных-GPU-контейнера&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. Выгрузить старый стек 580.159 в порядке зависимостей</span>
</span></span><span class="line"><span class="cl">rmmod nvidia_uvm nvidia_drm nvidia_modeset nvidia
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Загрузить свежий 580.173</span>
</span></span><span class="line"><span class="cl">modprobe nvidia nvidia_uvm
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 4. Проверить, что работающий модуль совпал с установленным</span>
</span></span><span class="line"><span class="cl">cat /proc/driver/nvidia/version   <span class="c1"># → 580.173.02</span>
</span></span><span class="line"><span class="cl">nvidia-smi                        <span class="c1"># → OK</span>
</span></span></code></pre></div><p>Как только держатели сняты – температура сразу падает (в нашем случае 96 C → 72 C за секунды, ещё до перезагрузки модулей): CPU-пожар был не причиной, а следствием, и гаснет вместе с ним.</p>
<p>Отдельная засада на шаге восстановления: контейнер с GPU, созданный <strong>до</strong> апгрейда, отказался стартовать:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">open /usr/lib/x86_64-linux-gnu/libEGL_nvidia.so.580.159.03: no such file or directory
</span></span></code></pre></div><p>Его OCI-спека запекла пути монтирования библиотек с <strong>версией 580.159</strong> ещё в момент создания. Файла больше нет – он теперь 580.173. <code>docker start</code> проигрывает старые монтирования и падает. Лечится пересозданием (<code>docker compose up -d --force-recreate</code>), при котором рантайм заново собирает список монтирований под текущий драйвер. И вот эта деталь – прямой мост к тому, как сделать правильно.</p>
<h2 id="лестница-профилактики-простое--правильное">Лестница профилактики: простое → правильное</h2>
<p>Инцидент закрыт, но без профилактики он повторится на следующем бампе драйвера. Три ступени – от быстрого костыля до архитектурного решения.</p>
<h3 id="1-простое-убрать-драйвер-из-автообновления">1. Простое: убрать драйвер из автообновления</h3>
<p>Пока <code>unattended-upgrades</code> трогает драйвер, мина будет взводиться в 06:00 каждый раз. Забираем драйвер из-под автоапдейта – его версия должна меняться только руками, в окне обслуживания, с последующим релоадом модулей:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># /etc/apt/apt.conf.d/50unattended-upgrades → Package-Blacklist
</span></span><span class="line"><span class="cl">&#34;nvidia-&#34;;
</span></span><span class="line"><span class="cl">&#34;libnvidia-&#34;;
</span></span><span class="line"><span class="cl">&#34;nvidia-dkms-&#34;;
</span></span></code></pre></div><p>Проверять надо не глазами по файлу, а по тому, как apt реально распарсил список:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt-config dump <span class="p">|</span> grep -A4 Package-Blacklist
</span></span></code></pre></div><p>Это костыль (security-обновления драйвера теперь тоже ручные), но он останавливает молчаливое кровотечение.</p>
<h3 id="2-правильное-для-наблюдаемости-детектор-рассинхрона">2. Правильное для наблюдаемости: детектор рассинхрона</h3>
<p>Костыль не спасёт, если драйвер обновят руками и забудут перезагрузить модули. Нужен алерт на <strong>саму причину</strong>, а не на её симптомы. Причина выражается ровно тем сравнением из начала статьи. Заворачиваем его в скрипт, который пишет метрику для <code>node_exporter</code> (textfile collector):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># nvidia_driver_kmod_mismatch: работающий модуль != установленный .ko</span>
</span></span><span class="line"><span class="cl"><span class="c1"># nvidia_smi_up: NVML инициализируется</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -uo pipefail
</span></span><span class="line"><span class="cl"><span class="nv">OUT</span><span class="o">=</span>/var/lib/node_exporter/textfile_collector/nvidia_driver.prom
</span></span><span class="line"><span class="cl"><span class="nv">TMP</span><span class="o">=</span><span class="k">$(</span>mktemp -p <span class="s2">&#34;</span><span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="nv">$OUT</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> nvidia_driver.XXXXXX<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">running</span><span class="o">=</span><span class="k">$(</span>sed -n <span class="s1">&#39;s/.*Kernel Module *\([0-9][0-9.]*\).*/\1/p&#39;</span> /proc/driver/nvidia/version <span class="p">|</span> head -1<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">ondisk</span><span class="o">=</span><span class="k">$(</span>modinfo -F version nvidia 2&gt;/dev/null <span class="p">|</span> head -1<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">mismatch</span><span class="o">=</span>0<span class="p">;</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$running</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$ondisk</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$running</span><span class="s2">&#34;</span> !<span class="o">=</span> <span class="s2">&#34;</span><span class="nv">$ondisk</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">mismatch</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nv">smi_up</span><span class="o">=</span>0<span class="p">;</span> nvidia-smi -L &gt;/dev/null 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="o">&amp;&amp;</span> <span class="nv">smi_up</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;# TYPE nvidia_driver_kmod_mismatch gauge&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;nvidia_driver_kmod_mismatch{running=\&#34;</span><span class="si">${</span><span class="nv">running</span><span class="k">:-</span><span class="nv">none</span><span class="si">}</span><span class="s2">\&#34;,ondisk=\&#34;</span><span class="si">${</span><span class="nv">ondisk</span><span class="k">:-</span><span class="nv">none</span><span class="si">}</span><span class="s2">\&#34;} </span><span class="si">${</span><span class="nv">mismatch</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;# TYPE nvidia_smi_up gauge&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;nvidia_smi_up </span><span class="si">${</span><span class="nv">smi_up</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span> &gt; <span class="s2">&#34;</span><span class="nv">$TMP</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">mv -f <span class="s2">&#34;</span><span class="nv">$TMP</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$OUT</span><span class="s2">&#34;</span>   <span class="c1"># атомарная подмена: один fs, textfile-коллектор не прочитает половину</span>
</span></span></code></pre></div><p>Запускаем systemd-таймером раз в 2 минуты и вешаем правило (пример под vmalert/Prometheus):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">alert</span><span class="p">:</span><span class="w"> </span><span class="l">NvidiaDriverKmodMismatch</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">expr</span><span class="p">:</span><span class="w"> </span><span class="l">nvidia_driver_kmod_mismatch == 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">for</span><span class="p">:</span><span class="w"> </span><span class="l">3m</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span>{<span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="nt">critical, component</span><span class="p">:</span><span class="w"> </span><span class="l">gpu}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">summary</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Драйвер NVIDIA рассинхронён: ядро {{ $labels.running }} != диск {{ $labels.ondisk }}&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Обычно после apt-апгрейда до релоада модулей. Новые CUDA-процессы падают и могут уйти на CPU. Фикс: перезагрузить модули или ребут.&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">alert</span><span class="p">:</span><span class="w"> </span><span class="l">NvidiaSmiDown</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">expr</span><span class="p">:</span><span class="w"> </span><span class="l">nvidia_smi_up == 0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">for</span><span class="p">:</span><span class="w"> </span><span class="l">5m</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span>{<span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="nt">critical, component</span><span class="p">:</span><span class="w"> </span><span class="l">gpu}</span><span class="w">
</span></span></span></code></pre></div><p>В инциденте таймлайн был такой: бамп в 06:09, CPU закипел в 06:21. Детектор поймал бы <code>mismatch == 1</code> в районе 06:12 – за 9 минут до перегрева. Мы ловим причину раньше, чем она успевает притвориться температурой.</p>
<h3 id="3-правильное-для-архитектуры-контейнеры-на-cdi">3. Правильное для архитектуры: контейнеры на CDI</h3>
<p>Помните запечённый путь <code>libEGL...580.159.03</code> при пересоздании? Это симптом легаси-способа отдавать GPU в контейнер (<code>deploy.resources.reservations.devices</code> в compose или флаг рантайма). Он собирает список монтирований библиотек – с их версией – в момент <strong>создания</strong> контейнера. Обновили драйвер – список протух, контейнер сломан до пересоздания.</p>
<p>CDI (Container Device Interface) решает это by design: контейнер ссылается на абстрактный девайс <code>nvidia.com/gpu=0</code>, а конкретные монтирования резолвятся при <strong>старте</strong> из спеки, которую генерирует и поддерживает в актуальном состоянии&hellip; <code>nvidia-cdi-refresh</code> – тот самый юнит, чьё падение и было алертом №1. Круг замыкается: служба, с падения которой начался инцидент, существует ровно для того, чтобы этого инцидента не было.</p>
<p>Docker 25+ понимает CDI нативно. Миграция сервиса – это замена блока резервации на ссылку на CDI-девайс:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># было (легаси: запекает версию либ при create):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">reservations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">devices</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">nvidia</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">count</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">capabilities</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">gpu]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># стало (CDI: резолв при старте из /var/run/cdi/nvidia.yaml):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runtime</span><span class="p">:</span><span class="w"> </span><span class="l">runc           </span><span class="w"> </span><span class="c"># не даём default-nvidia-рантайму инжектить второй раз</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">devices</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;nvidia.com/gpu=0&#34;</span><span class="w">
</span></span></span></code></pre></div><p>После пересоздания в инспекте контейнера – ноль запечённых version-pinned монтирований, девайс подтягивается при старте. Тот же апгрейд драйвера его больше не сломает. Проверить механику до миграции можно на одноразовом контейнере:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run --rm --runtime<span class="o">=</span>runc --device<span class="o">=</span>nvidia.com/gpu<span class="o">=</span><span class="m">0</span> alpine ls -la /dev/nvidia0
</span></span></code></pre></div><h2 id="вывод-для-прод-мозга">Вывод для прод-мозга</h2>
<p>Три вещи, которые этот инцидент кладёт на полку памяти:</p>
<ul>
<li><strong>Алерт называет того, кто упал последним, а не того, кто толкнул.</strong> Самый тихий <code>warning</code> про &quot;какой-то cdi-refresh&quot; был единственной стрелкой, указывавшей прямо в корень, – а два громких алерта показывали на симптомы.</li>
<li><strong>In-place апгрейд драйвера на stateful-хосте – это отложенный отказ.</strong> Всё зелёное ночью и мёртвое утром. Автообновление ядрозависимых пакетов (драйверы, DKMS-модули) на серверах с постоянной нагрузкой должно быть осознанным решением, а не фоновым процессом.</li>
<li><strong>Задекларированное поведение – не то же, что работающее.</strong> Поле рантайма сказало &quot;GPU у всех&quot;, ядро сказало &quot;у двоих&quot;. В инциденте истина лежит в <code>/proc</code>, а не в манифесте.</li>
</ul>
<p>И последнее, инженерно-философское. Верхний уровень (CDI) существует именно для того, чтобы развязать хрупкую зависимость нижнего – список монтирований от момента создания контейнера. Когда падает служба, чья работа – держать эту развязку в актуальности, ты получаешь не одну поломку, а целый парад: список протухает, процессы валятся, температура растёт. Прочность цепочки – по слабейшему звену, а слабейшее звено любит притворяться чем-то громким и посторонним.</p>
<p><em>Профилактика в этом разборе – реальная лестница, которую мы прошли за один сеанс после инцидента: бан драйвера в автоапдейте, детектор рассинхрона с алертом за 9 минут до перегрева, и миграция GPU-контейнеров на CDI. Простое чинит сегодня, правильное – закрывает класс проблемы.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
