<?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>Images on inetshell</title><link>https://inet.sh/es/tags/images/</link><description>Recent content in Images on inetshell</description><generator>Hugo</generator><language>es</language><lastBuildDate>Fri, 25 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/es/tags/images/index.xml" rel="self" type="application/rss+xml"/><item><title>k3s: usar imágenes sin registry (docker save → k3s ctr images import) y limpiar las que sobran</title><link>https://inet.sh/es/posts/k3s/importar-imagenes-sin-registry/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/k3s/importar-imagenes-sin-registry/</guid><description>Cómo correr en k3s una imagen que construiste con Docker sin montar un registry: docker save, copiar el tar y k3s ctr images import en cada nodo, con imagePullPolicy Never. Las alternativas cuando hay varios nodos (directorio agent/images, registry embebido de k3s) y cómo listar y borrar imágenes con k3s crictl.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<p>En la máquina donde construyes:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker build -t test-app:v1.0.0 .
</span></span><span class="line"><span class="cl">docker save -o test-app-v1.0.0.tar test-app:v1.0.0
</span></span><span class="line"><span class="cl">rsync -v test-app-v1.0.0.tar node1:/tmp/
</span></span></code></pre></td></tr></table>
</div>
</div><p>En <strong>cada nodo</strong> de k3s donde pueda caer el pod:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo k3s ctr images import /tmp/test-app-v1.0.0.tar
</span></span><span class="line"><span class="cl">sudo k3s crictl images <span class="p">|</span> grep test-app        <span class="c1"># docker.io/library/test-app  v1.0.0</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Y en el Deployment, que kubelet no intente descargarla:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test-app</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">test-app:v1.0.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">Never</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><h2 id="por-qué">Por qué</h2>
<p>k3s corre sobre <strong>containerd</strong>, no sobre Docker. Una imagen que existe en el Docker de la misma máquina es invisible para k3s, y la primera vez que un pod la pide kubelet intenta bajarla de Docker Hub y falla con <code>ErrImagePull</code>. Las salidas son tres: un registry (lo correcto a partir de cierto tamaño), importar el tar en cada nodo, o dejar que k3s la importe sola desde un directorio.</p>
<p><code>k3s ctr</code> es el <code>ctr</code> de containerd ya apuntado al socket de k3s y al namespace <code>k8s.io</code>, que es donde miran kubelet y <code>crictl</code>. Si usas el binario <code>ctr</code> a pelo necesitas <code>--address /run/k3s/containerd/containerd.sock -n k8s.io</code>.</p>
<h2 id="alternativas-para-más-de-un-nodo">Alternativas para más de un nodo</h2>
<p><strong>Directorio de imágenes.</strong> Todo <code>.tar</code>, <code>.tar.gz</code> o <code>.tar.zst</code> que dejes en <code>/var/lib/rancher/k3s/agent/images/</code> se importa cada vez que arranca k3s (es el mecanismo documentado para instalaciones sin Internet):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo mkdir -p /var/lib/rancher/k3s/agent/images/
</span></span><span class="line"><span class="cl">sudo cp test-app-v1.0.0.tar /var/lib/rancher/k3s/agent/images/
</span></span><span class="line"><span class="cl">sudo systemctl restart k3s        <span class="c1"># o k3s-agent</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Ventaja sobre el <code>import</code> a mano: si el recolector de imágenes de kubelet la borra, vuelve en el siguiente arranque.</p>
<p><strong>Registry embebido (Spegel).</strong> Desde k3s 1.29 los servidores pueden arrancar con <code>--embedded-registry</code> (o <code>embedded-registry: true</code> en <code>/etc/rancher/k3s/config.yaml</code>); cada nodo publica en la red P2P (puerto 5001) las imágenes que ya tiene y los demás las bajan de él en vez de Internet. Requiere listar los registries a espejar en <code>/etc/rancher/k3s/registries.yaml</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">mirrors</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">docker.io</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;*&#34;</span><span class="p">:</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Con eso, importas la imagen en <strong>un</strong> nodo y el resto la consigue solo, siempre que los nodos se vean entre sí por 5001 y 6443.</p>
<p><strong>Registry propio.</strong> Un <code>registry:2</code> o Harbor en el clúster, y <code>docker push</code>. Es el camino cuando hay CI de por medio.</p>
<h2 id="listar-y-limpiar">Listar y limpiar</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo k3s crictl images                    <span class="c1"># lo que hay en el nodo</span>
</span></span><span class="line"><span class="cl">sudo k3s crictl rmi --prune               <span class="c1"># borra las que ningún contenedor usa</span>
</span></span><span class="line"><span class="cl">sudo k3s crictl rmi docker.io/library/test-app:v1.0.0
</span></span><span class="line"><span class="cl">sudo k3s crictl rmi --all                 <span class="c1"># todo; kubelet volverá a bajar lo que necesite</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>kubelet también recolecta solo: cuando el disco de <code>/var/lib/rancher/k3s/agent/containerd</code> supera el <strong>85 %</strong> borra imágenes sin uso hasta bajar del <strong>80 %</strong>. Los umbrales se cambian en el servicio:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># /etc/rancher/k3s/config.yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kubelet-arg</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">image-gc-high-threshold=85</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">image-gc-low-threshold=80</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Si ese disco se queda corto, mover el data-dir es <a href="/es/posts/k3s/migrar-datos/">otro post</a>.</p>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong>Cada nodo.</strong> Un pod programado en un nodo sin la imagen da <code>ErrImageNeverPull</code>. O importas en todos, o etiquetas el nodo y usas <code>nodeSelector</code>, o pasas al registry embebido.</li>
<li><strong>Arquitectura.</strong> Para nodos ARM (Raspberry Pi) la imagen tiene que ser <code>linux/arm64</code>: <code>docker buildx build --platform linux/arm64</code>.</li>
<li><strong><code>latest</code> tira de la red.</strong> Con tag <code>latest</code>, la política por defecto es <code>Always</code> y kubelet intenta bajarla aunque exista. Usa tags explícitos y <code>Never</code> o <code>IfNotPresent</code>.</li>
<li><strong>El nombre completo</strong> de una imagen importada es <code>docker.io/library/test-app:v1.0.0</code>; en el manifiesto <code>test-app:v1.0.0</code> resuelve a lo mismo.</li>
<li><strong>podman</strong>: <code>podman save --format docker-archive -o app.tar app:v1</code>, el formato que <code>ctr</code> importa sin sorpresas.</li>
<li><strong>Un <code>crictl rmi --prune</code> borra también lo que importaste</strong> si en ese momento ningún contenedor lo usa. Para lo que quieres que sobreviva, el directorio <code>agent/images/</code>.</li>
</ul>
<p>Referencias: <a href="https://docs.k3s.io/installation/airgap">Air-Gap Install</a> y <a href="https://docs.k3s.io/installation/registry-mirror">Embedded Registry Mirror</a> en la documentación de k3s.</p>
]]></content:encoded></item></channel></rss>