<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Docker on kenji.blog</title><link>http://kenji.blog/es/tags/docker/</link><description>Recent content in Docker on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 01:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/docker/index.xml" rel="self" type="application/rss+xml"/><item><title>Pasos para construir un entorno de desarrollo local reproducible usando Docker</title><link>http://kenji.blog/es/p/docker-reproducible-local-dev-environment/</link><pubDate>Sun, 13 Sep 2026 01:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/docker-reproducible-local-dev-environment/</guid><description>&lt;img src="http://kenji.blog/p/docker-reproducible-local-dev-environment/img/eyecatch.jpg" alt="Featured image of post Pasos para construir un entorno de desarrollo local reproducible usando Docker" />&lt;h2 id="1-introducción-escapando-de-en-mi-máquina-funciona">1. Introducción: Escapando de &amp;ldquo;en mi máquina funciona&amp;rdquo;
&lt;/h2>&lt;p>En el ámbito del desarrollo de software, el problema de &amp;ldquo;en mi máquina funciona&amp;rdquo; (It works on my machine), causado por las diferencias en los entornos entre desarrolladores, ha sido durante mucho tiempo un factor de pérdida de tiempo en muchos proyectos. Las diferencias en el sistema operativo, las versiones de los lenguajes instalados, las dependencias de bibliotecas y los conflictos entre herramientas instaladas globalmente exponen constantemente a los entornos locales a la &amp;ldquo;incertidumbre de estado&amp;rdquo;.&lt;/p>
&lt;p>Lo que resuelve estos problemas de raíz son las tecnologías de contenedores, como &lt;strong>Docker&lt;/strong>, y el paradigma de &lt;strong>Infrastructure as Code (IaC)&lt;/strong>. Al contenerizar el entorno de desarrollo local, se logra el aislamiento a nivel del sistema operativo, haciendo posible controlar las versiones del propio entorno junto con el código base.&lt;/p>
&lt;p>En este artículo, explicaremos exhaustivamente los pasos para construir un &lt;strong>&amp;ldquo;entorno de desarrollo local reproducible, que tendrá exactamente el mismo estado sin importar quién, cuándo o en qué máquina lo inicie&amp;rdquo;&lt;/strong>, aprovechando Docker, Docker Compose y VSCode DevContainers, así como los profundos mecanismos técnicos detrás de ello, incorporando también una perspectiva matemática.&lt;/p>
&lt;hr>
&lt;h2 id="2-la-afinidad-entre-infrastructure-as-code-iac-y-la-tecnología-de-contenedores">2. La afinidad entre Infrastructure as Code (IaC) y la tecnología de contenedores
&lt;/h2>&lt;h3 id="principios-de-iac-y-su-aplicación-a-entornos-locales">Principios de IaC y su aplicación a entornos locales
&lt;/h3>&lt;p>Infrastructure as Code (IaC) es un enfoque que gestiona la configuración y el aprovisionamiento de la infraestructura a través de archivos de definición legibles por máquina, en lugar de procesos manuales. Los principios fundamentales de IaC incluyen los siguientes elementos:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Enfoque declarativo (Declarative Approach)&lt;/strong>: Define &amp;ldquo;cuál debería ser el estado final&amp;rdquo; en lugar de &amp;ldquo;cómo cambiar el estado&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Idempotencia (Idempotency)&lt;/strong>: No importa cuántas veces se ejecute el script, siempre se garantiza el mismo resultado (estado).&lt;/li>
&lt;li>&lt;strong>Control de versiones (Version Control)&lt;/strong>: El estado de la infraestructura se guarda como código en un VCS (como Git), lo que permite rastrear el historial de cambios y realizar revisiones por pares.&lt;/li>
&lt;/ol>
&lt;p>Practicar IaC en un entorno de desarrollo local significa codificar el &amp;ldquo;estado ideal&amp;rdquo; del entorno de desarrollo utilizando &lt;code>Dockerfile&lt;/code>, &lt;code>docker-compose.yml&lt;/code> y &lt;code>devcontainer.json&lt;/code>. Esto permite que los nuevos miembros del equipo logren una experiencia de incorporación donde pueden comenzar a desarrollar inmediatamente con solo clonar el repositorio y ejecutar un comando.&lt;/p>
&lt;h3 id="funciones-del-kernel-que-sustentan-la-tecnología-de-contenedores">Funciones del kernel que sustentan la tecnología de contenedores
&lt;/h3>&lt;p>La tecnología de contenedores, a diferencia de la virtualización basada en hipervisor como las máquinas virtuales (VM), es una tecnología de virtualización ligera que aísla procesos compartiendo el kernel del sistema operativo anfitrión. Para lograr esto, se utilizan principalmente las siguientes funciones del kernel de Linux:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Namespaces&lt;/strong>: Proporcionan una vista independiente de los recursos del sistema (PID, red, puntos de montaje, usuarios, etc.) para cada proceso.&lt;/li>
&lt;li>&lt;strong>Cgroups (Control Groups)&lt;/strong>: Limitan y asignan los recursos físicos (CPU, memoria, E/S de disco, etc.) que pueden usar los procesos.&lt;/li>
&lt;li>&lt;strong>UnionFS (Union File System)&lt;/strong>: Una tecnología que superpone de forma transparente múltiples árboles de directorios (capas) para presentarlos como un único sistema de archivos. Las capas de imagen de Docker dependen de esta tecnología.&lt;/li>
&lt;/ul>
&lt;p>Consideremos un modelo matemático para la limitación de recursos. Supongamos que la capacidad total de memoria de la máquina anfitriona es $M_{\text{total}}$ y el límite de memoria para los $n$ contenedores que se ejecutan en el host es $m_i$. La condición necesaria para que el sistema funcione de manera estable, considerando la memoria base $M_{\text{os}}$ consumida por el sistema operativo anfitrión y otros procesos, se puede expresar mediante la siguiente desigualdad:&lt;/p>
$$ \sum_{i=1}^{n} m_i \le M_{\text{total}} - M_{\text{os}} $$&lt;p>Al definir estrictamente $m_i$ para cada contenedor utilizando Cgroups, se puede evitar que otros contenedores o todo el sistema anfitrión se caigan debido al OOM (Out Of Memory) Killer en caso de que un contenedor en particular sufra una fuga de memoria.&lt;/p>
&lt;hr>
&lt;h2 id="3-diseño-eficiente-del-dockerfile-dominando-las-construcciones-en-múltiples-etapas">3. Diseño eficiente del Dockerfile: Dominando las construcciones en múltiples etapas
&lt;/h2>&lt;p>El primer paso hacia un entorno reproducible es diseñar un &lt;code>Dockerfile&lt;/code> que defina el entorno de ejecución de la aplicación. Aquí, usando Python (FastAPI) como ejemplo, explicaremos las mejores prácticas para un Dockerfile seguro y ligero aprovechando las &lt;strong>construcciones en múltiples etapas (multi-stage builds)&lt;/strong>.&lt;/p>
&lt;p>La construcción en múltiples etapas es una técnica que utiliza múltiples instrucciones &lt;code>FROM&lt;/code> en un solo &lt;code>Dockerfile&lt;/code> para separar el entorno de construcción (un entorno pesado que contiene compiladores y herramientas de desarrollo) del entorno de ejecución (un entorno ligero con solo los artefactos necesarios).&lt;/p>
&lt;h3 id="dockerfile-práctico-para-python-fastapi">Dockerfile práctico para Python FastAPI
&lt;/h3>&lt;p>El siguiente código es un ejemplo de un &lt;code>Dockerfile&lt;/code> avanzado que combina la gestión de dependencias con Poetry y la construcción en múltiples etapas.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Stage 1: Builder (Entorno de construcción)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> python:3.11-slim AS builder&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Configuración de variables de entorno necesarias&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PYTHONUNBUFFERED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">PYTHONDONTWRITEBYTECODE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_VERSION&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>.6.1 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_HOME&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/opt/poetry&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_VIRTUALENVS_IN_PROJECT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">true&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_NO_INTERACTION&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Instalación de paquetes de dependencias&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get install -y --no-install-recommends &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> curl build-essential &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> curl -sSL https://install.python-poetry.org &lt;span class="p">|&lt;/span> python3 - &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> apt-get clean &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> rm -rf /var/lib/apt/lists/*&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$POETRY_HOME&lt;/span>&lt;span class="s2">/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Copia e instalación de archivos de dependencias&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> pyproject.toml poetry.lock ./&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> poetry install --no-root --only main&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Stage 2: Runtime (Entorno de ejecución)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> python:3.11-slim AS runtime&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PYTHONUNBUFFERED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">PYTHONDONTWRITEBYTECODE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/app/.venv/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Creación de un usuario sin privilegios mínimos&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> groupadd -r appuser &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> useradd -r -g appuser appuser&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Copiar solo el entorno virtual (dependencias) desde el builder&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --from&lt;span class="o">=&lt;/span>builder --chown&lt;span class="o">=&lt;/span>appuser:appuser /app/.venv /app/.venv&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Copia del código de la aplicación&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --chown&lt;span class="o">=&lt;/span>appuser:appuser ./src /app/src&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Cambio al usuario sin privilegios&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">USER&lt;/span>&lt;span class="s"> appuser&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Comando por defecto al iniciar el contenedor&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENTRYPOINT&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;uvicorn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;src.main:app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--host&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;0.0.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--port&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;8000&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="evaluación-matemática-del-tamaño-de-la-imagen-mediante-la-construcción-en-múltiples-etapas">Evaluación matemática del tamaño de la imagen mediante la construcción en múltiples etapas
&lt;/h3>&lt;p>Supongamos que el tamaño de la imagen construida en una sola etapa es $S_{\text{single}}$ y el tamaño de la imagen al aplicar la construcción en múltiples etapas es $S_{\text{multi}}$. La tasa de reducción de tamaño $R$ se calcula de la siguiente manera:&lt;/p>
$$ R = \left( 1 - \frac{S_{\text{multi}}}{S_{\text{single}}} \right) \times 100 \ (\%) $$&lt;p>Por ejemplo, supongamos que $S_{\text{single}}$ incluye la imagen base del SO (aprox. 110MB), paquetes de desarrollo (como gcc, aprox. 150MB), el propio Poetry (aprox. 40MB), las bibliotecas de dependencias del proyecto (aprox. 80MB) y el código fuente (aprox. 5MB), totalizando 385MB.
Por otro lado, en $S_{\text{multi}}$, solo se copian a la imagen base (110MB) las bibliotecas de dependencias (80MB) y el código fuente (5MB), sumando un total de 195MB.&lt;/p>
$$ R = \left( 1 - \frac{195}{385} \right) \times 100 \approx 49.35\% $$&lt;p>De esta manera, al introducir construcciones en múltiples etapas, el tamaño de la imagen se puede reducir a la mitad aproximadamente. La reducción en el tamaño de la imagen se traduce directamente en tiempos más cortos de extracción (Pull) desde el registro, ahorro de espacio en disco y una mejora en la seguridad mediante la reducción de la superficie de ataque (Attack Surface).&lt;/p>
&lt;hr>
&lt;h2 id="4-orquestación-de-múltiples-contenedores-con-docker-compose">4. Orquestación de múltiples contenedores con Docker Compose
&lt;/h2>&lt;p>En el desarrollo de aplicaciones web modernas, es común utilizar una arquitectura de microservicios donde múltiples componentes colaboran, como servidores web, bases de datos y servidores de caché. Usamos &lt;code>docker-compose.yml&lt;/code> para gestionar esto de forma centralizada en un entorno local.&lt;/p>
&lt;p>Esta vez, construiremos localmente un sistema de tres capas compuesto por &amp;ldquo;Web (FastAPI)&amp;rdquo;, &amp;ldquo;Base de datos (PostgreSQL)&amp;rdquo; y &amp;ldquo;Caché (Redis)&amp;rdquo;.&lt;/p>
&lt;h3 id="diagrama-de-arquitectura-mermaid">Diagrama de arquitectura (Mermaid)
&lt;/h3>&lt;p>El siguiente diagrama de bloques ilustra la relación entre cada contenedor, red y volumen en la máquina local.&lt;/p>
&lt;pre class="mermaid">
graph TD
User[&amp;#34;Máquina Anfitriona (Navegador/curl)&amp;#34;] --&amp;gt;|Localhost:8000| Web[&amp;#34;Contenedor Web FastAPI&amp;#34;]
subgraph &amp;#34;Red Bridge de Docker (app-network)&amp;#34;
Web --&amp;gt;|Puerto 5432| DB[&amp;#34;Contenedor PostgreSQL&amp;#34;]
Web --&amp;gt;|Puerto 6379| Redis[&amp;#34;Contenedor Redis&amp;#34;]
end
DB --&amp;gt; Volume1[&amp;#34;Volumen Nombrado (postgres_data)&amp;#34;]
Redis --&amp;gt; Volume2[&amp;#34;Volumen Nombrado (redis_data)&amp;#34;]
HostDir[&amp;#34;Código Fuente del Anfitrión (./src)&amp;#34;] -.-&amp;gt;|Montaje de Enlace| Web
&lt;/pre>
&lt;h3 id="implementación-y-explicación-detallada-de-docker-composeyml">Implementación y explicación detallada de docker-compose.yml
&lt;/h3>&lt;p>A continuación se muestra un ejemplo de un &lt;code>docker-compose.yml&lt;/code> robusto, capaz de soportar la construcción de un entorno práctico.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">web&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">context&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">runtime&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dev_web&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8000:8000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./src:/app/src:ro &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Montaje de solo lectura del código del host (para recarga en caliente)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">REDIS_URL=redis://redis:6379/0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env_file&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">.env&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service_healthy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service_started&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">app-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;uvicorn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;src.main:app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--host&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.0.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--port&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;8000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--reload&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">postgres:15-alpine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dev_db&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;5432:5432&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">POSTGRES_USER&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">postgres&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">POSTGRES_PASSWORD&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${POSTGRES_PASSWORD}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">POSTGRES_DB&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${POSTGRES_DB}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">postgres_data:/var/lib/postgresql/data&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">app-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">healthcheck&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;CMD-SHELL&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;pg_isready -U postgres -d ${POSTGRES_DB}&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">interval&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">5s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">timeout&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">5s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">retries&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">redis:7-alpine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dev_redis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;6379:6379&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">redis_data:/data&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">app-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;redis-server&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--appendonly&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;yes&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">postgres_data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis_data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app-network&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">driver&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bridge&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="volúmenes-volumes-y-persistencia-de-datos">Volúmenes (Volumes) y persistencia de datos
&lt;/h3>&lt;p>Los contenedores, por principio, son entidades &amp;ldquo;sin estado (stateless)&amp;rdquo; y &amp;ldquo;efímeras (ephemeral)&amp;rdquo;. Si se destruye un contenedor, sus datos internos también desaparecen. Para retener los datos de bases de datos y cachés, es necesario montar un área del sistema de archivos de la máquina anfitriona dentro del contenedor.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Montaje de enlace (Bind Mount)&lt;/strong>: En el servicio &lt;code>web&lt;/code> mencionado anteriormente, &lt;code>./src:/app/src:ro&lt;/code> corresponde a esto. Asigna un directorio específico del host directamente dentro del contenedor. Se utiliza para reflejar instantáneamente los cambios en el código local dentro del contenedor (recarga en caliente - hot reload). Desde una perspectiva de seguridad, la mejor práctica es agregar la opción &lt;code>:ro&lt;/code> (Read-Only) para evitar que se pueda modificar el código fuente del host desde el lado del contenedor.&lt;/li>
&lt;li>&lt;strong>Volumen nombrado (Named Volume)&lt;/strong>: Corresponde a &lt;code>postgres_data&lt;/code> y &lt;code>redis_data&lt;/code>. Es un área gestionada internamente por Docker (como &lt;code>/var/lib/docker/volumes/&lt;/code>), la cual ofrece un rendimiento de E/S superior a los montajes de enlace y absorbe las diferencias de sistemas de archivos entre sistemas operativos. Siempre debe utilizarse este método para la persistencia de bases de datos.&lt;/li>
&lt;/ul>
&lt;h3 id="redes-networking-y-descubrimiento-de-servicios">Redes (Networking) y descubrimiento de servicios
&lt;/h3>&lt;p>Docker Compose crea de forma predeterminada una red bridge única por proyecto. Esto es la &lt;code>app-network&lt;/code> mencionada anteriormente.
Los contenedores que pertenecen a la misma red pueden resolverse por nombre (resolución DNS) utilizando su &amp;ldquo;nombre de servicio&amp;rdquo; (por ejemplo: &lt;code>db&lt;/code>, &lt;code>redis&lt;/code>) como nombre de host, en lugar de direcciones IP.
Por ejemplo, desde el contenedor web se puede acceder a la base de datos con la URL &lt;code>postgresql://postgres:password@db:5432/mydb&lt;/code>. Esto permite cambiar los destinos de conexión de manera transparente a través de variables de entorno, ya sea en el entorno local o en producción.&lt;/p>
&lt;h3 id="health-checks-comprobación-de-estado-y-control-del-orden-de-inicio">Health checks (Comprobación de estado) y control del orden de inicio
&lt;/h3>&lt;p>La directiva &lt;code>depends_on&lt;/code> controla el orden de inicio de los contenedores, pero si solo se especifica &lt;code>depends_on&lt;/code>, el contenedor web se iniciará tan pronto como el contenedor de la base de datos &amp;ldquo;haya arrancado&amp;rdquo;. En realidad, como el proceso de inicialización de la base de datos (iniciar el proceso de PostgreSQL, preparar tablas) toma unos segundos, la conexión a la base de datos desde el contenedor web puede generar un error.
Para evitar esto, se puede definir un &lt;code>healthcheck&lt;/code> y especificar &lt;code>condition: service_healthy&lt;/code>, lo cual permite confirmar que &amp;ldquo;la base de datos está lista para aceptar peticiones de conexión&amp;rdquo; antes de iniciar el contenedor web.&lt;/p>
&lt;hr>
&lt;h2 id="5-gestión-de-variables-de-entorno-y-seguridad-env">5. Gestión de variables de entorno y seguridad (.env)
&lt;/h2>&lt;p>Codificar (hardcodear) información confidencial como contraseñas de bases de datos o claves API en &lt;code>docker-compose.yml&lt;/code> es un antipatrón que debe evitarse estrictamente. En su lugar, utilice un archivo de variables de entorno &lt;code>.env&lt;/code> para inyectar estos valores.&lt;/p>
&lt;p>Cree un archivo &lt;code>.env&lt;/code> en la raíz del proyecto.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Archivo .env (Asegúrese de agregarlo a .gitignore para mantenerlo fuera del control de versiones de Git)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">POSTGRES_PASSWORD&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">supersecretpassword&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">POSTGRES_DB&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">devdb&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">API_SECRET_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">dev_secret_key_12345&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>De forma predeterminada, Docker Compose lee el archivo &lt;code>.env&lt;/code> en el directorio de ejecución y expande los marcadores de posición &lt;code>${VAR_NAME}&lt;/code> en los archivos YAML. Este enfoque permite gestionar de forma segura diferentes configuraciones para cada entorno (local, staging, producción) sin modificar el código de infraestructura.&lt;/p>
&lt;hr>
&lt;h2 id="6-la-experiencia-de-desarrollo-definitiva-con-vscode-devcontainers">6. La experiencia de desarrollo definitiva con VSCode DevContainers
&lt;/h2>&lt;p>Hasta este punto, se ha construido un entorno backend robusto usando Docker. Sin embargo, podemos ir un paso más allá. Al utilizar la función &lt;strong>VSCode DevContainers (Remote - Containers)&lt;/strong>, es posible ejecutar el backend del propio editor (VSCode) dentro de un contenedor.&lt;/p>
&lt;p>Como resultado, ni siquiera es necesario instalar Python o Node.js en su máquina local, y todo —desde linters (flake8/eslint) y formateadores (black/prettier) hasta extensiones del IDE— se puede definir dentro del código base y compartir con todo el equipo.&lt;/p>
&lt;h3 id="configuración-de-devcontainerjson">Configuración de devcontainer.json
&lt;/h3>&lt;p>Cree un directorio &lt;code>.devcontainer&lt;/code> en la raíz del proyecto y coloque el archivo de configuración dentro de él.&lt;/p>
&lt;p>&lt;code>.devcontainer/devcontainer.json&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Python FastAPI Dev Environment&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;dockerComposeFile&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;../docker-compose.yml&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;service&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;workspaceFolder&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;customizations&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;vscode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;settings&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;python.defaultInterpreterPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/app/.venv/bin/python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;python.formatting.provider&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;black&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editor.formatOnSave&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;extensions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ms-python.python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ms-python.vscode-pylance&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ms-python.black-formatter&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tamasfe.even-better-toml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;forwardPorts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">8000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5432&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6379&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;remoteUser&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;appuser&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;postCreateCommand&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;poetry install&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Al incluir este archivo en el repositorio, en el momento en que abra el proyecto en VSCode, aparecerá el aviso &amp;ldquo;Reopen in Container&amp;rdquo;. Con un solo clic, se iniciarán todos los contenedores necesarios, se instalarán las extensiones y estará listo para comenzar a programar de inmediato. Es verdaderamente una experiencia mágica.&lt;/p>
&lt;hr>
&lt;h2 id="7-secuencia-del-procesamiento-de-peticiones-y-modelado-de-rendimiento">7. Secuencia del procesamiento de peticiones y modelado de rendimiento
&lt;/h2>&lt;p>Revisaremos el ciclo de vida del procesamiento de peticiones (requests) de la aplicación web en el entorno de desarrollo local construido usando un diagrama de secuencia, y examinaremos un modelo matemático de su rendimiento.&lt;/p>
&lt;h3 id="diagrama-de-secuencia-flujo-de-peticiones">Diagrama de secuencia (Flujo de peticiones)
&lt;/h3>&lt;pre class="mermaid">
sequenceDiagram
participant Client as &amp;#34;Navegador / VSCode&amp;#34;
participant Web as &amp;#34;FastAPI (Web)&amp;#34;
participant Redis as &amp;#34;Caché Redis&amp;#34;
participant DB as &amp;#34;PostgreSQL&amp;#34;
Client-&amp;gt;&amp;gt;Web: &amp;#34;GET /api/users/123&amp;#34;
activate Web
Web-&amp;gt;&amp;gt;Redis: &amp;#34;Comprobar Caché para user:123&amp;#34;
activate Redis
alt &amp;#34;Cache Hit (Datos encontrados)&amp;#34;
Redis--&amp;gt;&amp;gt;Web: &amp;#34;Retornar Datos del Usuario en Caché&amp;#34;
Web--&amp;gt;&amp;gt;Client: &amp;#34;200 OK (Respuesta Rápida)&amp;#34;
else &amp;#34;Cache Miss (Datos no encontrados)&amp;#34;
Redis--&amp;gt;&amp;gt;Web: &amp;#34;Nulo (No Encontrado)&amp;#34;
deactivate Redis
Web-&amp;gt;&amp;gt;DB: &amp;#34;SELECT * FROM users WHERE id = 123&amp;#34;
activate DB
DB--&amp;gt;&amp;gt;Web: &amp;#34;Retornar Fila de Base de Datos&amp;#34;
deactivate DB
Web-&amp;gt;&amp;gt;Redis: &amp;#34;SET Datos de user:123 (TTL: 60s)&amp;#34;
activate Redis
Redis--&amp;gt;&amp;gt;Web: &amp;#34;OK&amp;#34;
deactivate Redis
Web--&amp;gt;&amp;gt;Client: &amp;#34;200 OK (Respuesta Estándar)&amp;#34;
end
deactivate Web
&lt;/pre>
&lt;h3 id="modelo-matemático-de-la-latencia-retraso-en-procesamiento">Modelo matemático de la latencia (retraso en procesamiento)
&lt;/h3>&lt;p>Modelaremos matemáticamente el tiempo promedio de procesamiento de peticiones $T_{\text{total}}$ en el sistema anterior.
Definimos la latencia de cada proceso de la siguiente manera:&lt;/p>
&lt;ul>
&lt;li>$T_{\text{net}}$: Latencia de red entre el cliente y el contenedor web&lt;/li>
&lt;li>$T_{\text{app}}$: Tiempo de procesamiento puro del lado de la aplicación (serialización, etc.)&lt;/li>
&lt;li>$T_{\text{cache}}$: Tiempo requerido para leer y escribir en Redis&lt;/li>
&lt;li>$T_{\text{db}}$: Tiempo requerido para ejecutar consultas en PostgreSQL&lt;/li>
&lt;li>$p_{\text{miss}}$: Tasa de fallos en caché (cache miss rate) ($0 \le p_{\text{miss}} \le 1$)&lt;/li>
&lt;/ul>
&lt;p>Entonces, el tiempo promedio de respuesta se expresa mediante la siguiente fórmula de valor esperado:&lt;/p>
$$ T_{\text{total}} = T_{\text{net}} + T_{\text{app}} + T_{\text{cache}} + p_{\text{miss}} \times (T_{\text{db}} + T_{\text{cache\_write}}) $$&lt;p>En un entorno de desarrollo local (dentro de Docker), $T_{\text{net}}$ está cerca de 0. Sin embargo, lo que se debe notar es el &lt;strong>rendimiento de E/S al usar montajes de enlace (bind mounts)&lt;/strong>. Especialmente al usar Docker Desktop en Windows/macOS, $T_{\text{app}}$ (tiempo de lectura de código, etc.) tiende a inflarse debido a la sobrecarga de compartir archivos entre el sistema operativo anfitrión y la máquina virtual (contenedor). Para resolver este cuello de botella, se recomienda encarecidamente una arquitectura que coloque todo el código fuente en un volumen nombrado utilizando DevContainers, o ejecutar el motor de Docker nativamente en un entorno WSL2 (Windows Subsystem for Linux 2).&lt;/p>
&lt;hr>
&lt;h2 id="8-optimización-del-rendimiento-de-la-construcción-en-docker-estrategia-de-caché-por-capas">8. Optimización del rendimiento de la construcción en Docker: Estrategia de caché por capas
&lt;/h2>&lt;p>Al escribir un Dockerfile, si se comprende o no el mecanismo de &amp;ldquo;caché de capas (layer caching)&amp;rdquo;, el tiempo de construcción cambia drásticamente.
Docker crea una diferencia en el sistema de archivos (capa) para cada instrucción del Dockerfile (como &lt;code>FROM&lt;/code>, &lt;code>RUN&lt;/code>, &lt;code>COPY&lt;/code>) y la guarda como caché. Al volver a construir, se reutiliza la caché de las capas que no han cambiado.&lt;/p>
&lt;p>El principio importante es &lt;strong>&amp;ldquo;escribir en orden, comenzando por las cosas que cambian con menor frecuencia&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Consideremos el modelado del impacto que los cambios en el código fuente tienen en el tiempo de construcción. Supongamos que el tiempo total de construcción es $T_{\text{build}}$, el tiempo de ejecución de cada paso es $T_{\text{layer}_i}$ y la existencia de un acierto de caché es un valor booleano $c_i \in \{0, 1\}$ (1 para aciertos de caché).&lt;/p>
$$ T_{\text{build}} = T_{\text{init}} + \sum_{i=1}^{n} (1 - c_i) \times T_{\text{layer}_i} $$&lt;p>Una vez que ocurre un fallo de caché en la capa $k$ ($c_k = 0$), la caché se invalida ($c_j = 0$) para todas las capas subsecuentes $j > k$.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Mal ejemplo (Copiando el código fuente primero)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> ./src /app/src&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> pyproject.toml poetry.lock ./&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> poetry install&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En el caso anterior, con solo modificar una línea de código, se producirá un fallo de caché en el primer &lt;code>COPY&lt;/code>, y el comando &lt;code>RUN poetry install&lt;/code>, que consume mucho tiempo, se ejecutará cada vez.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Buen ejemplo (Resolviendo las dependencias primero)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> pyproject.toml poetry.lock ./&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> poetry install&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> ./src /app/src&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Si lo escribe de esta manera, incluso si cambia el código fuente, la caché de la capa de &lt;code>poetry install&lt;/code> ($c_i = 1$) se mantendrá, lo que reduce drásticamente el tiempo de construcción de minutos a segundos.&lt;/p>
&lt;hr>
&lt;h2 id="9-solución-de-problemas-y-consejos">9. Solución de problemas y consejos
&lt;/h2>&lt;p>Aquí listamos problemas comunes y soluciones encontrados al operar entornos locales.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Error de conflicto de puertos&lt;/strong>
Si ocurre un error como &lt;code>Bind for 0.0.0.0:8000 failed: port is already allocated&lt;/code>, otro proceso en su máquina local ya está usando ese puerto. Puede evitarlo cambiando el número de puerto en el lado del host, como &lt;code>ports: - &amp;quot;8080:8000&amp;quot;&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Agotamiento del espacio en disco&lt;/strong>
Si usa Docker por un largo período, las imágenes y volúmenes no utilizados (Dangling Images / Volumes) pueden acumularse y consumir decenas de GB de espacio en disco. Se recomienda limpiar el sistema regularmente con el siguiente comando.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker system prune -a --volumes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Problemas de permisos de archivos&lt;/strong>
Al usar montajes de enlace (bind mounts) en un entorno Linux, los archivos creados dentro del contenedor pueden ser propiedad de &lt;code>root&lt;/code>, impidiendo que se editen en el lado del anfitrión. Puede resolver este problema creando un usuario sin privilegios en el Dockerfile y haciendo coincidir su UID/GID con el suyo en el sistema operativo anfitrión (por ejemplo: 1000:1000).&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="10-conclusión-la-mejora-en-la-velocidad-de-desarrollo-que-trae-la-reproducibilidad">10. Conclusión: La mejora en la velocidad de desarrollo que trae la reproducibilidad
&lt;/h2>&lt;p>Al combinar Docker, Docker Compose y VSCode DevContainers, se logra un entorno de desarrollo local robusto donde &amp;ldquo;el estado será exactamente el mismo sin importar quién inicie el entorno&amp;rdquo;.&lt;/p>
&lt;p>Llevar el paradigma de IaC a entornos locales no solo reduce el tiempo de configuración inicial. También elimina la ansiedad en torno a los cambios de configuración de infraestructura, facilita la experimentación con nuevas pilas tecnológicas y permite una transición fluida a las canalizaciones CI/CD, mejorando drásticamente la velocidad y la calidad de todo el ciclo de desarrollo.&lt;/p>
&lt;p>Aprovechando las mejores prácticas explicadas en este artículo —como la optimización del tamaño de imagen con construcciones en múltiples etapas, el control de dependencias usando health checks y la redacción de un Dockerfile consciente de la caché de capas— lo animamos encarecidamente a introducir la mejor experiencia de desarrollo (DX: Developer Experience) en sus propios proyectos.&lt;/p></description></item><item><title>Guía de configuración definitiva del entorno de desarrollo para WSL2 (Windows Subsystem for Linux)</title><link>http://kenji.blog/es/p/wsl2-ultimate-development-setup-guide/</link><pubDate>Sat, 12 Sep 2026 23:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/wsl2-ultimate-development-setup-guide/</guid><description>&lt;img src="http://kenji.blog/p/wsl2-ultimate-development-setup-guide/img/eyecatch.jpg" alt="Featured image of post Guía de configuración definitiva del entorno de desarrollo para WSL2 (Windows Subsystem for Linux)" />&lt;p>&amp;ldquo;WSL2 (Windows Subsystem for Linux 2)&amp;rdquo;, que proporciona un entorno de desarrollo nativo de Linux en Windows, se ha convertido en una herramienta indispensable en el desarrollo de software moderno. Sin embargo, existe una gran diferencia en el rendimiento y la experiencia de desarrollo entre usarlo en su estado predeterminado y aplicar los ajustes adecuados al comprender su arquitectura.&lt;/p>
&lt;p>En este artículo, comenzando con una explicación de la arquitectura fundamental de WSL2, explicaremos exhaustivamente todos los pasos para construir el &amp;ldquo;entorno de desarrollo definitivo&amp;rdquo; exigido por ingenieros profesionales, abarcando configuraciones para maximizar el rendimiento, la construcción de un entorno de terminal cómodo, la integración perfecta con Docker y VS Code, y configuraciones de red avanzadas, en un volumen de más de 10,000 caracteres.&lt;/p>
&lt;hr>
&lt;h2 id="1-arquitectura-de-wsl2-y-su-evolución-desde-wsl1">1. Arquitectura de WSL2 y su evolución desde WSL1
&lt;/h2>&lt;p>Para aprovechar todo el potencial de WSL2, es importante comprender primero su estructura interna. El enfoque para ejecutar binarios de Linux en Windows difiere fundamentalmente entre el WSL original (WSL1) y WSL2.&lt;/p>
&lt;h3 id="wsl1-capa-de-traducción-de-llamadas-al-sistema">WSL1: Capa de traducción de llamadas al sistema
&lt;/h3>&lt;p>WSL1 adoptó un mecanismo que traducía (translation) las llamadas al sistema (system calls) de Linux a las APIs NT de Windows en tiempo real. Esto tenía la ventaja de una sobrecarga de recursos (overhead) muy pequeña porque no utilizaba máquinas virtuales (VM). Sin embargo, era difícil emular completamente llamadas al sistema complejas, como operaciones de E/S del sistema de archivos, lo que provocaba una degradación del rendimiento abismal, especialmente al manejar un gran número de archivos pequeños en procesos como &lt;code>npm install&lt;/code> de Node.js u operaciones de repositorios de Git.&lt;/p>
&lt;h3 id="wsl2-máquina-virtual-de-utilidad-ligera-y-un-kernel-de-linux-completo">WSL2: Máquina Virtual de Utilidad Ligera y un Kernel de Linux completo
&lt;/h3>&lt;p>En WSL2, la arquitectura se renovó y un verdadero kernel de Linux construido por Microsoft ahora se ejecuta directamente sobre una &lt;strong>&amp;ldquo;Máquina Virtual de Utilidad Ligera (Lightweight Utility VM)&amp;rdquo; que utiliza un subconjunto de la arquitectura de Hyper-V&lt;/strong>. Esto garantiza el 100% de compatibilidad de llamadas al sistema, y al utilizar un disco virtual (VHDX) que emplea el sistema de archivos nativo ext4 de Linux, el rendimiento de E/S de archivos ha mejorado drásticamente en comparación con WSL1.&lt;/p>
&lt;p>El siguiente diagrama de Mermaid ilustra las diferencias estructurales entre WSL1 y WSL2.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
subgraph &amp;#34;Entorno del SO Windows&amp;#34;
A[&amp;#34;Kernel de Windows NT&amp;#34;]
A --&amp;gt; F[&amp;#34;Sistema de archivos NTFS (Disco C:)&amp;#34;]
end
subgraph &amp;#34;Arquitectura de WSL2&amp;#34;
B[&amp;#34;Hipervisor de Hyper-V&amp;#34;]
B --&amp;gt; C[&amp;#34;Máquina Virtual de Utilidad Ligera&amp;#34;]
C --&amp;gt; D[&amp;#34;Kernel de Linux (Microsoft)&amp;#34;]
D --&amp;gt; E[&amp;#34;Espacio de usuario de Ubuntu (glibc, bash, etc.)&amp;#34;]
D --&amp;gt; G[&amp;#34;Disco virtual ext4 (.vhdx)&amp;#34;]
end
A -.-&amp;gt;|&amp;#34;Recurso compartido de archivos de red del protocolo Plan 9 (9P)&amp;#34;| D
style B fill:#f9f,stroke:#333,stroke-width:2px
style D fill:#bbf,stroke:#333,stroke-width:2px
&lt;/pre>
&lt;p>Una lección importante de esta estructura es que &lt;strong>&amp;ldquo;el acceso a los archivos en el lado de Linux (dentro del VHDX) es extremadamente rápido, pero el acceso a los archivos en el lado de Windows (&lt;code>/mnt/c/&lt;/code>) es muy lento porque pasa por el protocolo 9P&amp;rdquo;&lt;/strong>. El código fuente de tus proyectos siempre debe ubicarse bajo el directorio de inicio (&lt;code>~&lt;/code>) en el lado de WSL.&lt;/p>
&lt;hr>
&lt;h2 id="2-análisis-matemático-del-rendimiento-por-qué-wsl2-es-rápido">2. Análisis matemático del rendimiento: ¿Por qué WSL2 es rápido?
&lt;/h2>&lt;p>Evaluemos cuantitativamente la mejora de rendimiento de WSL2 utilizando un modelo matemático. En el desarrollo de software, una de las operaciones que más tiempo consume es el procesamiento que implica operaciones intensivas de E/S de archivos (por ejemplo, instalación de bibliotecas o compilación).&lt;/p>
&lt;p>El tiempo total de ejecución de cierto proceso $T_{total}$ se expresa como la suma del tiempo computacional por la CPU $T_{compute}$ y el tiempo que tardan las operaciones de E/S de disco $T_{io}$.&lt;/p>
$$ T_{total} = T_{compute} + T_{io} $$&lt;p>En el caso de WSL1, se produce una sobrecarga para traducir las operaciones del lado de Linux en operaciones NTFS, por lo que el tiempo de E/S se modela de la siguiente manera. Aquí, $n$ es el número de operaciones de archivo, $t_{ntfs\_syscall}$ es el tiempo de ejecución de la llamada al sistema en el lado de Windows y $t_{trans}$ es la sobrecarga de la capa de traducción.&lt;/p>
$$ T_{wsl1\_io} = \sum_{i=1}^{n} (t_{ntfs\_syscall_i} + t_{trans_i}) $$&lt;p>Por otro lado, en el caso de WSL2, dado que el kernel emite E/S directamente al sistema de archivos ext4, la sobrecarga es solo el ligero retraso $t_{virt}$ debido a la virtualización.&lt;/p>
$$ T_{wsl2\_io} = \sum_{i=1}^{n} (t_{ext4_i} + t_{virt_i}) $$&lt;p>En los sistemas de archivos generales, como $t_{ext4} \ll t_{ntfs\_syscall} + t_{trans}$, cuando $n$ es muy grande (realizando decenas de miles a cientos de miles de operaciones de archivo), la diferencia en el tiempo de E/S entre WSL1 y WSL2 crece exponencialmente.&lt;/p>
&lt;p>Además, si la relación de sobrecarga del cálculo de la CPU en el entorno de virtualización es $\rho$, en la virtualización asistida por hardware moderna (Intel VT-x / AMD-V) se sitúa en alrededor de $\rho \approx 0.01 \sim 0.03$ (1 a 3%). Por lo tanto, incluso en tareas puramente computacionales, ofrece un rendimiento del $97\% \sim 99\%$, que es comparable al de un entorno Linux nativo.&lt;/p>
&lt;hr>
&lt;h2 id="3-instalación-y-construcción-de-la-base">3. Instalación y construcción de la base
&lt;/h2>&lt;p>En Windows 10/11, la instalación de WSL2 se ha vuelto muy sencilla. Simplemente abre PowerShell con privilegios de administrador y ejecuta el siguiente comando.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># WSL2 y Ubuntu se instalarán por defecto&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-install&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Si especificas una distribución en particular&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Se puede comprobar con wsl --list --online&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-install&lt;/span> &lt;span class="n">-d&lt;/span> &lt;span class="n">Ubuntu&lt;/span>&lt;span class="p">-&lt;/span>&lt;span class="mf">24.04&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Después de la instalación, tras reiniciar, se te pedirá que configures un nombre de usuario y una contraseña de UNIX en el primer inicio. Este usuario es independiente del usuario de Windows y sólo es válido dentro de WSL.&lt;/p>
&lt;p>Si ya estás usando WSL1, conviértelo a WSL2 con los siguientes comandos.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Convierte una distribución existente a WSL2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-set-version&lt;/span> &lt;span class="n">Ubuntu&lt;/span> &lt;span class="mf">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Establece WSL2 como versión por defecto para las distribuciones que añadas en el futuro&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-set-default-version&lt;/span> &lt;span class="mf">2&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="4-el-secreto-del-control-de-recursos-wslconfig-y-wslconf">4. El secreto del control de recursos: .wslconfig y wsl.conf
&lt;/h2>&lt;p>Una de las mayores trampas de WSL2 es el &amp;ldquo;consumo ilimitado de memoria (el aumento de tamaño del proceso Vmmem)&amp;rdquo;. Como WSL2 utiliza la caché de páginas (page cache) del kernel de Linux, devora la memoria del host (Windows) sin cesar cada vez que realiza E/S. Para evitar esto, es imprescindible limitar los recursos usando un archivo de configuración.&lt;/p>
&lt;p>Los archivos de configuración de WSL2 se dividen en dos: &lt;strong>&lt;code>.wslconfig&lt;/code> que afecta a todo Windows&lt;/strong> y &lt;strong>&lt;code>wsl.conf&lt;/code> que afecta al interior de cada distribución&lt;/strong>.&lt;/p>
&lt;h3 id="41-wslconfig-lado-de-windows">4.1. .wslconfig (Lado de Windows)
&lt;/h3>&lt;p>Crea un archivo en el directorio de perfil de usuario de Windows (&lt;code>C:\Users\&amp;lt;nombre_de_usuario&amp;gt;\.wslconfig&lt;/code>) para controlar la asignación de recursos a la máquina virtual.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># C:\Users\&amp;lt;nombre_de_usuario&amp;gt;\.wslconfig&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[wsl2]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Cantidad máxima de memoria para asignar a la VM. Se recomienda alrededor del 50% al 75% de la memoria total del host&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">memory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">16GB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Número de núcleos de CPU a utilizar (si se omite, se usarán todos)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">processors&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">8&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Tamaño del archivo de intercambio (swap)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">swap&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">8GB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Destino donde guardar el archivo de intercambio (si quieres ahorrar espacio en el disco C)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># swapfile=D:\\wsl\\swap.vhdx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Habilitar el reenvío de localhost (para acceder a WSL desde Windows a través de localhost)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">localhostForwarding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Liberar memoria automáticamente (solo Windows 11)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Libera dinámicamente la caché de páginas y previene el crecimiento excesivo de Vmmem&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">autoMemoryReclaim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">dropcache&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[experimental]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Funciones de red avanzadas disponibles en Windows 11 22H2 y posteriores&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Esto habilita el soporte de IPv6 y compartir la misma dirección IP entre WSL y Windows&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">networkingMode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">mirrored&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">dnsTunneling&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">firewall&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">autoProxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="42-wslconf-lado-de-linux">4.2. wsl.conf (Lado de Linux)
&lt;/h3>&lt;p>Edita &lt;code>/etc/wsl.conf&lt;/code> dentro de WSL para controlar el comportamiento específico de la distribución.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /etc/wsl.conf (editar dentro de WSL)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[network]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Deshabilitar la generación automática de /etc/resolv.conf al iniciar WSL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Útil si quieres establecer tu propio DNS (ej. 8.8.8.8)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">generateResolvConf&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Establecer tu propio nombre de host&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">hostname&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">WSL-DevNode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[automount]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Configuración para cuando se montan unidades de Windows&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">enabled&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">options&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;metadata,uid=1000,gid=1000,umask=022&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Cambiar el punto de montaje de la unidad C de /mnt/c a /c (hace la ruta más corta)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">root&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[boot]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Habilitar systemd (WSL 0.67.6 y posteriores)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Esto permite que snap y varios demonios (Docker, etc.) se ejecuten de forma nativa&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">systemd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[user]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># El usuario que inicia sesión por defecto&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">default&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">kenji&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Para aplicar estas configuraciones, es necesario ejecutar &lt;code>wsl --shutdown&lt;/code> en PowerShell para detener por completo la máquina virtual de WSL y luego reiniciarla.&lt;/p>
&lt;hr>
&lt;h2 id="5-el-entorno-de-terminal-definitivo-zsh--powerlevel10k">5. El entorno de terminal definitivo: Zsh + Powerlevel10k
&lt;/h2>&lt;p>La productividad no mejorará con bash por defecto. Combinaremos Zsh, que cuenta con una gran capacidad de autocompletado y visibilidad, con el ultrarrápido tema &amp;ldquo;Powerlevel10k&amp;rdquo; para construir el prompt más potente.&lt;/p>
&lt;h3 id="51-introducción-y-configuración-de-windows-terminal">5.1. Introducción y configuración de Windows Terminal
&lt;/h3>&lt;p>Instala &amp;ldquo;Windows Terminal&amp;rdquo; desde Microsoft Store. Abre la configuración JSON (&lt;code>settings.json&lt;/code>), establece el perfil predeterminado en WSL (Ubuntu) y cambia la fuente a una fuente Nerd para desarrollo (por ejemplo: &lt;code>HackGen Console NF&lt;/code> o &lt;code>MesloLGS NF&lt;/code>).&lt;/p>
&lt;h3 id="52-instalación-de-zsh-y-oh-my-zsh">5.2. Instalación de Zsh y Oh My Zsh
&lt;/h3>&lt;p>Ejecuta los siguientes comandos en el terminal de WSL.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Actualizar los paquetes e instalar Zsh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> sudo apt upgrade -y
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt install -y zsh git curl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ejecutar el script de instalación de Oh My Zsh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sh -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="53-introducción-a-powerlevel10k-y-plugins">5.3. Introducción a Powerlevel10k y plugins
&lt;/h3>&lt;p>Instala el tema Powerlevel10k y plugins (resaltado de sintaxis y autocompletado) que potencian aún más a Zsh.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Powerlevel10k&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> https://github.com/romkatv/powerlevel10k.git &lt;span class="si">${&lt;/span>&lt;span class="nv">ZSH_CUSTOM&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="p">/.oh-my-zsh/custom&lt;/span>&lt;span class="si">}&lt;/span>/themes/powerlevel10k
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># zsh-autosuggestions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone https://github.com/zsh-users/zsh-autosuggestions &lt;span class="si">${&lt;/span>&lt;span class="nv">ZSH_CUSTOM&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="p">~/.oh-my-zsh/custom&lt;/span>&lt;span class="si">}&lt;/span>/plugins/zsh-autosuggestions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># zsh-syntax-highlighting&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone https://github.com/zsh-users/zsh-syntax-highlighting.git &lt;span class="si">${&lt;/span>&lt;span class="nv">ZSH_CUSTOM&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="p">~/.oh-my-zsh/custom&lt;/span>&lt;span class="si">}&lt;/span>/plugins/zsh-syntax-highlighting
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Edita &lt;code>~/.zshrc&lt;/code> y activa el tema y los plugins.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Cambios en ~/.zshrc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ZSH_THEME&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;powerlevel10k/powerlevel10k&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Añadir a la matriz (array) de plugins&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">plugins&lt;/span>&lt;span class="o">=(&lt;/span>git zsh-autosuggestions zsh-syntax-highlighting&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Al guardar y ejecutar &lt;code>source ~/.zshrc&lt;/code>, se iniciará el asistente de configuración de Powerlevel10k (&lt;code>p10k configure&lt;/code>). Sigue las instrucciones en pantalla para personalizar el prompt a tu gusto (estilo del prompt, con o sin iconos, información que mostrar, etc.). Podrás ver en tiempo real el nombre de la rama (branch) y el estado de Git, la versión de Node.js, el tiempo de ejecución del comando, lo que aumentará espectacularmente tu eficiencia en el desarrollo.&lt;/p>
&lt;hr>
&lt;h2 id="6-vs-code-remote---integración-perfecta-con-wsl">6. VS Code Remote - Integración perfecta con WSL
&lt;/h2>&lt;p>En el desarrollo con WSL2, la extensión &amp;ldquo;Remote - WSL&amp;rdquo; proporciona el mecanismo para acceder sin problemas a los archivos dentro de WSL desde un IDE (Visual Studio Code) instalado en el lado de Windows.&lt;/p>
&lt;h3 id="explicación-de-la-arquitectura">Explicación de la arquitectura
&lt;/h3>&lt;p>El siguiente diagrama de secuencia muestra cómo VS Code se comunica con WSL2.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
autonumber
participant U as &amp;#34;Desarrollador&amp;#34;
participant V as &amp;#34;Interfaz de usuario de VS Code (Windows)&amp;#34;
participant S as &amp;#34;Servidor de VS Code (WSL2)&amp;#34;
participant F as &amp;#34;Sistema de archivos ext4 (WSL2)&amp;#34;
U-&amp;gt;&amp;gt;V: &amp;#34;Escribe `code .` en el terminal de WSL&amp;#34;
V-&amp;gt;&amp;gt;S: &amp;#34;Establece una conexión RPC a través de Vsock&amp;#34;
Note over V,S: Se comunica a través de un socket de Hyper-V sin utilizar TCP/IP
S-&amp;gt;&amp;gt;F: &amp;#34;Lee archivos fuente / Ejecuta el linter&amp;#34;
F--&amp;gt;&amp;gt;S: &amp;#34;Devuelve datos y análisis&amp;#34;
S--&amp;gt;&amp;gt;V: &amp;#34;Transmite los resultados del servidor de idiomas a la interfaz de usuario&amp;#34;
V--&amp;gt;&amp;gt;U: &amp;#34;Muestra resaltado de sintaxis y errores&amp;#34;
&lt;/pre>
&lt;p>El VS Code en el lado de Windows funciona simplemente como un &amp;ldquo;cliente ligero (thin client / UI)&amp;rdquo;, mientras que el procesamiento pesado, como el Language Server, el depurador (debugger) y la ejecución del terminal, son procesados íntegramente por el &amp;ldquo;Servidor de VS Code&amp;rdquo; en el lado de WSL. Esto te permite mantener tu entorno del lado de WSL limpio sin tener que instalar Node.js o Python en el lado de Windows.&lt;/p>
&lt;h3 id="configuraciones-obligatorias-en-vs-code">Configuraciones obligatorias en VS Code
&lt;/h3>&lt;p>Instala &lt;strong>&amp;ldquo;WSL&amp;rdquo; (ms-vscode-remote.remote-wsl)&lt;/strong> desde la sección &amp;ldquo;Extensiones&amp;rdquo; de VS Code. Luego, ve al directorio del proyecto en el terminal de WSL y simplemente ejecuta &lt;code>code .&lt;/code>, el VS Code del lado de Windows se abrirá en ese directorio.&lt;/p>
&lt;p>&lt;strong>Nota importante (El problema del código de salto de línea):&lt;/strong>
Los códigos de salto de línea difieren entre Windows y Linux (Windows es &lt;code>CRLF&lt;/code>, Linux es &lt;code>LF&lt;/code>). Si estás desarrollando en WSL, asegúrate siempre de unificar la configuración &lt;code>core.autocrlf&lt;/code> de Git o la configuración de archivos por defecto de VS Code a &lt;code>LF&lt;/code>. Si omites este paso, sufrirás errores misteriosos al ejecutar scripts de shell o contenedores de Docker.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Configuración del código de salto de línea para Git en el lado de WSL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config --global core.autocrlf input
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Añade también lo siguiente en &lt;code>settings.json&lt;/code> (configuración remota) de VS Code.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;files.eol&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;\n&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;terminal.integrated.defaultProfile.linux&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;zsh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-optimización-de-docker-desktop-e-integración-con-wsl2">7. Optimización de Docker Desktop e Integración con WSL2
&lt;/h2>&lt;p>Hay dos enfoques principales para usar Docker en el entorno WSL2:&lt;/p>
&lt;ol>
&lt;li>Instalar &lt;strong>Docker Desktop for Windows&lt;/strong> y habilitar la función de integración con WSL2.&lt;/li>
&lt;li>Instalar el &lt;strong>Docker Engine nativo&lt;/strong> directamente dentro de WSL2 (Ubuntu, etc.).&lt;/li>
&lt;/ol>
&lt;h3 id="enfoque-1-docker-desktop-recomendado">Enfoque 1: Docker Desktop (Recomendado)
&lt;/h3>&lt;p>Se suele recomendar esto en muchos casos porque facilita la gestión mediante interfaz gráfica (GUI) y el acceso transparente de los contenedores entre Windows/WSL. Verifica lo siguiente en la configuración de Docker Desktop (Settings).&lt;/p>
&lt;ul>
&lt;li>Marca la casilla de &lt;code>General&lt;/code> -&amp;gt; &lt;code>Use the WSL 2 based engine&lt;/code>.&lt;/li>
&lt;li>Marca la casilla de &lt;code>Resources&lt;/code> -&amp;gt; &lt;code>WSL Integration&lt;/code> -&amp;gt; &lt;code>Enable integration with my default WSL distro&lt;/code> y activa el interruptor (toggle button) de la distribución que vas a usar (Ubuntu).&lt;/li>
&lt;/ul>
&lt;p>Esto te permitirá ejecutar el comando &lt;code>docker&lt;/code> directamente desde el terminal de WSL2 y la comunicación con el demonio de Docker (Docker daemon) se realizará a través de máquinas virtuales ligeras y exclusivas (&lt;code>docker-desktop&lt;/code> y &lt;code>docker-desktop-data&lt;/code>) gestionadas por Docker Desktop.&lt;/p>
&lt;h3 id="enfoque-2-instalación-directa-de-docker-engine-nativo">Enfoque 2: Instalación directa de Docker Engine nativo
&lt;/h3>&lt;p>Si tienes restricciones por parte de la red corporativa (como evadir los costos de licencia de Docker Desktop) o deseas reducir la sobrecarga de rendimiento al extremo, instala Docker puramente como un servidor Ubuntu con &lt;code>systemd&lt;/code> habilitado en &lt;code>/etc/wsl.conf&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fragmento de los pasos oficiales de instalación de Docker en WSL2 Ubuntu con systemd habilitado&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get install ca-certificates curl gnupg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo install -m &lt;span class="m">0755&lt;/span> -d /etc/apt/keyrings
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -fsSL https://download.docker.com/linux/ubuntu/gpg &lt;span class="p">|&lt;/span> sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo chmod a+r /etc/apt/keyrings/docker.gpg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Agregar el repositorio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s2">&amp;#34;deb [arch=&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>dpkg --print-architecture&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34; signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>. /etc/os-release &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$VERSION_CODENAME&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34; stable&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Agregar el usuario actual al grupo docker (para ejecutar sin sudo)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo usermod -aG docker &lt;span class="nv">$USER&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Después de reiniciar, &lt;code>systemctl start docker&lt;/code> funcionará exactamente igual que en un entorno Linux nativo, ofreciendo un alto rendimiento.&lt;/p>
&lt;hr>
&lt;h2 id="8-integración-de-claves-ssh-autenticación-perfecta-entre-windows-y-wsl">8. Integración de claves SSH: Autenticación perfecta entre Windows y WSL
&lt;/h2>&lt;p>Al hacer clones SSH de Git o conectarse por SSH a un servidor remoto, resulta muy molesto gestionar claves SSH separadas entre el lado de Windows y el lado de WSL. Para equilibrar seguridad y conveniencia, configuraremos un puente (bridge) para el agente SSH que se ejecuta en el lado de Windows (o un gestor de contraseñas como 1Password) hacia el lado de WSL.&lt;/p>
&lt;p>Aquí explicaremos cómo reenviar al socket de dominio UNIX (UNIX domain socket) de WSL2 mediante &lt;code>npiperelay&lt;/code> o &lt;code>socat&lt;/code>, empleando la &lt;strong>función del agente SSH de 1Password&lt;/strong> o el &lt;strong>Agente de autenticación de OpenSSH de Windows&lt;/strong>, como los enfoques más modernos y seguros.&lt;/p>
&lt;h3 id="reenvío-de-sockets-de-ssh-agent">Reenvío de sockets de ssh-agent
&lt;/h3>&lt;p>Normalmente, el agente SSH proporcionado como una tubería con nombre (Named Pipe) en Windows debe convertirse en un archivo de socket (socket file) en el lado de WSL. Es fácil utilizando la función de &lt;code>wsl-ssh-agent&lt;/code> o la proporcionada por 1Password.&lt;/p>
&lt;p>En la pantalla de configuración de 1Password, ve a &amp;ldquo;Desarrollador (Developer)&amp;rdquo; -&amp;gt; habilita &amp;ldquo;Usar el agente SSH (Use SSH agent)&amp;rdquo;.
Luego, añade la siguiente configuración a &lt;code>~/.zshrc&lt;/code> o &lt;code>~/.bashrc&lt;/code> en el lado de WSL, para enlazar el socket automáticamente al iniciar sesión.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Apunte en ~/.zshrc (Ejemplo usando 1Password SSH Agent)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">SSH_AUTH_SOCK&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$HOME&lt;/span>/.ssh/agent.sock
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Usar socat y npiperelay para reenviar si el socket no existe o el proceso no está enlazado al iniciar WSL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ALREADY_RUNNING&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>ps -aux &lt;span class="p">|&lt;/span> grep &lt;span class="s2">&amp;#34;[n]piperelay.exe -ei -s //./pipe/openssh-ssh-agent&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> wc -l&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> &lt;span class="nv">$ALREADY_RUNNING&lt;/span> -eq &lt;span class="m">0&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -S &lt;span class="nv">$SSH_AUTH_SOCK&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rm &lt;span class="nv">$SSH_AUTH_SOCK&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Inicia socat en segundo plano (background) y conecta la tubería con nombre del lado de Windows al socket UNIX del lado de WSL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">(&lt;/span>setsid socat UNIX-LISTEN:&lt;span class="nv">$SSH_AUTH_SOCK&lt;/span>,fork EXEC:&lt;span class="s2">&amp;#34;npiperelay.exe -ei -s //./pipe/openssh-ssh-agent&amp;#34;&lt;/span>,nofork &lt;span class="p">&amp;amp;&lt;/span>&lt;span class="o">)&lt;/span> &amp;gt;/dev/null 2&amp;gt;&lt;span class="p">&amp;amp;&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>Es necesario instalar de antemano &lt;code>npiperelay.exe&lt;/code> en el lado de Windows y añadirlo a la variable PATH (ruta).&lt;/em>&lt;/p>
&lt;p>Una vez completada esta configuración, al ejecutar &lt;code>ssh-add -l&lt;/code> desde el terminal de WSL, se mostrará una lista de claves públicas de las claves SSH registradas en 1Password o en el lado de Windows. Esto permite pasar la autenticación de forma segura sin copiar el archivo de la clave privada (private key) al interior de WSL.&lt;/p>
&lt;hr>
&lt;h2 id="9-mantenimiento-optimización-compresión-de-un-vhdx-inflado">9. Mantenimiento: Optimización (Compresión) de un VHDX inflado
&lt;/h2>&lt;p>Una de las mayores desventajas de WSL2 es la especificación en la que &amp;ldquo;el tamaño de archivo del disco virtual (.vhdx) del lado de Windows no se reduce automáticamente incluso al eliminar imágenes de Docker o borrar archivos&amp;rdquo;. Si el desarrollo continúa durante mucho tiempo, el archivo ext4.vhdx se hincha de decenas a cientos de GB.&lt;/p>
&lt;p>Para liberar espacio en disco, es necesario optimizar (comprimir - Compact) el VHDX periódicamente desde el lado de Windows.&lt;/p>
&lt;ol>
&lt;li>En primer lugar, apaga WSL por completo.
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-shutdown&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>Abre PowerShell con privilegios de administrador y ejecuta el siguiente comando &lt;code>diskpart&lt;/code>, o el comando &lt;code>Optimize-VHD&lt;/code> del módulo de Hyper-V (este último solo se puede utilizar si Hyper-V está activado).&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Si el módulo Hyper-V está disponible&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Optimize-VHD&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:LOCALAPPDATA&lt;/span>&lt;span class="s2">\Packages\CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc\LocalState\ext4.vhdx&amp;#34;&lt;/span> &lt;span class="n">-Mode&lt;/span> &lt;span class="n">Full&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Si usas diskpart&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">diskpart&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Escribe interactivamente en la siguiente línea de comandos&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DISKPART&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nb">select &lt;/span>&lt;span class="n">vdisk&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="s2">&amp;#34;C:\Users\&amp;lt;nombre_de_usuario&amp;gt;\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc\LocalState\ext4.vhdx&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DISKPART&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">attach&lt;/span> &lt;span class="n">vdisk&lt;/span> &lt;span class="n">readonly&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DISKPART&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">compact&lt;/span> &lt;span class="n">vdisk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DISKPART&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">detach&lt;/span> &lt;span class="n">vdisk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DISKPART&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">exit&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Realizar esta operación de forma regular te permitirá recuperar la capacidad desperdiciada del disco C.&lt;/p>
&lt;hr>
&lt;h2 id="10-conclusión">10. Conclusión
&lt;/h2>&lt;p>WSL2 ha superado por completo los límites de ser solo un &amp;ldquo;extra de Linux ejecutándose en Windows&amp;rdquo; y ha evolucionado hacia una potente plataforma de desarrollo equivalente, o incluso superior, a un sistema MacOS o Linux nativo.&lt;/p>
&lt;p>Al aplicar todas las configuraciones explicadas en este momento (optimización de recursos mediante &lt;code>.wslconfig&lt;/code>, potenciación del terminal con Zsh + Powerlevel10k, acceso transparente con VS Code Remote y mantenimiento de VHDX e integración de SSH), se completará el &amp;ldquo;entorno de desarrollo definitivo&amp;rdquo;, sin estrés, rápido y seguro.&lt;/p>
&lt;p>Lleva un poco de tiempo configurarlo, pero una vez que hayas solidificado la configuración, no hay duda de que la productividad de la ingeniería mejorará drásticamente en el futuro. Anímate y explora futuras personalizaciones basadas en esta guía, según tus preferencias y proyectos.&lt;/p></description></item></channel></rss>