{
    "componentChunkName": "component---node-modules-gatsby-theme-try-ghost-src-templates-post-js",
    "path": "/docker-iii-mas-complicado/",
    "result": {"data":{"ghostPost":{"id":"Ghost__Post__5be6b01a2dd6610fd828ca02","title":"Docker III: Más complicado","slug":"docker-iii-mas-complicado","featured":false,"feature_image":"https://jlgarcia.fulldev.ninja/assets/images/2018/12/docker_facebook_share-2.png","excerpt":"Empecemos con cosas más complicadas, ahora vamos a preparar un entorno que se\ncomunique entre sí y en el que podamos crear varios contenedores que se\ncomuniquen entre con un solo comando.\nPara poder hacer esto, empezaremos a ver algo que se llama docker-compose, esto\nson otra serie de comandos pertenecientes al entorno de Docker, en el que\nbásicamente se busca un fichero docker-compose.yml con las instrucciones que se\ndeben seguir.\nLo primero, en nuestra carpeta visits donde hemos creado anterio","custom_excerpt":null,"visibility":"public","created_at_pretty":"10 Nov 2018","published_at_pretty":"1 Sep 2018","updated_at_pretty":"19 Jan 2021","created_at":"2018-11-10T11:16:58.000+01:00","published_at":"2018-09-01T12:32:00.000+02:00","updated_at":"2021-01-19T20:53:24.000+01:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"Perfil.jpg","publicURL":"/static/b0de6281fb28a266510b3b09b9243e5a/Perfil.jpg","imageMeta":{"width":307,"height":307},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAUDBAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGzw6zC6zHn+cLYP//EAB0QAAICAQUAAAAAAAAAAAAAAAEDAAIEEyEiIzL/2gAIAQEAAQUCifca8KgcKWVfUpkHsG5pxX//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAdEAACAgEFAAAAAAAAAAAAAAAAARARcQISIUFR/9oACAEBAAY/AhU88xkb7N06a8P/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUFRYXH/2gAIAQEAAT8hR2pq40aqb+xIAeXibhW9JXr8joF4TBcSNe0//9oADAMBAAIAAwAAABDzDwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFhcfD/2gAIAQEAAT8QyItrELaTlatLwU63MvEW6vUNdy4LZQDn7iVApV9VLtANdWwKkuYq4Er1VZ//2Q==","aspectRatio":1,"src":"/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg","srcSet":"/static/b0de6281fb28a266510b3b09b9243e5a/f340b/Perfil.jpg 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/22d64/Perfil.jpg 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/aa249/Perfil.jpg 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/0dc33/Perfil.jpg 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/60667/Perfil.jpg 307w","srcWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp","srcSetWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/59cda/Perfil.webp 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/7da75/Perfil.webp 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f282e/Perfil.webp 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/a7b21/Perfil.webp 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f59af/Perfil.webp 307w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},"tags":[{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null}],"plaintext":"Empecemos con cosas más complicadas, ahora vamos a preparar un entorno que se\ncomunique entre sí y en el que podamos crear varios contenedores que se\ncomuniquen entre con un solo comando.\nPara poder hacer esto, empezaremos a ver algo que se llama docker-compose, esto\nson otra serie de comandos pertenecientes al entorno de Docker, en el que\nbásicamente se busca un fichero docker-compose.yml con las instrucciones que se\ndeben seguir.\nLo primero, en nuestra carpeta visits donde hemos creado anteriormente nuestro \ndockerfile con nuestra aplicación en NodeJS, nos creamos el fichero \ndocker-compose.yml y vayamos poco a poco\n\nversion: '3'\nservices:\n\n\n * version: Hace referencia a la versión de la composición, esto le indica a\n   docker, cuando ejecutamos comandos del CLI de docker-compose, si es versión\n   más actual de docker-compose (a fecha de este texto es la 3).\n * services: Dentro del fichero, la propiedad services hace referencia a los\n   contenedores, no a un servicios, dentro de este apartado indicaremos todos\n   los contenedores que crearemos mediante esta composición.\n\nversion: '3'\nservices:\n  redis-server:\n    image: 'redis'\n  node-app:\n    build: .\n    ports:\n      - \"4001:8081\"\n\n\nDentro del apartado de services hemos indicado 2 contenedores: redis-server y \nnode-app. Un detalle importante antes de continuar: TODOS LOS CONTENEDORES\nDENTRO DEL MISMO DOCKER-COMPOSE ESTÁN DENTRO DE LA MISMA RED, es decir, tienen\ncomunicación entre ellos, y una forma de llamarse unos a otros es con el propio\nnombre que hemos indicado en el fichero de docker-compose.yml, por lo que si\nvolvemos a nuestro fichero index.js donde tenemos nuestra aplicación de NodeJS,\ntenemos que hacer un pequeño cambio que antes habíamos ignorado, cuando creamos\nnuestro cliente de redis es necesario indicarle el host y el puerto del servidor\nque tiene el servicio de redis-server (siempre es necesario indicarlo menos\ncuando está en el mismo servidor que el cliente). Para indicarlo en este caso\nsolo es necesario con:\n\nconst client = redis.createClient({\n    host: 'redis-server',\n    port: 6379\n});\n\n\nSi nos fijamos hemos puesto el mismo nombre que en nuestro docker-compose,\nrealmente lo que esta sucediendo es que cuando la petición llega a la red\ninterna de docker el está reconociendo el nombre y sabe donde ir a buscarlo.\nSabiendo esto y ya teniendo el cambio continuemos con nuestro docker-compose.yml\n, teniamos esto:\n\nversion: '3'\nservices:\n  redis-server:\n    image: 'redis'\n  node-app:\n    build: .\n    ports:\n      - \"4001:8081\"\n\n\nSiguiendo con lo que nos faltaba:\n\n * image: Básicamente le estamos indicando que ese contenedor use la imagen de\n   contenedor con nombre redis (ya sea nuestra o de la docker store)\n * build: Le indicamos que inicie el proceso de build con el fichero dockerfile \n   que se encuentra en la ruta indicada.\n * ports: Puertos a mapear en este contenedor de nuestro equipo local.\n\nCon esto ya tendríamos todo listo para que nuestro conjunto de contenedores\nfuncionaran, vamos a probarlos. Para ello basta con ejecutar:\n\ndocker-compose up\n\n\n\n\nComo vemos tenemos los dos contenedores levantados con los nombres que le hemos\nindicado en el fichero docker-compose.yml. Si ahora accedemos a localhost:4001 \n(recordad que hemos cambiado el puerto al hacer el mapeo)\n\n\n\nVemos como funciona perfectamente.\n\nComandos docker-compose\nVeamos algunos comandos de docker-compose:\n\n * docker-compose up: Funciona como docker run, busca el fichero docker-compose\n   up y ejecuta lo que aparece en el.\n * docker-compose up --build: Fuerza a rehacer los contenedores antes de\n   ejecutarlos. Muy útil si hemos realizado algún cambio en nuestro código de\n   aplicación.\n * docker-compose up -d: Arranca los contenedores en segundo plano.\n * docker-compose down: Para los contenedores y elimina tanto los contenedores\n   como la red que crea entre ellos.\n * docker-compose start: Arranca los contenedores si están parados (OJO parados\n   que no eliminados)\n * docker-compose stop: Para los contenedores y NO elimina nada.\n * docker-compose ps: Al igual que docker ps, vemos el estado de nuestra \n   composición de contenedores.\n\nTODOS LOS COMANDOS DE DOCKER COMPOSE NECESITAN EL FICHERO DE docker-compose.yml\nPARA FUNCIONAR\n\nReinicio automático de contenedores\nPuede pasar que alguno de nuestros contenedores se detenga, por algún error por\nejemplo, en este caso docker ya está preparado para automáticamente reiniciar\nlos contenedores según como lo configuremos en nuestro docker-compose.yml.\nTenemos estas opciones de reinicio:\n\n * \"no\": Es la configuración por defecto de los contenedores. No se reinician en\n   ningún caso. Para poner explicitamente no es necesario ponerlo entre \"\"\n * always: Siempre que se pare, sea cual sea la razón, automáticamente se\n   reinicia.\n * on-failure: Solo se reinicia cuando se pare con un error code. En general si\n   el código de salida ha sido un 0, eso significa que se ha parado de manera\n   controlada o porque ha terminado su ejecución correctamente, sin embargo, si\n   el código con el termina es distinto de 0 entonces lo tomará como un error y\n   reiniciará el contenedor.\n * unless-stopped: Siempre se reinicia a menos que lo hayamos parado nosotros\n   manualmente.\n\nPara configurar estas opciones solo tenemos que indicarselo en el fichero de la\nsiguiente forma:\n\nversion: '3'\nservices:\n  redis-server:\n    image: 'redis'\n  node-app:\n    restart: always <--Con esta opción\n    build: .\n    ports:\n      - \"4001:8081\"\n\n\nEsto último era un extra que nos puede ser útil a la hora de configurar nuestro\ncontenedor. Nos vemos en el siguienteeee, un abrazooor.","html":"<!--kg-card-begin: markdown--><p>Empecemos con cosas más complicadas, ahora vamos a preparar un entorno que se comunique entre sí y en el que podamos crear varios contenedores que se comuniquen entre con un solo comando.<br>\nPara poder hacer esto, empezaremos a ver algo que se llama <strong>docker-compose</strong>, esto son otra serie de comandos pertenecientes al entorno de Docker, en el que básicamente se busca un fichero <strong>docker-compose.yml</strong> con las instrucciones que se deben seguir.<br>\nLo primero, en nuestra carpeta <strong>visits</strong> donde hemos creado anteriormente nuestro <strong>dockerfile</strong> con nuestra aplicación en NodeJS, nos creamos el fichero <strong>docker-compose.yml</strong> y vayamos poco a poco</p>\n<pre><code class=\"language-yaml\">version: '3'\nservices:\n</code></pre>\n<ul>\n<li><strong>version</strong>: Hace referencia a la versión de la <em><strong>composición</strong></em>, esto le indica a docker, cuando ejecutamos comandos del CLI de docker-compose, si es versión más actual de <strong>docker-compose</strong> (a fecha de este texto es la 3).</li>\n<li><strong>services</strong>: Dentro del fichero, la propiedad services hace referencia a los contenedores, no a un servicios, dentro de este apartado indicaremos todos los contenedores que crearemos mediante esta <em><strong>composición</strong></em>.</li>\n</ul>\n<pre><code class=\"language-yaml\">version: '3'\nservices:\n  redis-server:\n    image: 'redis'\n  node-app:\n    build: .\n    ports:\n      - &quot;4001:8081&quot;\n</code></pre>\n<p>Dentro del apartado de services hemos indicado 2 contenedores: <strong>redis-server</strong> y <strong>node-app</strong>. Un detalle importante antes de continuar: <strong>TODOS LOS CONTENEDORES DENTRO DEL MISMO DOCKER-COMPOSE ESTÁN DENTRO DE LA MISMA RED</strong>, es decir, tienen comunicación entre ellos, y una forma de llamarse unos a otros es <strong>con el propio nombre que hemos indicado en el fichero de docker-compose.yml</strong>, por lo que si volvemos a nuestro fichero <em>index.js</em> donde tenemos nuestra aplicación de NodeJS, tenemos que hacer un pequeño cambio que antes habíamos ignorado, cuando creamos nuestro cliente de redis es necesario indicarle el host y el puerto del servidor que tiene el servicio de <strong>redis-server</strong> (siempre es necesario indicarlo menos cuando está en el mismo servidor que el cliente). Para indicarlo en este caso solo es necesario con:</p>\n<pre><code class=\"language-javascript\">const client = redis.createClient({\n    host: 'redis-server',\n    port: 6379\n});\n</code></pre>\n<p>Si nos fijamos hemos puesto el mismo nombre que en nuestro <strong>docker-compose</strong>, realmente lo que esta sucediendo es que cuando la petición llega a la <em>red interna de docker</em> el está reconociendo el nombre y sabe donde ir a buscarlo.<br>\nSabiendo esto y ya teniendo el cambio continuemos con nuestro <strong>docker-compose.yml</strong>, teniamos esto:</p>\n<pre><code class=\"language-yaml\">version: '3'\nservices:\n  redis-server:\n    image: 'redis'\n  node-app:\n    build: .\n    ports:\n      - &quot;4001:8081&quot;\n</code></pre>\n<p>Siguiendo con lo que nos faltaba:</p>\n<ul>\n<li><strong>image</strong>: Básicamente le estamos indicando que ese contenedor use la imagen de contenedor con nombre <em>redis</em> (ya sea nuestra o de la <em>docker store</em>)</li>\n<li><strong>build</strong>: Le indicamos que inicie el proceso de <strong>build</strong> con el fichero <strong>dockerfile</strong> que se encuentra en la ruta indicada.</li>\n<li><strong>ports:</strong> Puertos a <em>mapear</em> en este contenedor de nuestro equipo local.</li>\n</ul>\n<p>Con esto ya tendríamos todo listo para que nuestro conjunto de contenedores funcionaran, vamos a probarlos. Para ello basta con ejecutar:</p>\n<pre><code class=\"language-shell\">docker-compose up\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-12-a-las-9.13.39.png\" alt=\"Captura-de-pantalla-2018-11-12-a-las-9.13.39\"></p>\n<p>Como vemos tenemos los dos contenedores levantados con los nombres que le hemos indicado en el fichero <strong>docker-compose.yml</strong>. Si ahora accedemos a <strong>localhost:4001</strong> (recordad que hemos cambiado el puerto al hacer el mapeo)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-12-a-las-9.17.06.png\" alt=\"Captura-de-pantalla-2018-11-12-a-las-9.17.06\"></p>\n<p>Vemos como funciona perfectamente.</p>\n<h3 id=\"comandosdockercompose\">Comandos docker-compose</h3>\n<p>Veamos algunos comandos de <strong>docker-compose</strong>:</p>\n<ul>\n<li><strong>docker-compose up</strong>: Funciona como <strong>docker run</strong>, busca el fichero <em><strong>docker-compose up</strong></em> y ejecuta lo que aparece en el.</li>\n<li><strong>docker-compose up --build</strong>: Fuerza a rehacer los contenedores antes de ejecutarlos. Muy útil si hemos realizado algún cambio en nuestro código de aplicación.</li>\n<li><strong>docker-compose up -d</strong>: Arranca los contenedores en segundo plano.</li>\n<li><strong>docker-compose down</strong>: Para los contenedores y elimina tanto los contenedores como la red que crea entre ellos.</li>\n<li><strong>docker-compose start</strong>: Arranca los contenedores si están parados (OJO parados que no eliminados)</li>\n<li><strong>docker-compose stop</strong>: Para los contenedores y <strong>NO</strong> elimina nada.</li>\n<li><strong>docker-compose ps</strong>: Al igual que docker ps, vemos el estado de nuestra <em><strong>composición</strong></em> de contenedores.</li>\n</ul>\n<p><strong>TODOS LOS COMANDOS DE DOCKER COMPOSE NECESITAN EL FICHERO DE docker-compose.yml PARA FUNCIONAR</strong></p>\n<h3 id=\"reinicioautomticodecontenedores\">Reinicio automático de contenedores</h3>\n<p>Puede pasar que alguno de nuestros contenedores se detenga, por algún error por ejemplo, en este caso docker ya está preparado para automáticamente reiniciar los contenedores según como lo configuremos en nuestro docker-compose.yml. Tenemos estas opciones de reinicio:</p>\n<ul>\n<li><strong>&quot;no&quot;</strong>: Es la configuración por defecto de los contenedores. No se reinician en ningún caso. Para poner explicitamente <em><strong>no</strong></em> es necesario ponerlo entre &quot;&quot;</li>\n<li><strong>always</strong>: Siempre que se pare, sea cual sea la razón, automáticamente se reinicia.</li>\n<li><strong>on-failure</strong>: Solo se reinicia cuando se pare con un <em><strong>error code</strong></em>. En general si el código de salida ha sido un 0, eso significa que se ha parado de manera controlada o porque ha terminado su ejecución correctamente, sin embargo, si el código con el termina es distinto de 0 entonces lo tomará como un error y reiniciará el contenedor.</li>\n<li><strong>unless-stopped</strong>: Siempre se reinicia a menos que lo hayamos parado nosotros manualmente.</li>\n</ul>\n<p>Para configurar estas opciones solo tenemos que indicarselo en el fichero de la siguiente forma:</p>\n<pre><code class=\"language-yaml\">version: '3'\nservices:\n  redis-server:\n    image: 'redis'\n  node-app:\n    restart: always &lt;--Con esta opción\n    build: .\n    ports:\n      - &quot;4001:8081&quot;\n</code></pre>\n<p>Esto último era un extra que nos puede ser útil a la hora de configurar nuestro contenedor. Nos vemos en el siguienteeee, un abrazooor.</p>\n<!--kg-card-end: markdown-->","url":"https://jlgarcia.fulldev.ninja/docker-iii-mas-complicado/","canonical_url":null,"uuid":"e8ad1b71-053f-48e1-9574-0b10e1a21d78","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"5be6b01a2dd6610fd828ca02","reading_time":4,"send_email_when_published":false,"email_subject":null,"childHtmlRehype":{"html":"<!--kg-card-begin: markdown--><p>Empecemos con cosas más complicadas, ahora vamos a preparar un entorno que se comunique entre sí y en el que podamos crear varios contenedores que se comuniquen entre con un solo comando.<br>\nPara poder hacer esto, empezaremos a ver algo que se llama <strong>docker-compose</strong>, esto son otra serie de comandos pertenecientes al entorno de Docker, en el que básicamente se busca un fichero <strong>docker-compose.yml</strong> con las instrucciones que se deben seguir.<br>\nLo primero, en nuestra carpeta <strong>visits</strong> donde hemos creado anteriormente nuestro <strong>dockerfile</strong> con nuestra aplicación en NodeJS, nos creamos el fichero <strong>docker-compose.yml</strong> y vayamos poco a poco</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'3'</span>\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n</code></pre></div>\n<ul>\n<li><strong>version</strong>: Hace referencia a la versión de la <em><strong>composición</strong></em>, esto le indica a docker, cuando ejecutamos comandos del CLI de docker-compose, si es versión más actual de <strong>docker-compose</strong> (a fecha de este texto es la 3).</li>\n<li><strong>services</strong>: Dentro del fichero, la propiedad services hace referencia a los contenedores, no a un servicios, dentro de este apartado indicaremos todos los contenedores que crearemos mediante esta <em><strong>composición</strong></em>.</li>\n</ul>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'3'</span>\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">redis-server</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'redis'</span>\n  <span class=\"token key atrule\">node-app</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span> .\n    <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token string\">\"4001:8081\"</span>\n</code></pre></div>\n<p>Dentro del apartado de services hemos indicado 2 contenedores: <strong>redis-server</strong> y <strong>node-app</strong>. Un detalle importante antes de continuar: <strong>TODOS LOS CONTENEDORES DENTRO DEL MISMO DOCKER-COMPOSE ESTÁN DENTRO DE LA MISMA RED</strong>, es decir, tienen comunicación entre ellos, y una forma de llamarse unos a otros es <strong>con el propio nombre que hemos indicado en el fichero de docker-compose.yml</strong>, por lo que si volvemos a nuestro fichero <em>index.js</em> donde tenemos nuestra aplicación de NodeJS, tenemos que hacer un pequeño cambio que antes habíamos ignorado, cuando creamos nuestro cliente de redis es necesario indicarle el host y el puerto del servidor que tiene el servicio de <strong>redis-server</strong> (siempre es necesario indicarlo menos cuando está en el mismo servidor que el cliente). Para indicarlo en este caso solo es necesario con:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> client <span class=\"token operator\">=</span> redis<span class=\"token punctuation\">.</span><span class=\"token function\">createClient</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    host<span class=\"token operator\">:</span> <span class=\"token string\">'redis-server'</span><span class=\"token punctuation\">,</span>\n    port<span class=\"token operator\">:</span> <span class=\"token number\">6379</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div>\n<p>Si nos fijamos hemos puesto el mismo nombre que en nuestro <strong>docker-compose</strong>, realmente lo que esta sucediendo es que cuando la petición llega a la <em>red interna de docker</em> el está reconociendo el nombre y sabe donde ir a buscarlo.<br>\nSabiendo esto y ya teniendo el cambio continuemos con nuestro <strong>docker-compose.yml</strong>, teniamos esto:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'3'</span>\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">redis-server</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'redis'</span>\n  <span class=\"token key atrule\">node-app</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span> .\n    <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token string\">\"4001:8081\"</span>\n</code></pre></div>\n<p>Siguiendo con lo que nos faltaba:</p>\n<ul>\n<li><strong>image</strong>: Básicamente le estamos indicando que ese contenedor use la imagen de contenedor con nombre <em>redis</em> (ya sea nuestra o de la <em>docker store</em>)</li>\n<li><strong>build</strong>: Le indicamos que inicie el proceso de <strong>build</strong> con el fichero <strong>dockerfile</strong> que se encuentra en la ruta indicada.</li>\n<li><strong>ports:</strong> Puertos a <em>mapear</em> en este contenedor de nuestro equipo local.</li>\n</ul>\n<p>Con esto ya tendríamos todo listo para que nuestro conjunto de contenedores funcionaran, vamos a probarlos. Para ello basta con ejecutar:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker-compose up\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-12-a-las-9.13.39.png\" alt=\"Captura-de-pantalla-2018-11-12-a-las-9.13.39\"></p>\n<p>Como vemos tenemos los dos contenedores levantados con los nombres que le hemos indicado en el fichero <strong>docker-compose.yml</strong>. Si ahora accedemos a <strong>localhost:4001</strong> (recordad que hemos cambiado el puerto al hacer el mapeo)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-12-a-las-9.17.06.png\" alt=\"Captura-de-pantalla-2018-11-12-a-las-9.17.06\"></p>\n<p>Vemos como funciona perfectamente.</p>\n<h3 id=\"comandosdockercompose\">Comandos docker-compose</h3>\n<p>Veamos algunos comandos de <strong>docker-compose</strong>:</p>\n<ul>\n<li><strong>docker-compose up</strong>: Funciona como <strong>docker run</strong>, busca el fichero <em><strong>docker-compose up</strong></em> y ejecuta lo que aparece en el.</li>\n<li><strong>docker-compose up --build</strong>: Fuerza a rehacer los contenedores antes de ejecutarlos. Muy útil si hemos realizado algún cambio en nuestro código de aplicación.</li>\n<li><strong>docker-compose up -d</strong>: Arranca los contenedores en segundo plano.</li>\n<li><strong>docker-compose down</strong>: Para los contenedores y elimina tanto los contenedores como la red que crea entre ellos.</li>\n<li><strong>docker-compose start</strong>: Arranca los contenedores si están parados (OJO parados que no eliminados)</li>\n<li><strong>docker-compose stop</strong>: Para los contenedores y <strong>NO</strong> elimina nada.</li>\n<li><strong>docker-compose ps</strong>: Al igual que docker ps, vemos el estado de nuestra <em><strong>composición</strong></em> de contenedores.</li>\n</ul>\n<p><strong>TODOS LOS COMANDOS DE DOCKER COMPOSE NECESITAN EL FICHERO DE docker-compose.yml PARA FUNCIONAR</strong></p>\n<h3 id=\"reinicioautomticodecontenedores\">Reinicio automático de contenedores</h3>\n<p>Puede pasar que alguno de nuestros contenedores se detenga, por algún error por ejemplo, en este caso docker ya está preparado para automáticamente reiniciar los contenedores según como lo configuremos en nuestro docker-compose.yml. Tenemos estas opciones de reinicio:</p>\n<ul>\n<li><strong>\"no\"</strong>: Es la configuración por defecto de los contenedores. No se reinician en ningún caso. Para poner explicitamente <em><strong>no</strong></em> es necesario ponerlo entre \"\"</li>\n<li><strong>always</strong>: Siempre que se pare, sea cual sea la razón, automáticamente se reinicia.</li>\n<li><strong>on-failure</strong>: Solo se reinicia cuando se pare con un <em><strong>error code</strong></em>. En general si el código de salida ha sido un 0, eso significa que se ha parado de manera controlada o porque ha terminado su ejecución correctamente, sin embargo, si el código con el termina es distinto de 0 entonces lo tomará como un error y reiniciará el contenedor.</li>\n<li><strong>unless-stopped</strong>: Siempre se reinicia a menos que lo hayamos parado nosotros manualmente.</li>\n</ul>\n<p>Para configurar estas opciones solo tenemos que indicarselo en el fichero de la siguiente forma:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'3'</span>\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">redis-server</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'redis'</span>\n  <span class=\"token key atrule\">node-app</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">restart</span><span class=\"token punctuation\">:</span> always &#x3C;<span class=\"token punctuation\">-</span><span class=\"token punctuation\">-</span>Con esta opción\n    <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span> .\n    <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token string\">\"4001:8081\"</span>\n</code></pre></div>\n<p>Esto último era un extra que nos puede ser útil a la hora de configurar nuestro contenedor. Nos vemos en el siguienteeee, un abrazooor.</p>\n<!--kg-card-end: markdown-->","htmlAst":{"type":"root","children":[{"type":"comment","value":"kg-card-begin: markdown"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Empecemos con cosas más complicadas, ahora vamos a preparar un entorno que se comunique entre sí y en el que podamos crear varios contenedores que se comuniquen entre con un solo comando."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nPara poder hacer esto, empezaremos a ver algo que se llama "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":", esto son otra serie de comandos pertenecientes al entorno de Docker, en el que básicamente se busca un fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose.yml"}]},{"type":"text","value":" con las instrucciones que se deben seguir."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nLo primero, en nuestra carpeta "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"visits"}]},{"type":"text","value":" donde hemos creado anteriormente nuestro "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" con nuestra aplicación en NodeJS, nos creamos el fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose.yml"}]},{"type":"text","value":" y vayamos poco a poco"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"version"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'3'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"services"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"version"}]},{"type":"text","value":": Hace referencia a la versión de la "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"composición"}]}]},{"type":"text","value":", esto le indica a docker, cuando ejecutamos comandos del CLI de docker-compose, si es versión más actual de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":" (a fecha de este texto es la 3)."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"services"}]},{"type":"text","value":": Dentro del fichero, la propiedad services hace referencia a los contenedores, no a un servicios, dentro de este apartado indicaremos todos los contenedores que crearemos mediante esta "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"composición"}]}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"version"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'3'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"services"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"redis-server"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"image"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'redis'"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"node-app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"build"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" .\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"4001:8081\""}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Dentro del apartado de services hemos indicado 2 contenedores: "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"redis-server"}]},{"type":"text","value":" y "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"node-app"}]},{"type":"text","value":". Un detalle importante antes de continuar: "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"TODOS LOS CONTENEDORES DENTRO DEL MISMO DOCKER-COMPOSE ESTÁN DENTRO DE LA MISMA RED"}]},{"type":"text","value":", es decir, tienen comunicación entre ellos, y una forma de llamarse unos a otros es "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"con el propio nombre que hemos indicado en el fichero de docker-compose.yml"}]},{"type":"text","value":", por lo que si volvemos a nuestro fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"index.js"}]},{"type":"text","value":" donde tenemos nuestra aplicación de NodeJS, tenemos que hacer un pequeño cambio que antes habíamos ignorado, cuando creamos nuestro cliente de redis es necesario indicarle el host y el puerto del servidor que tiene el servicio de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"redis-server"}]},{"type":"text","value":" (siempre es necesario indicarlo menos cuando está en el mismo servidor que el cliente). Para indicarlo en este caso solo es necesario con:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"javascript"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-javascript"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-javascript"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" client "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" redis"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"createClient"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    host"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'redis-server'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n    port"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"6379"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si nos fijamos hemos puesto el mismo nombre que en nuestro "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":", realmente lo que esta sucediendo es que cuando la petición llega a la "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"red interna de docker"}]},{"type":"text","value":" el está reconociendo el nombre y sabe donde ir a buscarlo."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nSabiendo esto y ya teniendo el cambio continuemos con nuestro "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose.yml"}]},{"type":"text","value":", teniamos esto:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"version"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'3'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"services"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"redis-server"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"image"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'redis'"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"node-app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"build"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" .\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"4001:8081\""}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Siguiendo con lo que nos faltaba:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"image"}]},{"type":"text","value":": Básicamente le estamos indicando que ese contenedor use la imagen de contenedor con nombre "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"redis"}]},{"type":"text","value":" (ya sea nuestra o de la "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker store"}]},{"type":"text","value":")"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"build"}]},{"type":"text","value":": Le indicamos que inicie el proceso de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"build"}]},{"type":"text","value":" con el fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" que se encuentra en la ruta indicada."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"ports:"}]},{"type":"text","value":" Puertos a "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"mapear"}]},{"type":"text","value":" en este contenedor de nuestro equipo local."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con esto ya tendríamos todo listo para que nuestro conjunto de contenedores funcionaran, vamos a probarlos. Para ello basta con ejecutar:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker-compose up\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-12-a-las-9.13.39.png","alt":"Captura-de-pantalla-2018-11-12-a-las-9.13.39"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como vemos tenemos los dos contenedores levantados con los nombres que le hemos indicado en el fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose.yml"}]},{"type":"text","value":". Si ahora accedemos a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"localhost:4001"}]},{"type":"text","value":" (recordad que hemos cambiado el puerto al hacer el mapeo)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-12-a-las-9.17.06.png","alt":"Captura-de-pantalla-2018-11-12-a-las-9.17.06"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Vemos como funciona perfectamente."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"comandosdockercompose"},"children":[{"type":"text","value":"Comandos docker-compose"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Veamos algunos comandos de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":":"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose up"}]},{"type":"text","value":": Funciona como "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker run"}]},{"type":"text","value":", busca el fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose up"}]}]},{"type":"text","value":" y ejecuta lo que aparece en el."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose up --build"}]},{"type":"text","value":": Fuerza a rehacer los contenedores antes de ejecutarlos. Muy útil si hemos realizado algún cambio en nuestro código de aplicación."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose up -d"}]},{"type":"text","value":": Arranca los contenedores en segundo plano."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose down"}]},{"type":"text","value":": Para los contenedores y elimina tanto los contenedores como la red que crea entre ellos."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose start"}]},{"type":"text","value":": Arranca los contenedores si están parados (OJO parados que no eliminados)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose stop"}]},{"type":"text","value":": Para los contenedores y "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"NO"}]},{"type":"text","value":" elimina nada."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose ps"}]},{"type":"text","value":": Al igual que docker ps, vemos el estado de nuestra "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"composición"}]}]},{"type":"text","value":" de contenedores."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"TODOS LOS COMANDOS DE DOCKER COMPOSE NECESITAN EL FICHERO DE docker-compose.yml PARA FUNCIONAR"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"reinicioautomticodecontenedores"},"children":[{"type":"text","value":"Reinicio automático de contenedores"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Puede pasar que alguno de nuestros contenedores se detenga, por algún error por ejemplo, en este caso docker ya está preparado para automáticamente reiniciar los contenedores según como lo configuremos en nuestro docker-compose.yml. Tenemos estas opciones de reinicio:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"\"no\""}]},{"type":"text","value":": Es la configuración por defecto de los contenedores. No se reinician en ningún caso. Para poner explicitamente "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"no"}]}]},{"type":"text","value":" es necesario ponerlo entre \"\""}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"always"}]},{"type":"text","value":": Siempre que se pare, sea cual sea la razón, automáticamente se reinicia."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"on-failure"}]},{"type":"text","value":": Solo se reinicia cuando se pare con un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"error code"}]}]},{"type":"text","value":". En general si el código de salida ha sido un 0, eso significa que se ha parado de manera controlada o porque ha terminado su ejecución correctamente, sin embargo, si el código con el termina es distinto de 0 entonces lo tomará como un error y reiniciará el contenedor."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"unless-stopped"}]},{"type":"text","value":": Siempre se reinicia a menos que lo hayamos parado nosotros manualmente."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Para configurar estas opciones solo tenemos que indicarselo en el fichero de la siguiente forma:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"version"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'3'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"services"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"redis-server"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"image"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'redis'"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"node-app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"restart"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" always <"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"Con esta opción\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"build"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" .\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"4001:8081\""}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto último era un extra que nos puede ser útil a la hora de configurar nuestro contenedor. Nos vemos en el siguienteeee, un abrazooor."}]},{"type":"text","value":"\n"},{"type":"comment","value":"kg-card-end: markdown"}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"comandosdockercompose","heading":"Comandos docker-compose"},{"id":"reinicioautomticodecontenedores","heading":"Reinicio automático de contenedores"}]},"featureImageSharp":{"base":"docker_facebook_share-2.png","publicURL":"/static/8250fb6cd246dd294ed3fc4d99218f71/docker_facebook_share-2.png","imageMeta":{"width":336,"height":287},"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADmElEQVQ4y21Ua0wUVxQ+s8PMvbvgo0YlaWP8UZvYGK2JsU39Y2IwNcZWiI+aqt2YIGjLuhRdtYDFBXlUBbQtLnQXV80uuCi0FNvaovFBUdEpxl9qamtTAR8FY+sfKbC3350dwFU3+XJ2zr3nO995zBA989M3N5GeczyGvJ9t+vYfbOo7LmKlBjHvL2Q/LshxQpDuCsfhuZ90sqKLpGdHSN9UH8PWFjOBluEfB7IqtuO00x58RLy6R+EV1214JlZwllj+KdIkcdZTxLr7KPEv/iAtvTr2vOXbMSCcpXu+m6Z/ciyVlXUKVnihOfGsoIlCEK+8QbZFUL7zfIL6roe09TWoqnGUkBWcI1bcQXr+KY1520j3NL/FdnUIVnJlkJUYT1jZrwIqg9zXTfqnJ19in52pSFhe4AAhqam5LyAEGdt1ycZ2thEue1lh+1VWfHnQJCw1BlgpCIsudiFZPdQv4ftvCfhCLK91hZYVcsjK4giRVdE+Oow+hKayogtdrOyqVPcfLzWGoA7oHDRJAQyrin95+z7Khuo2wzbfyW0pGYTWjBLyypsqr7ghSy/nB+4Iu6/7SWJ1dzTBdzfKfN1RHsMQ93X185p7Ancf6+7IQlJ1xRxIdkSJL7nEUHlhOybWuhaBQqn9t598vdG0pj+jkw/9HdX8fVF7oG/AHvxngFf9JXBvG1pEUzAgbIQi1cWXnN2gUJqX6L3cJEdJx63XAr+L5S09Q3MiD4aYvzdqr30o7IFegU0QWJWqpHZBSW1C0V11ilwvuToj+2guJ9bmdW+z+kpuIyW7gzMqf7pm9DzoE+XGQzG+Gor2/dbPyzov69u/f98R6jfj5CC0TH/8YmtYRgk41bE5ESKnn1burpdHygRPw1z34dNLZxe3LCZnzRQyBMmVsh98ZNM+PqJiulAYllAtKwlDcssVaeGYCDuPNh4hWuWjWd5vaNlXP9KkrceIbQq/reU0JWuZteZbZMbFYkYAYcqoIyu8CLYcSJeksE76MEAOd10Kc4VXIOnn8E3H2Zuwk4FVwKvAG0CmPMPK0TDzGtgNQBZw0rqwGvABeVbQXuASgtJg9wMBoA5olSIg6GXzXY7JDq2HU5KmAg1IsBZWKm7AmQc2Gff2WiRS2ddAEMgH9lgVSnHKcC9kUzPgXGe14IOYWrNHS6DKhSQpKFsDFlilbgRmArPhS9SGP2PPftfMFoz8Dz3VcDm8+CFYqqyY2Nn/8hrXL6If80cAAAAASUVORK5CYII=","aspectRatio":1.1711711711711712,"src":"/static/8250fb6cd246dd294ed3fc4d99218f71/5742b/docker_facebook_share-2.png","srcSet":"/static/8250fb6cd246dd294ed3fc4d99218f71/bcfcb/docker_facebook_share-2.png 260w,\n/static/8250fb6cd246dd294ed3fc4d99218f71/5742b/docker_facebook_share-2.png 336w","srcWebp":"/static/8250fb6cd246dd294ed3fc4d99218f71/8cfb4/docker_facebook_share-2.webp","srcSetWebp":"/static/8250fb6cd246dd294ed3fc4d99218f71/dc8f3/docker_facebook_share-2.webp 260w,\n/static/8250fb6cd246dd294ed3fc4d99218f71/8cfb4/docker_facebook_share-2.webp 336w","sizes":"(max-width: 336px) 100vw, 336px"}}}},"prev":{"id":"Ghost__Post__5be93e342dd6610fd828ca11","title":"Docker IV: Development Workflow","slug":"docker-iv-development-workflow","featured":false,"feature_image":"https://jlgarcia.fulldev.ninja/assets/images/2018/12/docker_facebook_share-3.png","excerpt":"La idea es ir empezando a ver como sería un entorno real de desarrollo con\nDocker teniendo:\n\n * Nuestro entorno de desarrollo: Un entorno donde creamos las cosas.\n * Entorno de test: Tras el desarrollo, pasamos nuestro código al entorno de\n   test donde, como su propio nombre indica, probamos que todo funciona.\n * Entorno de producción: Tras pasar los tests pertinentes automáticamente el\n   proyecto pasaría a producción.\n\nEste workflow es lo que comunmente se conoce como Integración Continua,\nCo","custom_excerpt":null,"visibility":"public","created_at_pretty":"12 Nov 2018","published_at_pretty":"1 Oct 2018","updated_at_pretty":"19 Jan 2021","created_at":"2018-11-12T09:47:48.000+01:00","published_at":"2018-10-01T12:33:00.000+02:00","updated_at":"2021-01-19T20:50:35.000+01:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"Perfil.jpg","publicURL":"/static/b0de6281fb28a266510b3b09b9243e5a/Perfil.jpg","imageMeta":{"width":307,"height":307},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAUDBAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGzw6zC6zHn+cLYP//EAB0QAAICAQUAAAAAAAAAAAAAAAEDAAIEEyEiIzL/2gAIAQEAAQUCifca8KgcKWVfUpkHsG5pxX//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAdEAACAgEFAAAAAAAAAAAAAAAAARARcQISIUFR/9oACAEBAAY/AhU88xkb7N06a8P/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUFRYXH/2gAIAQEAAT8hR2pq40aqb+xIAeXibhW9JXr8joF4TBcSNe0//9oADAMBAAIAAwAAABDzDwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFhcfD/2gAIAQEAAT8QyItrELaTlatLwU63MvEW6vUNdy4LZQDn7iVApV9VLtANdWwKkuYq4Er1VZ//2Q==","aspectRatio":1,"src":"/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg","srcSet":"/static/b0de6281fb28a266510b3b09b9243e5a/f340b/Perfil.jpg 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/22d64/Perfil.jpg 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/aa249/Perfil.jpg 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/0dc33/Perfil.jpg 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/60667/Perfil.jpg 307w","srcWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp","srcSetWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/59cda/Perfil.webp 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/7da75/Perfil.webp 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f282e/Perfil.webp 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/a7b21/Perfil.webp 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f59af/Perfil.webp 307w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},"tags":[{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null}],"plaintext":"La idea es ir empezando a ver como sería un entorno real de desarrollo con\nDocker teniendo:\n\n * Nuestro entorno de desarrollo: Un entorno donde creamos las cosas.\n * Entorno de test: Tras el desarrollo, pasamos nuestro código al entorno de\n   test donde, como su propio nombre indica, probamos que todo funciona.\n * Entorno de producción: Tras pasar los tests pertinentes automáticamente el\n   proyecto pasaría a producción.\n\nEste workflow es lo que comunmente se conoce como Integración Continua,\nContinuous Integration o CI. Para este proceso usaremos herramientas o servicios\ncomo Github o Travis CI y desarrollaremos con ReactJS (no importa que no sepamos\nusarlos veremos lo necesario para entender lo que estamos haciendo).\n\nEntorno de desarrollo\nEmpecemos por el primero de los entornos, el entorno de desarrollo. Lo que\nbuscamos en este entorno es que nuestros cambios mientras estamos desarrollando\nse sincronicen automáticamente con el contenedor de desarrollo (esto en varios\ncasos es innecesario pero lo veremos igualmente con los fines didácticos que nos\nocupan).\nPensando en el objetivo de este entorno, ya hemos visto que si realizamos\ncambios en nuestro código tenemos que volver a realizar un build y luego\narrancar un contenedor nuevo con la imagen que nos crearía el build anterior.\nEsto realmente no es lo que estamos buscando, es poco eficiente y tendríamos\ndemasiadas imágenes de contenedor.\n\nEn docker existe una forma de solucionar esto, vamos a introducir algo que no\nhemos visto todavia: Los Volúmenes.\nEn docker un volumen no deja de ser una referencia al filesystem del contenedor\ny puede ser una referencia, como un mapeo (al igual que los puertos) de una\ncarpeta local a una del contenedor, o un 'no lo toques' (que básicamente es\nmapea todo menos esto). Lo vamos a probar directamente, primero de todo vamos\npreparar un proyecto para trabajar con el, empecemos por crearnos un proyecto de\nReactJS.\n\nPara trabajar con react usaremos un paquete de npm que nos instala un proyecto\nbásico inicial, lo instalamos con\n\nnpm -g install create-react-app\n\n\nUna vez instalado ya podríamos crearnos un proyecto\n\ncreate-react-app frontWeb\n\n\nEsto nos creará un proyecto de react dentro de una carpeta llamada frontWeb.\nPara probar si funciona solo tenemos que entrar en la carpeta y ejecutar con:\n\nnpm start\n\n\n\n\nBien ya tenemos nuestra web de react, vamos ahora con la parte del entorno de \ndesarrollo de docker.\nPara ejecutar nuestro entorno de desarrollo en un contenedor solo necesitamos\nque tenga node instalado, es decir, que es similar a lo que hemos creado\nanteriormente.\n\nAntes de continuar comentar que aunque vamos a trabajar casi todo el rato con\ncomandos de docker-compose todo lo que hagamos a partir de ahora se puede hacer\ncon comandos docker run también, pero realmente son comandos muy largos y poco\nútiles a la larga. En caso de necesidad siempre podemos buscar cual es el flag\ndel comando para ponerlo directamente sin usar un dockerfile.\n\nContinuemos, volvamos al principio, hemos dicho que queríamos un entorno de\ndesarrollo donde nuestro contenedor se actualice automáticamente según vayamos\nhaciendo cambios en local, para ello vamos a hacer uso de lo que en docker se\nconoce como volumenes.\nLo primero nos crearemos dentro de nuestra carpeta de proyecto de react un\nfichero dockerfile pero esta vez pondremos:\n\nDockerfile.dev\n\n\nComo es normal aunque no lo hayamos visto a docker se le puede indicar el\nfichero dockerfile a usar cuando hacemos un build, solo tenemos que usar el flag \n-f, por ejemplo (ojo al punto del final)\n\ndocker build -f ./Dockerfile.dev .\n\n\nLo mismo con los ficheros para docker-compose, por lo que realmente no tendremos\nningún problema y podemos tener varios dockerfile distintos según nuestro\nentorno.\nSabiendo esto continuamos con nuestro fichero Dockerfile.dev\n\nFROM node:alpine\n\nWORKDIR '/app'\n\nCOPY package.json .\nRUN npm install\n\nCOPY . .\n\nCMD [\"npm\", \"start\"]\n\n\nMisma teoria que anteriormente, copiamos el package.json primero por posibles\ncambios solo del resto y no tener que hacer otra vez el npm install todo el rato\ncada vez que hagamos un build.\nYa tenemos nuestro fichero dockerfile, ahora como la idea es usar docker-compose \npara todo necesitamos crear el fichero docker-compose.yml\n\nversion: '3'\nservices:\n    web_react:\n        build: \n            context: .\n            dockerfile: Dockerfile.dev\n        ports:\n            - \"3000:3000\"\n\n\nAntes de continuar, vemos que ahora donde tenemos puesto build ahora hemos\nañadido 2 propiedades:\n\n * context: Indicamos el contexto desde(path) desde el que trabajara el build\n   del docker-compose.\n * dockerfile: Nombre del fichero dockerfile que queremos usar.\n\nContinuemos, ahora vamos a hablar del concepto de Volume (por fin ;) ), primero\nañadamoslo al fichero\n\nversion: '3'\nservices:\n    web_react:\n        build: ./Dockerfile.dev\n        ports:\n            - \"3000:3000\"\n        volumes:\n            - /app/node_modules\n            - .:/app\n\n\nSi nos fijamos en lo que hemos puesto, tenemos realmente dos conceptos distintos\ndentro de volumes:\n\n * .:/app: Hablemos primero del segundo, este es similar al concepto de mapear\n   puertos, básicamente le estamos indicando que mapee todo el contenido de la\n   ruta actual de mi equipo local, al path /app del contenedor, lo que\n   funcionaría similar a un acceso directo a los ficheros de la carpeta local de\n   nuestro equipo.\n * /app/node_modules: Si nos fijamos en esta línea no tenemos ':', eso es porque\n   aquí le estamos indicando que haga como un marcador de la carpeta \n   node_modules del contenedor, es decir, usa la del contenedor, no la toques y\n   dejala donde está (esto hace que la carpeta se mantenga aunque hagamos el\n   paso anterior).\n\nAhora que ya sabemos lo que hemos puesto, viene la pregunta del ¿por\nqué?....bien, si pensamos en el proceso que hemos añadido en el Dockerfile.dev,\ntenemos una parte donde instalamos los paquetes que están indicados en el \npackage.json, es decir, queremos que los descargues e instales de nuevo cuando\nhagamos un build de la imagen del contenedor, y no pasamos la carpeta local \nnode_modules (que es donde instala las dependencias), que de hecho la vamos a\neliminar para que veamos como funciona, si, eliminarla.\n\nYa que hablamos del Dockerfile.dev, alguno se puede preguntar si hacemos la\nrefencia o linkado de nuestra carpeta local, ¿para qué hacemos el\nCOPY?....bueno, esto es para prevenir creaciones para producción usando los\nmismos Dockerfiles, realmente en nuestro caso actual no lo necesitamos pero para\nproducción siempre es mejor para evitar errores, ya que un contenedor en\nproducción NO DEBE hacer referencias a carpetas locales de ningún sitio solo\ntiene que tener sus propios ficheros.\n\nYa tenemos todo ahora nos situamos en la ruta donde tenemos el fichero \ndocker-compose.yml y ejecutamos:\n\ndocker-compose up\n\n\nSi todo va bien deberíamos ver algo como esto en la consola\n\n\nY si accedemos en el navegador a:\n\nlocalhost:3000\n\n\nDeberíamos ver\n\n\n\nBien como tal ya tenemos todo funcionando, pero realmente lo que queremos es\npoder desarrollar en local y que se actualice el contenedor , ¿no?... pues vamos\na probarlo.\nNos vamos a nuestra web de react, y dentro de la carpeta src modificamos el\nfichero App.js y ponemos lo que queramos:\n\n <div className=\"App\">\n        <header className=\"App-header\">\n          <img src={logo} className=\"App-logo\" alt=\"logo\" />\n          <p>\n            Hola ninjaaaaasssss\n          </p> //<-- Esta es la linea que cambiamos\n          <a\n            className=\"App-link\"\n            href=\"https://reactjs.org\"\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n          >\n            Learn React\n          </a>\n        </header>\n      </div>\n\n\nY una vez que guardemos el documento automáticamente se debería actualizar el\nnavegador y mostrar\n\n\n\n¡¡¡PERFECTO!!!... ya tenemos nuestro entorno de desarrollo funcionando.\n\nEmpecemos con el entorno de TEST\n\nTEST\nPor defecto react viene ya configurado con 1 test para poder probar, solo\ntenemos que hacer\n\nnpm run test\n\n\no\n\nnpm test\n\n\ny automáticamente nos pasaría los tests\n\n\n\nBien, pues ahora queremos esto pero en un contenedor, como opción rápida podemos\nejecutar una imagen creada con el mismo dockerfile pero cambiando el comando de\narranque (esto es solo para que veais que funciona).\n\nPor si no la tenemos creamos el build de la imagen, yo la voy a taggear para\npoder identificarla\n\ndocker build -f ./Dockerfile.dev . -t test_react\n\n\nEsto nos devuelve una imagen con ese nombre\n\n\n\nYa la tenemos, ahora solo vamos a realizar un run habitual cambiando el comando\nde arranque\n\ndocker run -it test_react npm test\n\n\nLe he añadido -it para poder trabajar con la consola de test y como algo nuevo\nsi nos fijamos hemos puesto cosas tras el nombre de la imagen, básicamente todo\nlo que ponemos a continuación del nombre de la imagen a ejecutar lo toma como\ncomando de arranque para el contenedor. Si lo ejecutamos nos devuelve\n\n\nOtra opción es añadirlo a nuestro docker-compose añadiendo otro servicio pero\ncon la misma teoría, compartiendo o mapeando ficheros entre el equipo local y el\ncontenedor, teniendo 2 contenedores funcionando uno para las pruebas en\ndesarrollo y otro probando los test. El problema de este acercamiento es que no\ntenemos control sobre la consola de test por lo que no podemos hacer mucho más\nque ver como pasan los tests cada vez que hacemos un cambio en los ficheros.\nVamos a probarlo, cambiamos nuestro fichero docker-compose\n\nversion: '3'\nservices:\n    web_react:\n        build:\n            context: .\n            dockerfile: Dockerfile.dev\n        ports:\n            - \"3000:3000\"\n        volumes:\n            - /app/node_modules\n            - .:/app\n    test_react:\n        build:\n            context: .\n            dockerfile: Dockerfile.dev\n        volumes:\n            - /app/node_modules\n            - .:/app\n        command: [\"npm\",\"test\"]\n\n\nComo véis hemos añadido otro servicio (contenedor ya sabéis), en este caso se\nllama test_react, que usa el mismo dockerfile y mapea de la misma forma los \nvolumenes. Despues de eso si que tiene cambios, hemos quitado el mapeo del\npuerto porque ya no lo necesitamos y como extra nuevo hemos añadido la propiedad \ncommand que básicamente lo que hace es cambiar el comando de inicio del\ncontenedor.\nA continuación si ejecutamos nuestro docker-compose up, nos crea dos\ncontenedores y como podremos ver el log ambos funcionan correctamente, y si\ncambiamos algo en los ficheros se actualizan ambos, tanto el de desarrollo como\nel que pasa los tests.\n\n\n\nNinguno de los casos es muy ideal pero son funcionales y puede que en algún caso\nnos pueda servir para algo, más adelante veremos un entorno de test más 'real'\npor el momento esto es más que suficiente, a continuación empezaremos a hablar\nun poco de PRODUCCIÓN\n\nPRODUCCIÓN\nPasemos ahora a producción, la intención es crear un contenedor que nos devuelva\nnuestra aplicación ya preparada para producción, para el que no lo sepa, una app\nde react la preparamos para producción ejecutando el comando:\n\nnpm run build\n\n\nY este comando nos deja unos ficheros típicos de web (html, js y css), es decir, \nficheros estáticos. Estos ficheros los deja en una carpeta llamada build dentro\nde nuestro proyecto.\n\nAhora necesitamos para producción un servidor web, los más utilizados son Apache\no Nginx, aunque podríamos hacerlo con NodeJS, Go, Ruby, etc..... con casi todos\nlos lenguajes tenemos alguna opción para hacerlo. En nuestro caso usaremos un\ncontenedor con Nginx.\n\nSi miramos la documentación del contenedor oficial\n[https://hub.docker.com/_/nginx/] podemos ver que los ficheros los sirve desde\nel path /usr/share/nginx/html. Sabiendo esto entonces básicamente lo que\ntendriamos que hacer sería copiar nuestros ficheros de producción en esa ruta\ndel contenedor de nginx... pero claro se supone que no tenemos en local los\narchivos, veamos como podemos hacerlo con el entorno que tenemos ahora.\n\nNos vamos a crear un nuevo dockerfile con esto\n\nFROM node:alpine as builder\nWORKDIR '/app'\nCOPY package.json .\nRUN npm install\nCOPY . .\nRUN npm run build\n\nFROM nginx\nCOPY --from=builder /app/build /usr/share/nginx/html\n\n\nEmpecemos por la primera linea:\n\nFROM node:alpine as builder\n\n\nIntroducimos algo nuevo en este punto, básicamente lo que estamos haciendo es\nindicarle al proceso que añada como una referencia al resultado del build de ese\ncontenedor con nombre builder, pero puede ser cualquier otro. Esta referencia\nsolo está disponible en el contexto de la ejecución de docker build.\nEl resto del primer contenedor es algo que ya hemos visto, vayamos con el\nsegundo\n\nFROM nginx\nCOPY --from=builder /app/build /usr/share/nginx/html\n\n\nAquí empezamos con el segundo contenedor. Vemos como la instrucción COPY tiene\nalgo nuevo\n\n--from=builder\n\n\nComo os podéis imaginar tiene que ver con la instrucción as builder del primer\ncontenedor, aquí le estamos diciendo que del primer contenedor se copie la ruta \n/app/build y la pegue en /usr/share/nginx/html\nBien pues vamos a ejecutar nuestro nuevo build\n\ndocker build .\n\n\nUna vez terminado\n\n\n\nTenemos ya construido una imagen con supuestamente nginx y nuestra app en\nproducción. Por último nos faltaría crear un contenedor con esa imagen, pues\nvamos a ello\n\ndocker run -p 3500:80 --name webpro idImagen\n\n\nComo extra he añadido --name que lo que hace es taggearnos el contenedor con un\nnombre que podamos gestionar de manera más comoda que un ID numérico\n\n\n\nSe puede ver al principio el comando y a continuación un log (el de nginx) una\nvez que intentamos acceder a la página\n\n\n\nY como podemos ver ya tenemos nuestro entorno para producción que básicamente es\nel build del dockerfile una vez que hemos terminado de desarrollar.\n\nEn próximos posts veremos una forma más profesional de hacer todo esto con \nIntegración Continua gracias a TravisCI y GitHub.","html":"<!--kg-card-begin: markdown--><p>La idea es ir empezando a ver como sería un entorno real de desarrollo con Docker teniendo:</p>\n<ul>\n<li><strong>Nuestro entorno de desarrollo</strong>: Un entorno donde <em><strong>creamos</strong></em> las cosas.</li>\n<li><strong>Entorno de test</strong>: Tras el desarrollo, pasamos nuestro código al entorno de test donde, como su propio nombre indica, probamos que todo funciona.</li>\n<li><strong>Entorno de producción</strong>: Tras pasar los tests pertinentes automáticamente el proyecto pasaría a producción.</li>\n</ul>\n<p>Este <em>workflow</em> es lo que comunmente se conoce como <em>Integración Continua, Continuous Integration o CI</em>. Para este proceso usaremos herramientas o servicios como Github o Travis CI y desarrollaremos con ReactJS (no importa que no sepamos usarlos veremos lo necesario para entender lo que estamos haciendo).</p>\n<h3 id=\"entornodedesarrollo\">Entorno de desarrollo</h3>\n<p>Empecemos por el primero de los entornos, el entorno de desarrollo. Lo que buscamos en este entorno es que nuestros cambios mientras estamos desarrollando se <em>sincronicen</em> automáticamente con el contenedor de desarrollo  (esto en varios casos es innecesario pero lo veremos igualmente con los fines didácticos que nos ocupan).<br>\nPensando en el objetivo de este entorno, ya hemos visto que si realizamos cambios en nuestro código tenemos que volver a realizar un <em><strong>build</strong></em> y luego arrancar un contenedor nuevo con la imagen que nos crearía el build anterior. Esto realmente no es lo que estamos buscando, es poco eficiente y tendríamos demasiadas imágenes de contenedor.</p>\n<p>En docker existe una forma de solucionar esto, vamos a introducir algo que no hemos visto todavia: <strong>Los Volúmenes</strong>.<br>\nEn docker un <em>volumen</em> no deja de ser una referencia al filesystem del contenedor y puede ser una referencia, como un <em>mapeo</em> (al igual que los puertos) de una carpeta local a una del contenedor, o un 'no lo toques' (que básicamente es mapea todo menos esto). Lo vamos a probar directamente, primero de todo vamos preparar un proyecto para trabajar con el, empecemos por crearnos un proyecto de ReactJS.</p>\n<p>Para trabajar con react usaremos un paquete de npm que nos instala un proyecto básico inicial, lo instalamos con</p>\n<pre><code class=\"language-shell\">npm -g install create-react-app\n</code></pre>\n<p>Una vez instalado ya podríamos crearnos un proyecto</p>\n<pre><code>create-react-app frontWeb\n</code></pre>\n<p>Esto nos creará un proyecto de react dentro de una carpeta llamada frontWeb. Para probar si funciona solo tenemos que entrar en la carpeta y ejecutar con:</p>\n<pre><code class=\"language-shell\">npm start\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture.JPG\" alt=\"Capture\"></p>\n<p>Bien ya tenemos nuestra web de react, vamos ahora con la parte del entorno de <em>desarrollo</em> de docker.<br>\nPara ejecutar nuestro entorno de desarrollo en un contenedor solo necesitamos que tenga <strong>node</strong> instalado, es decir, que es similar a lo que hemos creado anteriormente.</p>\n<p>Antes de continuar comentar que aunque vamos a trabajar casi todo el rato con comandos de <strong>docker-compose</strong> todo lo que hagamos a partir de ahora se puede hacer con comandos <strong>docker run</strong> también, pero realmente son comandos muy largos y poco útiles a la larga. En caso de necesidad siempre podemos buscar cual es el flag del comando para ponerlo directamente sin usar un <strong>dockerfile</strong>.</p>\n<p>Continuemos, volvamos al principio, hemos dicho que queríamos un entorno de desarrollo donde nuestro contenedor se actualice automáticamente según vayamos haciendo cambios en local, para ello vamos a hacer uso de lo que en docker se conoce como <em>volumenes</em>.<br>\nLo primero nos crearemos dentro de nuestra carpeta de proyecto de react un fichero <em>dockerfile</em> pero esta vez pondremos:</p>\n<pre><code class=\"language-shell\">Dockerfile.dev\n</code></pre>\n<p>Como es normal aunque no lo hayamos visto a docker se le puede indicar el fichero <em>dockerfile</em> a usar cuando hacemos un build, solo tenemos que usar el flag <strong>-f</strong>,  por ejemplo (ojo al punto del final)</p>\n<pre><code class=\"language-shell\">docker build -f ./Dockerfile.dev .\n</code></pre>\n<p>Lo mismo con los ficheros para <strong>docker-compose</strong>, por lo que realmente no tendremos ningún problema y podemos tener varios <em>dockerfile</em> distintos según nuestro entorno.<br>\nSabiendo esto continuamos con nuestro fichero <strong>Dockerfile.dev</strong></p>\n<pre><code class=\"language-docker\">FROM node:alpine\n\nWORKDIR '/app'\n\nCOPY package.json .\nRUN npm install\n\nCOPY . .\n\nCMD [&quot;npm&quot;, &quot;start&quot;]\n</code></pre>\n<p>Misma teoria que anteriormente, copiamos el package.json primero por posibles cambios solo del resto y no tener que hacer otra vez el <em>npm install</em> todo el rato cada vez que hagamos un build.<br>\nYa tenemos nuestro fichero <em>dockerfile</em>, ahora como la idea es usar <em>docker-compose</em> para todo necesitamos crear el fichero docker-compose.yml</p>\n<pre><code class=\"language-yaml\">version: '3'\nservices:\n    web_react:\n        build: \n            context: .\n            dockerfile: Dockerfile.dev\n        ports:\n            - &quot;3000:3000&quot;\n</code></pre>\n<p>Antes de continuar, vemos que ahora donde tenemos puesto <em>build</em> ahora hemos añadido 2 propiedades:</p>\n<ul>\n<li><strong>context</strong>: Indicamos el contexto desde(path) desde el que trabajara el build del docker-compose.</li>\n<li><strong>dockerfile</strong>: Nombre del fichero dockerfile que queremos usar.</li>\n</ul>\n<p>Continuemos, ahora vamos a hablar del concepto de <strong>Volume</strong> (por fin ;) ), primero añadamoslo al fichero</p>\n<pre><code class=\"language-yaml\">version: '3'\nservices:\n    web_react:\n        build: ./Dockerfile.dev\n        ports:\n            - &quot;3000:3000&quot;\n        volumes:\n            - /app/node_modules\n            - .:/app\n</code></pre>\n<p>Si nos fijamos en lo que hemos puesto, tenemos realmente dos conceptos distintos dentro de <em>volumes</em>:</p>\n<ul>\n<li><strong>.:/app</strong>: Hablemos primero del segundo, este es similar al concepto de mapear puertos, básicamente le estamos indicando que <em>mapee</em> todo el contenido de la ruta actual de mi equipo local, al path <em>/app</em> del contenedor, lo que funcionaría similar a un acceso directo a los ficheros de la carpeta local de nuestro equipo.</li>\n<li><strong>/app/node_modules</strong>: Si nos fijamos en esta línea no tenemos <strong>':'</strong>, eso es porque aquí le estamos indicando que haga como un <em>marcador</em> de la carpeta <em>node_modules</em> del contenedor, es decir, usa la del contenedor, no la toques y dejala donde está (esto hace que la carpeta se mantenga aunque hagamos el paso anterior).</li>\n</ul>\n<p>Ahora que ya sabemos lo que hemos puesto, viene la pregunta del ¿por qué?....bien, si pensamos en el proceso que hemos añadido en el <strong>Dockerfile.dev</strong>, tenemos una parte donde instalamos los paquetes que están indicados en el <em>package.json</em>, es decir, queremos que los descargues e instales de nuevo cuando hagamos un build de la imagen del contenedor, y no pasamos la carpeta local <em>node_modules</em> (que es donde instala las dependencias), que de hecho la vamos a eliminar para que veamos como funciona, si, <strong>eliminarla</strong>.</p>\n<p>Ya que hablamos del <strong>Dockerfile.dev</strong>, alguno se puede preguntar si hacemos la refencia o linkado de nuestra carpeta local, ¿para qué hacemos el COPY?....bueno, esto es para prevenir creaciones para producción usando los mismos Dockerfiles, realmente en nuestro caso actual no lo necesitamos pero para producción siempre es mejor para evitar errores, ya que un contenedor en producción <strong>NO DEBE</strong> hacer referencias a carpetas locales de <strong>ningún sitio</strong> solo tiene que tener sus propios ficheros.</p>\n<p>Ya tenemos todo ahora nos situamos en la ruta donde tenemos el fichero <em>docker-compose.yml</em> y ejecutamos:</p>\n<pre><code class=\"language-shell\">docker-compose up\n</code></pre>\n<p>Si todo va bien deberíamos ver algo como esto en la consola<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.35.40.png\" alt=\"Captura-de-pantalla-2018-11-14-a-las-9.35.40\"></p>\n<p>Y si accedemos en el navegador a:</p>\n<pre><code class=\"language-shell\">localhost:3000\n</code></pre>\n<p>Deberíamos ver</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.37.02.png\" alt=\"Captura-de-pantalla-2018-11-14-a-las-9.37.02\"></p>\n<p>Bien como tal ya tenemos todo funcionando, pero realmente lo que queremos es poder desarrollar en local y que se actualice el contenedor , ¿no?... pues vamos a probarlo.<br>\nNos vamos a nuestra web de react, y dentro de la carpeta <em>src</em> modificamos el fichero <em>App.js</em> y ponemos lo que queramos:</p>\n<pre><code class=\"language-html\"> &lt;div className=&quot;App&quot;&gt;\n        &lt;header className=&quot;App-header&quot;&gt;\n          &lt;img src={logo} className=&quot;App-logo&quot; alt=&quot;logo&quot; /&gt;\n          &lt;p&gt;\n            Hola ninjaaaaasssss\n          &lt;/p&gt; //&lt;-- Esta es la linea que cambiamos\n          &lt;a\n            className=&quot;App-link&quot;\n            href=&quot;https://reactjs.org&quot;\n            target=&quot;_blank&quot;\n            rel=&quot;noopener noreferrer&quot;\n          &gt;\n            Learn React\n          &lt;/a&gt;\n        &lt;/header&gt;\n      &lt;/div&gt;\n</code></pre>\n<p>Y una vez que guardemos el documento automáticamente se debería actualizar el navegador y mostrar</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.40.32.png\" alt=\"Captura-de-pantalla-2018-11-14-a-las-9.40.32\"></p>\n<p>¡¡¡PERFECTO!!!... ya tenemos nuestro entorno de desarrollo funcionando.</p>\n<p>Empecemos con el entorno de <strong>TEST</strong></p>\n<h4 id=\"test\">TEST</h4>\n<p>Por defecto react viene ya configurado con 1 test para poder probar, solo tenemos que hacer</p>\n<pre><code class=\"language-shell\">npm run test\n</code></pre>\n<p>o</p>\n<pre><code class=\"language-shell\">npm test\n</code></pre>\n<p>y automáticamente nos pasaría los tests</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-1.JPG\" alt=\"Capture-1\"></p>\n<p>Bien, pues ahora queremos esto pero en un contenedor, como opción rápida podemos ejecutar una imagen creada con el mismo <em>dockerfile</em> pero cambiando el comando de arranque (esto es solo para que veais que funciona).</p>\n<p>Por si no la tenemos creamos el build de la imagen, yo la voy a <em>taggear</em> para poder identificarla</p>\n<pre><code class=\"language-shell\">docker build -f ./Dockerfile.dev . -t test_react\n</code></pre>\n<p>Esto nos devuelve una imagen con ese nombre</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-2.JPG\" alt=\"Capture-2\"></p>\n<p>Ya la tenemos, ahora solo vamos a realizar un <em>run</em> habitual cambiando el comando de arranque</p>\n<pre><code class=\"language-shell\">docker run -it test_react npm test\n</code></pre>\n<p>Le he añadido <em>-it</em> para poder trabajar con la consola de test y como algo nuevo si nos fijamos hemos puesto <em>cosas</em> tras el nombre de la imagen, básicamente todo lo que ponemos a continuación del nombre de la imagen a ejecutar lo toma como comando de arranque para el contenedor. Si lo ejecutamos nos devuelve<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-3.JPG\" alt=\"Capture-3\"></p>\n<p>Otra opción es añadirlo a nuestro <em>docker-compose</em> añadiendo otro servicio pero con la misma teoría, compartiendo o <em>mapeando</em> ficheros entre el equipo local y el contenedor, teniendo 2 contenedores funcionando uno para las pruebas en desarrollo y otro probando los test. El problema de este acercamiento es que no tenemos control sobre la consola de test por lo que no podemos hacer mucho más que ver como pasan los tests cada vez que hacemos un cambio en los ficheros.<br>\nVamos a probarlo, cambiamos nuestro fichero <em>docker-compose</em></p>\n<pre><code class=\"language-yaml\">version: '3'\nservices:\n    web_react:\n        build:\n            context: .\n            dockerfile: Dockerfile.dev\n        ports:\n            - &quot;3000:3000&quot;\n        volumes:\n            - /app/node_modules\n            - .:/app\n    test_react:\n        build:\n            context: .\n            dockerfile: Dockerfile.dev\n        volumes:\n            - /app/node_modules\n            - .:/app\n        command: [&quot;npm&quot;,&quot;test&quot;]\n</code></pre>\n<p>Como véis hemos añadido otro servicio (contenedor ya sabéis), en este caso se llama <strong>test_react</strong>, que usa el mismo <em>dockerfile</em> y mapea de la misma forma los <em>volumenes</em>. Despues de eso si que tiene cambios, hemos quitado el <em>mapeo</em> del puerto porque ya no lo necesitamos y como extra nuevo hemos añadido la propiedad <strong>command</strong> que básicamente lo que hace es cambiar el comando de inicio del contenedor.<br>\nA continuación si ejecutamos nuestro <em>docker-compose up</em>, nos crea dos contenedores y como podremos ver el log ambos funcionan correctamente, y si cambiamos algo en los ficheros se actualizan ambos, tanto el de desarrollo como el que pasa los tests.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-4.JPG\" alt=\"Capture-4\"></p>\n<p>Ninguno de los casos es muy <strong>ideal</strong> pero son funcionales y puede que en algún caso nos pueda servir para algo, más adelante veremos un entorno de test más 'real' por el momento esto es más que suficiente, a continuación empezaremos a hablar un poco de <strong>PRODUCCIÓN</strong></p>\n<h4 id=\"produccin\">PRODUCCIÓN</h4>\n<p>Pasemos ahora a <em>producción</em>, la intención es crear un contenedor que nos devuelva nuestra aplicación ya preparada para producción, para el que no lo sepa, una app de react la preparamos para producción ejecutando el comando:</p>\n<pre><code class=\"language-shell\">npm run build\n</code></pre>\n<p>Y este comando nos deja unos ficheros típicos de web (html, js y css), es decir, <em>ficheros estáticos</em>. Estos ficheros los deja en una carpeta llamada <strong>build</strong> dentro de nuestro proyecto.</p>\n<p>Ahora necesitamos para producción un <em>servidor web</em>, los más utilizados son <strong>Apache o Nginx</strong>, aunque podríamos hacerlo con NodeJS, Go, Ruby, etc..... con casi todos los lenguajes tenemos alguna opción para hacerlo. En nuestro caso usaremos un contenedor con Nginx.</p>\n<p>Si miramos la documentación del <a href=\"https://hub.docker.com/_/nginx/\">contenedor oficial</a> podemos ver que los ficheros los sirve desde el path <strong>/usr/share/nginx/html</strong>. Sabiendo esto entonces básicamente lo que tendriamos que hacer sería copiar nuestros ficheros de producción en esa ruta del contenedor de nginx... pero claro se supone que no tenemos en local los archivos, veamos como podemos hacerlo con el entorno que tenemos ahora.</p>\n<p>Nos vamos a crear un nuevo <strong>dockerfile</strong> con esto</p>\n<pre><code class=\"language-docker\">FROM node:alpine as builder\nWORKDIR '/app'\nCOPY package.json .\nRUN npm install\nCOPY . .\nRUN npm run build\n\nFROM nginx\nCOPY --from=builder /app/build /usr/share/nginx/html\n</code></pre>\n<p>Empecemos por la primera linea:</p>\n<pre><code>FROM node:alpine as builder\n</code></pre>\n<p>Introducimos algo nuevo en este punto, básicamente lo que estamos haciendo es indicarle al proceso que añada como una referencia al resultado del <em>build</em> de ese contenedor con nombre <em>builder</em>, pero puede ser cualquier otro. Esta referencia solo está disponible en el contexto de la ejecución de <em>docker build</em>.<br>\nEl resto del primer contenedor es algo que ya hemos visto, vayamos con el segundo</p>\n<pre><code class=\"language-docker\">FROM nginx\nCOPY --from=builder /app/build /usr/share/nginx/html\n</code></pre>\n<p>Aquí empezamos con el segundo contenedor. Vemos como la instrucción COPY tiene algo nuevo</p>\n<pre><code class=\"language-docker\">--from=builder\n</code></pre>\n<p>Como os podéis imaginar tiene que ver con la instrucción <em>as builder</em> del primer contenedor, aquí le estamos diciendo que del primer contenedor se copie la ruta <em>/app/build</em> y la pegue en <em>/usr/share/nginx/html</em><br>\nBien pues vamos a ejecutar nuestro nuevo build</p>\n<pre><code class=\"language-shell\">docker build .\n</code></pre>\n<p>Una vez terminado</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-5.JPG\" alt=\"Capture-5\"></p>\n<p>Tenemos ya construido una imagen con supuestamente <strong>nginx</strong> y nuestra app en producción. Por último nos faltaría crear un contenedor con esa imagen, pues vamos a ello</p>\n<pre><code class=\"language-shell\">docker run -p 3500:80 --name webpro idImagen\n</code></pre>\n<p>Como extra he añadido <strong>--name</strong> que lo que hace es <em>taggearnos</em> el contenedor con un nombre que podamos gestionar de manera más comoda que un ID numérico</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-6.JPG\" alt=\"Capture-6\"></p>\n<p>Se puede ver al principio el comando y a continuación un log (el de nginx) una vez que intentamos acceder a la página</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture2.JPG\" alt=\"Capture2\"></p>\n<p>Y como podemos ver ya tenemos nuestro entorno para producción que básicamente es el build del <em>dockerfile</em> una vez que hemos terminado de desarrollar.</p>\n<p>En próximos posts veremos una forma más profesional de hacer todo esto con <em>Integración Continua</em> gracias a <strong>TravisCI</strong> y <strong>GitHub</strong>.</p>\n<!--kg-card-end: markdown-->","url":"https://jlgarcia.fulldev.ninja/docker-iv-development-workflow/","canonical_url":null,"uuid":"182bbd23-305b-4f6d-9eaa-fabfb8003c51","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"5be93e342dd6610fd828ca11","reading_time":9,"send_email_when_published":false,"email_subject":null,"childHtmlRehype":{"html":"<!--kg-card-begin: markdown--><p>La idea es ir empezando a ver como sería un entorno real de desarrollo con Docker teniendo:</p>\n<ul>\n<li><strong>Nuestro entorno de desarrollo</strong>: Un entorno donde <em><strong>creamos</strong></em> las cosas.</li>\n<li><strong>Entorno de test</strong>: Tras el desarrollo, pasamos nuestro código al entorno de test donde, como su propio nombre indica, probamos que todo funciona.</li>\n<li><strong>Entorno de producción</strong>: Tras pasar los tests pertinentes automáticamente el proyecto pasaría a producción.</li>\n</ul>\n<p>Este <em>workflow</em> es lo que comunmente se conoce como <em>Integración Continua, Continuous Integration o CI</em>. Para este proceso usaremos herramientas o servicios como Github o Travis CI y desarrollaremos con ReactJS (no importa que no sepamos usarlos veremos lo necesario para entender lo que estamos haciendo).</p>\n<h3 id=\"entornodedesarrollo\">Entorno de desarrollo</h3>\n<p>Empecemos por el primero de los entornos, el entorno de desarrollo. Lo que buscamos en este entorno es que nuestros cambios mientras estamos desarrollando se <em>sincronicen</em> automáticamente con el contenedor de desarrollo  (esto en varios casos es innecesario pero lo veremos igualmente con los fines didácticos que nos ocupan).<br>\nPensando en el objetivo de este entorno, ya hemos visto que si realizamos cambios en nuestro código tenemos que volver a realizar un <em><strong>build</strong></em> y luego arrancar un contenedor nuevo con la imagen que nos crearía el build anterior. Esto realmente no es lo que estamos buscando, es poco eficiente y tendríamos demasiadas imágenes de contenedor.</p>\n<p>En docker existe una forma de solucionar esto, vamos a introducir algo que no hemos visto todavia: <strong>Los Volúmenes</strong>.<br>\nEn docker un <em>volumen</em> no deja de ser una referencia al filesystem del contenedor y puede ser una referencia, como un <em>mapeo</em> (al igual que los puertos) de una carpeta local a una del contenedor, o un 'no lo toques' (que básicamente es mapea todo menos esto). Lo vamos a probar directamente, primero de todo vamos preparar un proyecto para trabajar con el, empecemos por crearnos un proyecto de ReactJS.</p>\n<p>Para trabajar con react usaremos un paquete de npm que nos instala un proyecto básico inicial, lo instalamos con</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token function\">npm</span> -g <span class=\"token function\">install</span> create-react-app\n</code></pre></div>\n<p>Una vez instalado ya podríamos crearnos un proyecto</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">create-react-app frontWeb\n</code></pre></div>\n<p>Esto nos creará un proyecto de react dentro de una carpeta llamada frontWeb. Para probar si funciona solo tenemos que entrar en la carpeta y ejecutar con:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token function\">npm</span> start\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture.JPG\" alt=\"Capture\"></p>\n<p>Bien ya tenemos nuestra web de react, vamos ahora con la parte del entorno de <em>desarrollo</em> de docker.<br>\nPara ejecutar nuestro entorno de desarrollo en un contenedor solo necesitamos que tenga <strong>node</strong> instalado, es decir, que es similar a lo que hemos creado anteriormente.</p>\n<p>Antes de continuar comentar que aunque vamos a trabajar casi todo el rato con comandos de <strong>docker-compose</strong> todo lo que hagamos a partir de ahora se puede hacer con comandos <strong>docker run</strong> también, pero realmente son comandos muy largos y poco útiles a la larga. En caso de necesidad siempre podemos buscar cual es el flag del comando para ponerlo directamente sin usar un <strong>dockerfile</strong>.</p>\n<p>Continuemos, volvamos al principio, hemos dicho que queríamos un entorno de desarrollo donde nuestro contenedor se actualice automáticamente según vayamos haciendo cambios en local, para ello vamos a hacer uso de lo que en docker se conoce como <em>volumenes</em>.<br>\nLo primero nos crearemos dentro de nuestra carpeta de proyecto de react un fichero <em>dockerfile</em> pero esta vez pondremos:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">Dockerfile.dev\n</code></pre></div>\n<p>Como es normal aunque no lo hayamos visto a docker se le puede indicar el fichero <em>dockerfile</em> a usar cuando hacemos un build, solo tenemos que usar el flag <strong>-f</strong>,  por ejemplo (ojo al punto del final)</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker build -f ./Dockerfile.dev <span class=\"token builtin class-name\">.</span>\n</code></pre></div>\n<p>Lo mismo con los ficheros para <strong>docker-compose</strong>, por lo que realmente no tendremos ningún problema y podemos tener varios <em>dockerfile</em> distintos según nuestro entorno.<br>\nSabiendo esto continuamos con nuestro fichero <strong>Dockerfile.dev</strong></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token instruction\"><span class=\"token keyword\">FROM</span> node:alpine</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> <span class=\"token string\">'/app'</span></span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> package.json .</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> npm install</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> . .</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">\"npm\"</span>, <span class=\"token string\">\"start\"</span>]</span>\n</code></pre></div>\n<p>Misma teoria que anteriormente, copiamos el package.json primero por posibles cambios solo del resto y no tener que hacer otra vez el <em>npm install</em> todo el rato cada vez que hagamos un build.<br>\nYa tenemos nuestro fichero <em>dockerfile</em>, ahora como la idea es usar <em>docker-compose</em> para todo necesitamos crear el fichero docker-compose.yml</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'3'</span>\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">web_react</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span> \n            <span class=\"token key atrule\">context</span><span class=\"token punctuation\">:</span> .\n            <span class=\"token key atrule\">dockerfile</span><span class=\"token punctuation\">:</span> Dockerfile.dev\n        <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> <span class=\"token string\">\"3000:3000\"</span>\n</code></pre></div>\n<p>Antes de continuar, vemos que ahora donde tenemos puesto <em>build</em> ahora hemos añadido 2 propiedades:</p>\n<ul>\n<li><strong>context</strong>: Indicamos el contexto desde(path) desde el que trabajara el build del docker-compose.</li>\n<li><strong>dockerfile</strong>: Nombre del fichero dockerfile que queremos usar.</li>\n</ul>\n<p>Continuemos, ahora vamos a hablar del concepto de <strong>Volume</strong> (por fin ;) ), primero añadamoslo al fichero</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'3'</span>\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">web_react</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span> ./Dockerfile.dev\n        <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> <span class=\"token string\">\"3000:3000\"</span>\n        <span class=\"token key atrule\">volumes</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> /app/node_modules\n            <span class=\"token punctuation\">-</span> .<span class=\"token punctuation\">:</span>/app\n</code></pre></div>\n<p>Si nos fijamos en lo que hemos puesto, tenemos realmente dos conceptos distintos dentro de <em>volumes</em>:</p>\n<ul>\n<li><strong>.:/app</strong>: Hablemos primero del segundo, este es similar al concepto de mapear puertos, básicamente le estamos indicando que <em>mapee</em> todo el contenido de la ruta actual de mi equipo local, al path <em>/app</em> del contenedor, lo que funcionaría similar a un acceso directo a los ficheros de la carpeta local de nuestro equipo.</li>\n<li><strong>/app/node_modules</strong>: Si nos fijamos en esta línea no tenemos <strong>':'</strong>, eso es porque aquí le estamos indicando que haga como un <em>marcador</em> de la carpeta <em>node_modules</em> del contenedor, es decir, usa la del contenedor, no la toques y dejala donde está (esto hace que la carpeta se mantenga aunque hagamos el paso anterior).</li>\n</ul>\n<p>Ahora que ya sabemos lo que hemos puesto, viene la pregunta del ¿por qué?....bien, si pensamos en el proceso que hemos añadido en el <strong>Dockerfile.dev</strong>, tenemos una parte donde instalamos los paquetes que están indicados en el <em>package.json</em>, es decir, queremos que los descargues e instales de nuevo cuando hagamos un build de la imagen del contenedor, y no pasamos la carpeta local <em>node_modules</em> (que es donde instala las dependencias), que de hecho la vamos a eliminar para que veamos como funciona, si, <strong>eliminarla</strong>.</p>\n<p>Ya que hablamos del <strong>Dockerfile.dev</strong>, alguno se puede preguntar si hacemos la refencia o linkado de nuestra carpeta local, ¿para qué hacemos el COPY?....bueno, esto es para prevenir creaciones para producción usando los mismos Dockerfiles, realmente en nuestro caso actual no lo necesitamos pero para producción siempre es mejor para evitar errores, ya que un contenedor en producción <strong>NO DEBE</strong> hacer referencias a carpetas locales de <strong>ningún sitio</strong> solo tiene que tener sus propios ficheros.</p>\n<p>Ya tenemos todo ahora nos situamos en la ruta donde tenemos el fichero <em>docker-compose.yml</em> y ejecutamos:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker-compose up\n</code></pre></div>\n<p>Si todo va bien deberíamos ver algo como esto en la consola<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.35.40.png\" alt=\"Captura-de-pantalla-2018-11-14-a-las-9.35.40\"></p>\n<p>Y si accedemos en el navegador a:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">localhost:3000\n</code></pre></div>\n<p>Deberíamos ver</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.37.02.png\" alt=\"Captura-de-pantalla-2018-11-14-a-las-9.37.02\"></p>\n<p>Bien como tal ya tenemos todo funcionando, pero realmente lo que queremos es poder desarrollar en local y que se actualice el contenedor , ¿no?... pues vamos a probarlo.<br>\nNos vamos a nuestra web de react, y dentro de la carpeta <em>src</em> modificamos el fichero <em>App.js</em> y ponemos lo que queramos:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"html\"><pre class=\"language-html\"><code class=\"language-html\"> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>App<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;</span>header</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>App-header<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;</span>img</span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span>{logo}</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>App-logo<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>logo<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;</span>p</span><span class=\"token punctuation\">></span></span>\n            Hola ninjaaaaasssss\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;/</span>p</span><span class=\"token punctuation\">></span></span> //<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;</span>--</span> <span class=\"token attr-name\">Esta</span> <span class=\"token attr-name\">es</span> <span class=\"token attr-name\">la</span> <span class=\"token attr-name\">linea</span> <span class=\"token attr-name\">que</span> <span class=\"token attr-name\">cambiamos</span>\n          <span class=\"token attr-name\">&#x3C;a</span>\n            <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>App-link<span class=\"token punctuation\">\"</span></span>\n            <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>https://reactjs.org<span class=\"token punctuation\">\"</span></span>\n            <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>_blank<span class=\"token punctuation\">\"</span></span>\n            <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>noopener noreferrer<span class=\"token punctuation\">\"</span></span>\n          <span class=\"token punctuation\">></span></span>\n            Learn React\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;/</span>a</span><span class=\"token punctuation\">></span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;/</span>header</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&#x3C;/</span>div</span><span class=\"token punctuation\">></span></span>\n</code></pre></div>\n<p>Y una vez que guardemos el documento automáticamente se debería actualizar el navegador y mostrar</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.40.32.png\" alt=\"Captura-de-pantalla-2018-11-14-a-las-9.40.32\"></p>\n<p>¡¡¡PERFECTO!!!... ya tenemos nuestro entorno de desarrollo funcionando.</p>\n<p>Empecemos con el entorno de <strong>TEST</strong></p>\n<h4 id=\"test\">TEST</h4>\n<p>Por defecto react viene ya configurado con 1 test para poder probar, solo tenemos que hacer</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token function\">npm</span> run <span class=\"token builtin class-name\">test</span>\n</code></pre></div>\n<p>o</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token function\">npm</span> <span class=\"token builtin class-name\">test</span>\n</code></pre></div>\n<p>y automáticamente nos pasaría los tests</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-1.JPG\" alt=\"Capture-1\"></p>\n<p>Bien, pues ahora queremos esto pero en un contenedor, como opción rápida podemos ejecutar una imagen creada con el mismo <em>dockerfile</em> pero cambiando el comando de arranque (esto es solo para que veais que funciona).</p>\n<p>Por si no la tenemos creamos el build de la imagen, yo la voy a <em>taggear</em> para poder identificarla</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker build -f ./Dockerfile.dev <span class=\"token builtin class-name\">.</span> -t test_react\n</code></pre></div>\n<p>Esto nos devuelve una imagen con ese nombre</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-2.JPG\" alt=\"Capture-2\"></p>\n<p>Ya la tenemos, ahora solo vamos a realizar un <em>run</em> habitual cambiando el comando de arranque</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker run -it test_react <span class=\"token function\">npm</span> <span class=\"token builtin class-name\">test</span>\n</code></pre></div>\n<p>Le he añadido <em>-it</em> para poder trabajar con la consola de test y como algo nuevo si nos fijamos hemos puesto <em>cosas</em> tras el nombre de la imagen, básicamente todo lo que ponemos a continuación del nombre de la imagen a ejecutar lo toma como comando de arranque para el contenedor. Si lo ejecutamos nos devuelve<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-3.JPG\" alt=\"Capture-3\"></p>\n<p>Otra opción es añadirlo a nuestro <em>docker-compose</em> añadiendo otro servicio pero con la misma teoría, compartiendo o <em>mapeando</em> ficheros entre el equipo local y el contenedor, teniendo 2 contenedores funcionando uno para las pruebas en desarrollo y otro probando los test. El problema de este acercamiento es que no tenemos control sobre la consola de test por lo que no podemos hacer mucho más que ver como pasan los tests cada vez que hacemos un cambio en los ficheros.<br>\nVamos a probarlo, cambiamos nuestro fichero <em>docker-compose</em></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'3'</span>\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">web_react</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span>\n            <span class=\"token key atrule\">context</span><span class=\"token punctuation\">:</span> .\n            <span class=\"token key atrule\">dockerfile</span><span class=\"token punctuation\">:</span> Dockerfile.dev\n        <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> <span class=\"token string\">\"3000:3000\"</span>\n        <span class=\"token key atrule\">volumes</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> /app/node_modules\n            <span class=\"token punctuation\">-</span> .<span class=\"token punctuation\">:</span>/app\n    <span class=\"token key atrule\">test_react</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span>\n            <span class=\"token key atrule\">context</span><span class=\"token punctuation\">:</span> .\n            <span class=\"token key atrule\">dockerfile</span><span class=\"token punctuation\">:</span> Dockerfile.dev\n        <span class=\"token key atrule\">volumes</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> /app/node_modules\n            <span class=\"token punctuation\">-</span> .<span class=\"token punctuation\">:</span>/app\n        <span class=\"token key atrule\">command</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"npm\"</span><span class=\"token punctuation\">,</span><span class=\"token string\">\"test\"</span><span class=\"token punctuation\">]</span>\n</code></pre></div>\n<p>Como véis hemos añadido otro servicio (contenedor ya sabéis), en este caso se llama <strong>test_react</strong>, que usa el mismo <em>dockerfile</em> y mapea de la misma forma los <em>volumenes</em>. Despues de eso si que tiene cambios, hemos quitado el <em>mapeo</em> del puerto porque ya no lo necesitamos y como extra nuevo hemos añadido la propiedad <strong>command</strong> que básicamente lo que hace es cambiar el comando de inicio del contenedor.<br>\nA continuación si ejecutamos nuestro <em>docker-compose up</em>, nos crea dos contenedores y como podremos ver el log ambos funcionan correctamente, y si cambiamos algo en los ficheros se actualizan ambos, tanto el de desarrollo como el que pasa los tests.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-4.JPG\" alt=\"Capture-4\"></p>\n<p>Ninguno de los casos es muy <strong>ideal</strong> pero son funcionales y puede que en algún caso nos pueda servir para algo, más adelante veremos un entorno de test más 'real' por el momento esto es más que suficiente, a continuación empezaremos a hablar un poco de <strong>PRODUCCIÓN</strong></p>\n<h4 id=\"produccin\">PRODUCCIÓN</h4>\n<p>Pasemos ahora a <em>producción</em>, la intención es crear un contenedor que nos devuelva nuestra aplicación ya preparada para producción, para el que no lo sepa, una app de react la preparamos para producción ejecutando el comando:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token function\">npm</span> run build\n</code></pre></div>\n<p>Y este comando nos deja unos ficheros típicos de web (html, js y css), es decir, <em>ficheros estáticos</em>. Estos ficheros los deja en una carpeta llamada <strong>build</strong> dentro de nuestro proyecto.</p>\n<p>Ahora necesitamos para producción un <em>servidor web</em>, los más utilizados son <strong>Apache o Nginx</strong>, aunque podríamos hacerlo con NodeJS, Go, Ruby, etc..... con casi todos los lenguajes tenemos alguna opción para hacerlo. En nuestro caso usaremos un contenedor con Nginx.</p>\n<p>Si miramos la documentación del <a href=\"https://hub.docker.com/_/nginx/\">contenedor oficial</a> podemos ver que los ficheros los sirve desde el path <strong>/usr/share/nginx/html</strong>. Sabiendo esto entonces básicamente lo que tendriamos que hacer sería copiar nuestros ficheros de producción en esa ruta del contenedor de nginx... pero claro se supone que no tenemos en local los archivos, veamos como podemos hacerlo con el entorno que tenemos ahora.</p>\n<p>Nos vamos a crear un nuevo <strong>dockerfile</strong> con esto</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token instruction\"><span class=\"token keyword\">FROM</span> node:alpine <span class=\"token keyword\">as</span> builder</span>\n<span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> <span class=\"token string\">'/app'</span></span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> package.json .</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> npm install</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> . .</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> npm run build</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">FROM</span> nginx</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> <span class=\"token options\"><span class=\"token property\">--from</span><span class=\"token punctuation\">=</span><span class=\"token string\">builder</span></span> /app/build /usr/share/nginx/html</span>\n</code></pre></div>\n<p>Empecemos por la primera linea:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">FROM node:alpine as builder\n</code></pre></div>\n<p>Introducimos algo nuevo en este punto, básicamente lo que estamos haciendo es indicarle al proceso que añada como una referencia al resultado del <em>build</em> de ese contenedor con nombre <em>builder</em>, pero puede ser cualquier otro. Esta referencia solo está disponible en el contexto de la ejecución de <em>docker build</em>.<br>\nEl resto del primer contenedor es algo que ya hemos visto, vayamos con el segundo</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token instruction\"><span class=\"token keyword\">FROM</span> nginx</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> <span class=\"token options\"><span class=\"token property\">--from</span><span class=\"token punctuation\">=</span><span class=\"token string\">builder</span></span> /app/build /usr/share/nginx/html</span>\n</code></pre></div>\n<p>Aquí empezamos con el segundo contenedor. Vemos como la instrucción COPY tiene algo nuevo</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\">--from=builder\n</code></pre></div>\n<p>Como os podéis imaginar tiene que ver con la instrucción <em>as builder</em> del primer contenedor, aquí le estamos diciendo que del primer contenedor se copie la ruta <em>/app/build</em> y la pegue en <em>/usr/share/nginx/html</em><br>\nBien pues vamos a ejecutar nuestro nuevo build</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker build <span class=\"token builtin class-name\">.</span>\n</code></pre></div>\n<p>Una vez terminado</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-5.JPG\" alt=\"Capture-5\"></p>\n<p>Tenemos ya construido una imagen con supuestamente <strong>nginx</strong> y nuestra app en producción. Por último nos faltaría crear un contenedor con esa imagen, pues vamos a ello</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker run -p <span class=\"token number\">3500</span>:80 --name webpro idImagen\n</code></pre></div>\n<p>Como extra he añadido <strong>--name</strong> que lo que hace es <em>taggearnos</em> el contenedor con un nombre que podamos gestionar de manera más comoda que un ID numérico</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-6.JPG\" alt=\"Capture-6\"></p>\n<p>Se puede ver al principio el comando y a continuación un log (el de nginx) una vez que intentamos acceder a la página</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture2.JPG\" alt=\"Capture2\"></p>\n<p>Y como podemos ver ya tenemos nuestro entorno para producción que básicamente es el build del <em>dockerfile</em> una vez que hemos terminado de desarrollar.</p>\n<p>En próximos posts veremos una forma más profesional de hacer todo esto con <em>Integración Continua</em> gracias a <strong>TravisCI</strong> y <strong>GitHub</strong>.</p>\n<!--kg-card-end: markdown-->","htmlAst":{"type":"root","children":[{"type":"comment","value":"kg-card-begin: markdown"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"La idea es ir empezando a ver como sería un entorno real de desarrollo con Docker teniendo:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Nuestro entorno de desarrollo"}]},{"type":"text","value":": Un entorno donde "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"creamos"}]}]},{"type":"text","value":" las cosas."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Entorno de test"}]},{"type":"text","value":": Tras el desarrollo, pasamos nuestro código al entorno de test donde, como su propio nombre indica, probamos que todo funciona."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Entorno de producción"}]},{"type":"text","value":": Tras pasar los tests pertinentes automáticamente el proyecto pasaría a producción."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Este "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"workflow"}]},{"type":"text","value":" es lo que comunmente se conoce como "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Integración Continua, Continuous Integration o CI"}]},{"type":"text","value":". Para este proceso usaremos herramientas o servicios como Github o Travis CI y desarrollaremos con ReactJS (no importa que no sepamos usarlos veremos lo necesario para entender lo que estamos haciendo)."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"entornodedesarrollo"},"children":[{"type":"text","value":"Entorno de desarrollo"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Empecemos por el primero de los entornos, el entorno de desarrollo. Lo que buscamos en este entorno es que nuestros cambios mientras estamos desarrollando se "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"sincronicen"}]},{"type":"text","value":" automáticamente con el contenedor de desarrollo  (esto en varios casos es innecesario pero lo veremos igualmente con los fines didácticos que nos ocupan)."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nPensando en el objetivo de este entorno, ya hemos visto que si realizamos cambios en nuestro código tenemos que volver a realizar un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"build"}]}]},{"type":"text","value":" y luego arrancar un contenedor nuevo con la imagen que nos crearía el build anterior. Esto realmente no es lo que estamos buscando, es poco eficiente y tendríamos demasiadas imágenes de contenedor."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"En docker existe una forma de solucionar esto, vamos a introducir algo que no hemos visto todavia: "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Los Volúmenes"}]},{"type":"text","value":"."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nEn docker un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"volumen"}]},{"type":"text","value":" no deja de ser una referencia al filesystem del contenedor y puede ser una referencia, como un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"mapeo"}]},{"type":"text","value":" (al igual que los puertos) de una carpeta local a una del contenedor, o un 'no lo toques' (que básicamente es mapea todo menos esto). Lo vamos a probar directamente, primero de todo vamos preparar un proyecto para trabajar con el, empecemos por crearnos un proyecto de ReactJS."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Para trabajar con react usaremos un paquete de npm que nos instala un proyecto básico inicial, lo instalamos con"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" -g "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"install"}]},{"type":"text","value":" create-react-app\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez instalado ya podríamos crearnos un proyecto"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"text"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-text"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"create-react-app frontWeb\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos creará un proyecto de react dentro de una carpeta llamada frontWeb. Para probar si funciona solo tenemos que entrar en la carpeta y ejecutar con:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" start\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture.JPG","alt":"Capture"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Bien ya tenemos nuestra web de react, vamos ahora con la parte del entorno de "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"desarrollo"}]},{"type":"text","value":" de docker."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nPara ejecutar nuestro entorno de desarrollo en un contenedor solo necesitamos que tenga "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"node"}]},{"type":"text","value":" instalado, es decir, que es similar a lo que hemos creado anteriormente."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Antes de continuar comentar que aunque vamos a trabajar casi todo el rato con comandos de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":" todo lo que hagamos a partir de ahora se puede hacer con comandos "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker run"}]},{"type":"text","value":" también, pero realmente son comandos muy largos y poco útiles a la larga. En caso de necesidad siempre podemos buscar cual es el flag del comando para ponerlo directamente sin usar un "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Continuemos, volvamos al principio, hemos dicho que queríamos un entorno de desarrollo donde nuestro contenedor se actualice automáticamente según vayamos haciendo cambios en local, para ello vamos a hacer uso de lo que en docker se conoce como "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"volumenes"}]},{"type":"text","value":"."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nLo primero nos crearemos dentro de nuestra carpeta de proyecto de react un fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" pero esta vez pondremos:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"Dockerfile.dev\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como es normal aunque no lo hayamos visto a docker se le puede indicar el fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" a usar cuando hacemos un build, solo tenemos que usar el flag "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"-f"}]},{"type":"text","value":",  por ejemplo (ojo al punto del final)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker build -f ./Dockerfile.dev "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Lo mismo con los ficheros para "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":", por lo que realmente no tendremos ningún problema y podemos tener varios "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" distintos según nuestro entorno."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nSabiendo esto continuamos con nuestro fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Dockerfile.dev"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" node:alpine"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"WORKDIR"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'/app'"}]}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" package.json ."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" npm install"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" . ."}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"CMD"}]},{"type":"text","value":" ["},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"npm\""}]},{"type":"text","value":", "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"start\""}]},{"type":"text","value":"]"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Misma teoria que anteriormente, copiamos el package.json primero por posibles cambios solo del resto y no tener que hacer otra vez el "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"npm install"}]},{"type":"text","value":" todo el rato cada vez que hagamos un build."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nYa tenemos nuestro fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":", ahora como la idea es usar "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":" para todo necesitamos crear el fichero docker-compose.yml"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"version"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'3'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"services"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"web_react"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"build"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" \n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"context"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" .\n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"dockerfile"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Dockerfile.dev\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"3000:3000\""}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Antes de continuar, vemos que ahora donde tenemos puesto "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"build"}]},{"type":"text","value":" ahora hemos añadido 2 propiedades:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"context"}]},{"type":"text","value":": Indicamos el contexto desde(path) desde el que trabajara el build del docker-compose."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":": Nombre del fichero dockerfile que queremos usar."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Continuemos, ahora vamos a hablar del concepto de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Volume"}]},{"type":"text","value":" (por fin ;) ), primero añadamoslo al fichero"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"version"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'3'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"services"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"web_react"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"build"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" ./Dockerfile.dev\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"3000:3000\""}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"volumes"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" /app/node_modules\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" ."},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"/app\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si nos fijamos en lo que hemos puesto, tenemos realmente dos conceptos distintos dentro de "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"volumes"}]},{"type":"text","value":":"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":".:/app"}]},{"type":"text","value":": Hablemos primero del segundo, este es similar al concepto de mapear puertos, básicamente le estamos indicando que "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"mapee"}]},{"type":"text","value":" todo el contenido de la ruta actual de mi equipo local, al path "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"/app"}]},{"type":"text","value":" del contenedor, lo que funcionaría similar a un acceso directo a los ficheros de la carpeta local de nuestro equipo."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"/app/node_modules"}]},{"type":"text","value":": Si nos fijamos en esta línea no tenemos "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"':'"}]},{"type":"text","value":", eso es porque aquí le estamos indicando que haga como un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"marcador"}]},{"type":"text","value":" de la carpeta "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"node_modules"}]},{"type":"text","value":" del contenedor, es decir, usa la del contenedor, no la toques y dejala donde está (esto hace que la carpeta se mantenga aunque hagamos el paso anterior)."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora que ya sabemos lo que hemos puesto, viene la pregunta del ¿por qué?....bien, si pensamos en el proceso que hemos añadido en el "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Dockerfile.dev"}]},{"type":"text","value":", tenemos una parte donde instalamos los paquetes que están indicados en el "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"package.json"}]},{"type":"text","value":", es decir, queremos que los descargues e instales de nuevo cuando hagamos un build de la imagen del contenedor, y no pasamos la carpeta local "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"node_modules"}]},{"type":"text","value":" (que es donde instala las dependencias), que de hecho la vamos a eliminar para que veamos como funciona, si, "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"eliminarla"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ya que hablamos del "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Dockerfile.dev"}]},{"type":"text","value":", alguno se puede preguntar si hacemos la refencia o linkado de nuestra carpeta local, ¿para qué hacemos el COPY?....bueno, esto es para prevenir creaciones para producción usando los mismos Dockerfiles, realmente en nuestro caso actual no lo necesitamos pero para producción siempre es mejor para evitar errores, ya que un contenedor en producción "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"NO DEBE"}]},{"type":"text","value":" hacer referencias a carpetas locales de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"ningún sitio"}]},{"type":"text","value":" solo tiene que tener sus propios ficheros."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ya tenemos todo ahora nos situamos en la ruta donde tenemos el fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker-compose.yml"}]},{"type":"text","value":" y ejecutamos:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker-compose up\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si todo va bien deberíamos ver algo como esto en la consola"},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\n"},{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.35.40.png","alt":"Captura-de-pantalla-2018-11-14-a-las-9.35.40"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y si accedemos en el navegador a:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"localhost:3000\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Deberíamos ver"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.37.02.png","alt":"Captura-de-pantalla-2018-11-14-a-las-9.37.02"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Bien como tal ya tenemos todo funcionando, pero realmente lo que queremos es poder desarrollar en local y que se actualice el contenedor , ¿no?... pues vamos a probarlo."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nNos vamos a nuestra web de react, y dentro de la carpeta "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"src"}]},{"type":"text","value":" modificamos el fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"App.js"}]},{"type":"text","value":" y ponemos lo que queramos:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"html"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-html"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-html"]},"children":[{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"div"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"className"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"App"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"header"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"className"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"App-header"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":"\n          "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"img"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"src"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":"{logo}"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"className"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"App-logo"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"alt"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"logo"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"/>"}]}]},{"type":"text","value":"\n          "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"p"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":"\n            Hola ninjaaaaasssss\n          "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"</"}]},{"type":"text","value":"p"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":" //"},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"<"}]},{"type":"text","value":"--"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"Esta"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"es"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"la"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"linea"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"que"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"cambiamos"}]},{"type":"text","value":"\n          "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"<a"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"className"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"App-link"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"href"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"https://reactjs.org"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"target"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"_blank"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","attr-name"]},"children":[{"type":"text","value":"rel"}]},{"type":"element","tagName":"span","properties":{"className":["token","attr-value"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation","attr-equals"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]},{"type":"text","value":"noopener noreferrer"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"\""}]}]},{"type":"text","value":"\n          "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":"\n            Learn React\n          "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"</"}]},{"type":"text","value":"a"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"</"}]},{"type":"text","value":"header"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","tag"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"</"}]},{"type":"text","value":"div"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":">"}]}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y una vez que guardemos el documento automáticamente se debería actualizar el navegador y mostrar"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-14-a-las-9.40.32.png","alt":"Captura-de-pantalla-2018-11-14-a-las-9.40.32"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"¡¡¡PERFECTO!!!... ya tenemos nuestro entorno de desarrollo funcionando."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Empecemos con el entorno de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"TEST"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h4","properties":{"id":"test"},"children":[{"type":"text","value":"TEST"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Por defecto react viene ya configurado con 1 test para poder probar, solo tenemos que hacer"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" run "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"test"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"o"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"test"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"y automáticamente nos pasaría los tests"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-1.JPG","alt":"Capture-1"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Bien, pues ahora queremos esto pero en un contenedor, como opción rápida podemos ejecutar una imagen creada con el mismo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" pero cambiando el comando de arranque (esto es solo para que veais que funciona)."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Por si no la tenemos creamos el build de la imagen, yo la voy a "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"taggear"}]},{"type":"text","value":" para poder identificarla"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker build -f ./Dockerfile.dev "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":" -t test_react\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos devuelve una imagen con ese nombre"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-2.JPG","alt":"Capture-2"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ya la tenemos, ahora solo vamos a realizar un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"run"}]},{"type":"text","value":" habitual cambiando el comando de arranque"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker run -it test_react "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"test"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Le he añadido "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"-it"}]},{"type":"text","value":" para poder trabajar con la consola de test y como algo nuevo si nos fijamos hemos puesto "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"cosas"}]},{"type":"text","value":" tras el nombre de la imagen, básicamente todo lo que ponemos a continuación del nombre de la imagen a ejecutar lo toma como comando de arranque para el contenedor. Si lo ejecutamos nos devuelve"},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\n"},{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-3.JPG","alt":"Capture-3"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Otra opción es añadirlo a nuestro "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker-compose"}]},{"type":"text","value":" añadiendo otro servicio pero con la misma teoría, compartiendo o "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"mapeando"}]},{"type":"text","value":" ficheros entre el equipo local y el contenedor, teniendo 2 contenedores funcionando uno para las pruebas en desarrollo y otro probando los test. El problema de este acercamiento es que no tenemos control sobre la consola de test por lo que no podemos hacer mucho más que ver como pasan los tests cada vez que hacemos un cambio en los ficheros."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nVamos a probarlo, cambiamos nuestro fichero "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker-compose"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"version"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'3'"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"services"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"web_react"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"build"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"context"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" .\n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"dockerfile"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Dockerfile.dev\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"3000:3000\""}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"volumes"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" /app/node_modules\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" ."},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"/app\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"test_react"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"build"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"context"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" .\n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"dockerfile"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Dockerfile.dev\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"volumes"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" /app/node_modules\n            "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" ."},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"/app\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"command"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"npm\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"test\""}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"]"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como véis hemos añadido otro servicio (contenedor ya sabéis), en este caso se llama "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"test_react"}]},{"type":"text","value":", que usa el mismo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" y mapea de la misma forma los "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"volumenes"}]},{"type":"text","value":". Despues de eso si que tiene cambios, hemos quitado el "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"mapeo"}]},{"type":"text","value":" del puerto porque ya no lo necesitamos y como extra nuevo hemos añadido la propiedad "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"command"}]},{"type":"text","value":" que básicamente lo que hace es cambiar el comando de inicio del contenedor."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nA continuación si ejecutamos nuestro "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker-compose up"}]},{"type":"text","value":", nos crea dos contenedores y como podremos ver el log ambos funcionan correctamente, y si cambiamos algo en los ficheros se actualizan ambos, tanto el de desarrollo como el que pasa los tests."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-4.JPG","alt":"Capture-4"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ninguno de los casos es muy "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"ideal"}]},{"type":"text","value":" pero son funcionales y puede que en algún caso nos pueda servir para algo, más adelante veremos un entorno de test más 'real' por el momento esto es más que suficiente, a continuación empezaremos a hablar un poco de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"PRODUCCIÓN"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h4","properties":{"id":"produccin"},"children":[{"type":"text","value":"PRODUCCIÓN"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Pasemos ahora a "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"producción"}]},{"type":"text","value":", la intención es crear un contenedor que nos devuelva nuestra aplicación ya preparada para producción, para el que no lo sepa, una app de react la preparamos para producción ejecutando el comando:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"npm"}]},{"type":"text","value":" run build\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y este comando nos deja unos ficheros típicos de web (html, js y css), es decir, "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"ficheros estáticos"}]},{"type":"text","value":". Estos ficheros los deja en una carpeta llamada "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"build"}]},{"type":"text","value":" dentro de nuestro proyecto."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora necesitamos para producción un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"servidor web"}]},{"type":"text","value":", los más utilizados son "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Apache o Nginx"}]},{"type":"text","value":", aunque podríamos hacerlo con NodeJS, Go, Ruby, etc..... con casi todos los lenguajes tenemos alguna opción para hacerlo. En nuestro caso usaremos un contenedor con Nginx."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si miramos la documentación del "},{"type":"element","tagName":"a","properties":{"href":"https://hub.docker.com/_/nginx/"},"children":[{"type":"text","value":"contenedor oficial"}]},{"type":"text","value":" podemos ver que los ficheros los sirve desde el path "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"/usr/share/nginx/html"}]},{"type":"text","value":". Sabiendo esto entonces básicamente lo que tendriamos que hacer sería copiar nuestros ficheros de producción en esa ruta del contenedor de nginx... pero claro se supone que no tenemos en local los archivos, veamos como podemos hacerlo con el entorno que tenemos ahora."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Nos vamos a crear un nuevo "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" con esto"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" node:alpine "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"as"}]},{"type":"text","value":" builder"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"WORKDIR"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'/app'"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" package.json ."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" npm install"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" . ."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" npm run build"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" nginx"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","options"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"--from"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"builder"}]}]},{"type":"text","value":" /app/build /usr/share/nginx/html"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Empecemos por la primera linea:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"text"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-text"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"FROM node:alpine as builder\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Introducimos algo nuevo en este punto, básicamente lo que estamos haciendo es indicarle al proceso que añada como una referencia al resultado del "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"build"}]},{"type":"text","value":" de ese contenedor con nombre "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"builder"}]},{"type":"text","value":", pero puede ser cualquier otro. Esta referencia solo está disponible en el contexto de la ejecución de "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker build"}]},{"type":"text","value":"."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nEl resto del primer contenedor es algo que ya hemos visto, vayamos con el segundo"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" nginx"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","options"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"--from"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"builder"}]}]},{"type":"text","value":" /app/build /usr/share/nginx/html"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Aquí empezamos con el segundo contenedor. Vemos como la instrucción COPY tiene algo nuevo"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"text","value":"--from=builder\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como os podéis imaginar tiene que ver con la instrucción "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"as builder"}]},{"type":"text","value":" del primer contenedor, aquí le estamos diciendo que del primer contenedor se copie la ruta "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"/app/build"}]},{"type":"text","value":" y la pegue en "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"/usr/share/nginx/html"}]},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nBien pues vamos a ejecutar nuestro nuevo build"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker build "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez terminado"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-5.JPG","alt":"Capture-5"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Tenemos ya construido una imagen con supuestamente "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"nginx"}]},{"type":"text","value":" y nuestra app en producción. Por último nos faltaría crear un contenedor con esa imagen, pues vamos a ello"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker run -p "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"3500"}]},{"type":"text","value":":80 --name webpro idImagen\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como extra he añadido "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"--name"}]},{"type":"text","value":" que lo que hace es "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"taggearnos"}]},{"type":"text","value":" el contenedor con un nombre que podamos gestionar de manera más comoda que un ID numérico"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture-6.JPG","alt":"Capture-6"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Se puede ver al principio el comando y a continuación un log (el de nginx) una vez que intentamos acceder a la página"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Capture2.JPG","alt":"Capture2"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y como podemos ver ya tenemos nuestro entorno para producción que básicamente es el build del "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dockerfile"}]},{"type":"text","value":" una vez que hemos terminado de desarrollar."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"En próximos posts veremos una forma más profesional de hacer todo esto con "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Integración Continua"}]},{"type":"text","value":" gracias a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"TravisCI"}]},{"type":"text","value":" y "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"GitHub"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"comment","value":"kg-card-end: markdown"}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"entornodedesarrollo","heading":"Entorno de desarrollo","items":[{"id":"test","heading":"TEST"},{"id":"produccin","heading":"PRODUCCIÓN"}]}]},"featureImageSharp":{"base":"docker_facebook_share-3.png","publicURL":"/static/8250fb6cd246dd294ed3fc4d99218f71/docker_facebook_share-3.png","imageMeta":{"width":336,"height":287},"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADmElEQVQ4y21Ua0wUVxQ+s8PMvbvgo0YlaWP8UZvYGK2JsU39Y2IwNcZWiI+aqt2YIGjLuhRdtYDFBXlUBbQtLnQXV80uuCi0FNvaovFBUdEpxl9qamtTAR8FY+sfKbC3350dwFU3+XJ2zr3nO995zBA989M3N5GeczyGvJ9t+vYfbOo7LmKlBjHvL2Q/LshxQpDuCsfhuZ90sqKLpGdHSN9UH8PWFjOBluEfB7IqtuO00x58RLy6R+EV1214JlZwllj+KdIkcdZTxLr7KPEv/iAtvTr2vOXbMSCcpXu+m6Z/ciyVlXUKVnihOfGsoIlCEK+8QbZFUL7zfIL6roe09TWoqnGUkBWcI1bcQXr+KY1520j3NL/FdnUIVnJlkJUYT1jZrwIqg9zXTfqnJ19in52pSFhe4AAhqam5LyAEGdt1ycZ2thEue1lh+1VWfHnQJCw1BlgpCIsudiFZPdQv4ftvCfhCLK91hZYVcsjK4giRVdE+Oow+hKayogtdrOyqVPcfLzWGoA7oHDRJAQyrin95+z7Khuo2wzbfyW0pGYTWjBLyypsqr7ghSy/nB+4Iu6/7SWJ1dzTBdzfKfN1RHsMQ93X185p7Ancf6+7IQlJ1xRxIdkSJL7nEUHlhOybWuhaBQqn9t598vdG0pj+jkw/9HdX8fVF7oG/AHvxngFf9JXBvG1pEUzAgbIQi1cWXnN2gUJqX6L3cJEdJx63XAr+L5S09Q3MiD4aYvzdqr30o7IFegU0QWJWqpHZBSW1C0V11ilwvuToj+2guJ9bmdW+z+kpuIyW7gzMqf7pm9DzoE+XGQzG+Gor2/dbPyzov69u/f98R6jfj5CC0TH/8YmtYRgk41bE5ESKnn1burpdHygRPw1z34dNLZxe3LCZnzRQyBMmVsh98ZNM+PqJiulAYllAtKwlDcssVaeGYCDuPNh4hWuWjWd5vaNlXP9KkrceIbQq/reU0JWuZteZbZMbFYkYAYcqoIyu8CLYcSJeksE76MEAOd10Kc4VXIOnn8E3H2Zuwk4FVwKvAG0CmPMPK0TDzGtgNQBZw0rqwGvABeVbQXuASgtJg9wMBoA5olSIg6GXzXY7JDq2HU5KmAg1IsBZWKm7AmQc2Gff2WiRS2ddAEMgH9lgVSnHKcC9kUzPgXGe14IOYWrNHS6DKhSQpKFsDFlilbgRmArPhS9SGP2PPftfMFoz8Dz3VcDm8+CFYqqyY2Nn/8hrXL6If80cAAAAASUVORK5CYII=","aspectRatio":1.174496644295302,"src":"/static/8250fb6cd246dd294ed3fc4d99218f71/5742b/docker_facebook_share-3.png","srcSet":"/static/8250fb6cd246dd294ed3fc4d99218f71/847ef/docker_facebook_share-3.png 175w,\n/static/8250fb6cd246dd294ed3fc4d99218f71/5742b/docker_facebook_share-3.png 336w","srcWebp":"/static/8250fb6cd246dd294ed3fc4d99218f71/8cfb4/docker_facebook_share-3.webp","srcSetWebp":"/static/8250fb6cd246dd294ed3fc4d99218f71/9fca7/docker_facebook_share-3.webp 175w,\n/static/8250fb6cd246dd294ed3fc4d99218f71/8cfb4/docker_facebook_share-3.webp 336w","sizes":"(max-width: 336px) 100vw, 336px"}}}},"next":{"id":"Ghost__Post__5be1df0c2dd6610fd828c9ed","title":"Docker II: Creando contenedores","slug":"docker-ii-creando-contenedores","featured":false,"feature_image":"https://jlgarcia.fulldev.ninja/assets/images/2018/12/docker_facebook_share-1.png","excerpt":"Ahora vamos a empezar a trabajar con lo que se conoce como Dockerfile, que\nbásicamente es un fichero con la configuración que queremos para un contenedor.\n\nLo primero nos creamos un fichero llamado Dockerfile(sin extension)\n\n# Indicamos una imagen base de la que partir. Usamos alpine que es una básica sin prácticamente nada\nFROM alpine\n\n#Descargamos e instalamos las dependencias que queramos. Usamos el copando apk que es un gestor de paquetes que viene dentro de la imagen de alpine\nRUN apk add -","custom_excerpt":null,"visibility":"public","created_at_pretty":"6 Nov 2018","published_at_pretty":"1 Aug 2018","updated_at_pretty":"19 Jan 2021","created_at":"2018-11-06T19:35:56.000+01:00","published_at":"2018-08-01T12:32:00.000+02:00","updated_at":"2021-01-19T20:43:46.000+01:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"Perfil.jpg","publicURL":"/static/b0de6281fb28a266510b3b09b9243e5a/Perfil.jpg","imageMeta":{"width":307,"height":307},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAUDBAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGzw6zC6zHn+cLYP//EAB0QAAICAQUAAAAAAAAAAAAAAAEDAAIEEyEiIzL/2gAIAQEAAQUCifca8KgcKWVfUpkHsG5pxX//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAdEAACAgEFAAAAAAAAAAAAAAAAARARcQISIUFR/9oACAEBAAY/AhU88xkb7N06a8P/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUFRYXH/2gAIAQEAAT8hR2pq40aqb+xIAeXibhW9JXr8joF4TBcSNe0//9oADAMBAAIAAwAAABDzDwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFhcfD/2gAIAQEAAT8QyItrELaTlatLwU63MvEW6vUNdy4LZQDn7iVApV9VLtANdWwKkuYq4Er1VZ//2Q==","aspectRatio":1,"src":"/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg","srcSet":"/static/b0de6281fb28a266510b3b09b9243e5a/f340b/Perfil.jpg 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/22d64/Perfil.jpg 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/aa249/Perfil.jpg 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/0dc33/Perfil.jpg 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/60667/Perfil.jpg 307w","srcWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp","srcSetWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/59cda/Perfil.webp 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/7da75/Perfil.webp 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f282e/Perfil.webp 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/a7b21/Perfil.webp 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f59af/Perfil.webp 307w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},"tags":[{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null}],"plaintext":"Ahora vamos a empezar a trabajar con lo que se conoce como Dockerfile, que\nbásicamente es un fichero con la configuración que queremos para un contenedor.\n\nLo primero nos creamos un fichero llamado Dockerfile(sin extension)\n\n# Indicamos una imagen base de la que partir. Usamos alpine que es una básica sin prácticamente nada\nFROM alpine\n\n#Descargamos e instalamos las dependencias que queramos. Usamos el copando apk que es un gestor de paquetes que viene dentro de la imagen de alpine\nRUN apk add --update redis\n\n# Le indicamos a la imagen que hacer o que comandos ejecutar cuando arranque\nCMD [\"redis-server\"]\n\n\nNos situamos en la consola (o linea de comandos), en la carpeta donde tenemos el\nfichero y ejecutamos\n\ndocker build .\n\n\nEsto nos ejecuta los comandos que tiene nuestro fichero. Para que entendamos el\nproceso, si miramos detenidamente el log de salida del comando veremos como en\ncada paso nos genera una imagen intermedia, ejecuta el comando del paso que le\ntoque crea un snapshot para el siguiente paso y elimina la imagen anterior\n\n\nPor último vemos que nos pone Successfully build ....., ese ID que nos aparece\nseria el nombre de nuestra imagen creada, a continuación solo tenemos que\nejecutar\n\ndocker run IDimagen\n\n\nY veremos como se ejecuta perfectamente nuestro contenedor\n\n\nY si miramos que se esta ejecutando, veremos un nuevo contenedor con un nombre\naleatorio que parte de nuestra imagen anteriormente creada\n\n\nUsando la Caché\nSi rehusamos un Dockerfile o creamo uno nuevo igual que otro o con los mismo\npasos iniciales, Docker usará su caché para no tener que repetir procesos que ya\nha ejecutado, veamoslo con un ejemplo. Si añadimos otra dependencia a la\ninstalación:\n\nFROM alpine\n\nRUN apk add --update redis\nRUN apk add --update gcc\nRUN apk add --update vim\n\nCMD [\"redis-server\"]\n\n\nY ejecutamos de nuevo el comando build, veremos como en los pasos iniciales nos\nsale Using cache, en algunas lineas, concretamente en las que se han repetido. \nOJO solo aparece hasta que llega a la línea que ha cambiado del Dockerfile, una\nvez que encuentra algo distinto ya lo hace todo de nuevo (si cambiaramos el\norden de los comandos RUN lo haría todo de nuevo)\n\n\n\nAñadir TAG a imagen\nSi nos fijamos cuando se termina de construir la imagen de nuestro contenedor,\nnos pone un ID cualquiera, algo que es dificil de recordar. Es posible indicarle\nun nombre identificativo a nuestro contenedor, para ello basta con:\n\ndocker build -t aliasDockerHub/nombreImagen:latest .\n\n\nCon el flag t indicamos que queremos taggear la imagen creada con un nombre, el\ncual es necesario que tenga la estructura que veis con el alias que tenemos en\nel marketplace de DockerHub\n\nCrear Imagen a partir de un contenedor existente\nLa idea es configurar un contenedor como queramos y luego generar una imagen de\nese contenedor para poder desplegarla. Es necesario indicarle cual el es comando\nde inicio\n\ndocker commit -c 'CMD[\"redis-server\"]' idContenedor\n\n\nCon este comando generaríamos una imagen que arrancaría con el comando \nredis-server. Este nos genera una imagen con un id gigantesco, pero para usarla\nno tenemos que copiar todo el id con una parte inicial que sea única y ya docker\nla busca.\n\nCreando imagen propia 2.0\nHasta ahora hemos visto como crear una imagen usando otra como base e instalando\nalgún paquete extra, pero....y ¿si queremos añadir algún desarrollo o fichero\nnuestro? Vamos a crear una mini aplicación en NodeJS que nos devuelva una\npágina. Para ello vamos a crearnos una nueva carpeta para almacenar todos los\nficheros de este contenedor, por ejemplo la llamaremos miniapp.\nDentro de esta carpeta lo primero nos creamos un fichero package.json que\ncontendrá las dependencias y scripts de nuestro aplicación en NodeJS (quién no\nsepa de que estamos hablando puede revisarse los post de NodeJS donde lo explico\ntodo con más detalle). Dentro de este fichero ponemos:\n\n{\n  \"dependencies\": {\n    \"express\": \"*\"\n  },\n  \"scripts\": {\n    \"start\": \"node index.js\"\n  }\n}\n\n\nAquí le estamos diciendo que tenemos como dependencias cualquier versión de\nexpress y un script de npm para arrancar el fichero index.js.\nAhora como os podéis imaginar tenemos que crear ese fichero, el index.js\n\nconst express = require('express');\n \nconst app = express();\n \napp.get('/', (req, res) => {\n  res.send('How are you doing');\n});\n \napp.listen(8080, () => {\n  console.log('Listening on port 8080');\n});\n\n\nYa tenemos nuestro proyecto de node preparado, ahora vamos a crear nuestro\nfichero Dockerfile para indicarle los pasos necesarios\n\n#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)\nFROM node:alpine\n\n#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor\nCOPY ./ ./\n#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json\nRUN npm install\n\n# Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node\nCMD [\"npm\",\"start\"]\n\n\nAhora tenemos como comando nuevo COPY este comando copia contenido de una\ncarpeta local (relativa a la ejecución del comando de docker build) a una\ncarpeta en el propio contenedor.\n\nCambiando el directorio de ejecución en el contenedor\nCon la configuración anterior, en el contenedor anterior todo se copia y se\nejecuta en la raiz (/), eso en linux NO ESTÁ BIEEEEN, tenemos que indicarle un\ndirectorio en condiciones a nuestra aplicación o a nuestro ficheros. Para ello\ntenemos el comando WORKDIR:\n\nWORKDIR pathDondeTrabajar\n\n\nBásicamente cambia el path desde el que se está trabajando en el Dockerfile, una\nruta posible para usar puede ser dentro de /usr/app (si no existe, nos crea la\ncarpeta), por lo que quedaría así nuestro Dockerfile\n\n#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)\nFROM node:alpine\n\n# Directorio desde el que funcionarian las siguientes instrucciones\nWORKDIR /usr/app\n\n#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor\nCOPY ./ ./\n\n#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json\nRUN npm install\n\n# Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node\nCMD [\"npm\",\"start\"]\n\n\nAhora si hicieramos un build del contenedor, arrancaramos una imagen y nos\nconectaramos con exec -it podríamos ver que nos conectamos directamente al\ndirectorio que hemos indicado como working directory....peeeeeero realmente\ntodavía no funcionaria nuestra aplicación, veamos lo último para hacerla\nfuncionar.\n\nMapear puertos locales a puertos de contenedor\nEl que tenga algunos conocimientos de node se habrá dado cuenta de que lo que\nhacemos es arrancar una aplicación en el puerto 8080, pero claro, es el puerto\n8080 de nuestro contenedor no de nuestro equipo, tenemos que pensar que un\ncontenedor es prácticamente como un equipo independiente, como si hablaramos de\nuna máquina virtual.\n\nPara poder hacer uso de un puerto del contenedor, tenemos que mapear un puerto\nlocal a uno del contenedor, no tienen porque ser iguales, pero si tiene que\nestar libre nuestro puerto local (hace algo similar a una redirección).\nEste mapeo se hace a la hora de crear el contenedor no al construir la imagen,\nes decir, el mapeo de puertos no se hace en nuestra fase de docker build, si no\nen la fase de docker create/docker run de la siguiente manera:\n\ndocker run -p ptoLocal:ptoContenedor IDimagen\n\n\n\n\nSi ahora hacemos un docker ps, veremos que tenemos algo en la columna PORTS\n\n\n\nCon esto ya tendríamos nuestra app funcionando perfectamente, solo tenemos que\nentrar a localhost:5000 en nuestro navegador y voilá.","html":"<!--kg-card-begin: markdown--><p>Ahora vamos a empezar a trabajar con lo que se conoce como <strong>Dockerfile</strong>, que básicamente es un fichero con la configuración que queremos para un contenedor.</p>\n<p>Lo primero nos creamos un fichero llamado <strong>Dockerfile</strong>(sin extension)</p>\n<pre><code class=\"language-docker\"># Indicamos una imagen base de la que partir. Usamos alpine que es una básica sin prácticamente nada\nFROM alpine\n\n#Descargamos e instalamos las dependencias que queramos. Usamos el copando apk que es un gestor de paquetes que viene dentro de la imagen de alpine\nRUN apk add --update redis\n\n# Le indicamos a la imagen que hacer o que comandos ejecutar cuando arranque\nCMD [&quot;redis-server&quot;]\n</code></pre>\n<p>Nos situamos en la consola (o linea de comandos), en la carpeta donde tenemos el fichero y ejecutamos</p>\n<pre><code class=\"language-shell\">docker build .\n</code></pre>\n<p>Esto nos ejecuta los comandos que tiene nuestro fichero. Para que entendamos el proceso, si miramos detenidamente el log de salida del comando veremos como en cada paso nos genera una imagen intermedia, ejecuta el comando del paso que le toque crea un <strong>snapshot</strong> para el siguiente paso y elimina la imagen anterior<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.29.37.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.29.37\"></p>\n<p>Por último vemos que nos pone <strong>Successfully build .....</strong>, ese ID que nos aparece seria el nombre de nuestra imagen creada, a continuación solo tenemos que ejecutar</p>\n<pre><code class=\"language-shell\">docker run IDimagen\n</code></pre>\n<p>Y veremos como se ejecuta perfectamente nuestro contenedor<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.32.26.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.32.26\"></p>\n<p>Y si miramos que se esta ejecutando, veremos un nuevo contenedor con un nombre aleatorio que parte de nuestra imagen anteriormente creada<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.37.28.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.37.28\"></p>\n<h3 id=\"usandolacach\">Usando la Caché</h3>\n<p>Si rehusamos un <strong>Dockerfile</strong> o creamo uno nuevo igual que otro o con los mismo pasos iniciales, Docker usará su caché para no tener que repetir procesos que ya ha ejecutado, veamoslo con un ejemplo. Si añadimos otra dependencia a la instalación:</p>\n<pre><code class=\"language-docker\">FROM alpine\n\nRUN apk add --update redis\nRUN apk add --update gcc\nRUN apk add --update vim\n\nCMD [&quot;redis-server&quot;]\n</code></pre>\n<p>Y ejecutamos de nuevo el comando <strong>build</strong>, veremos como en los pasos iniciales nos sale <strong>Using cache</strong>, en algunas lineas, concretamente en las que se han repetido. <strong>OJO solo aparece hasta que llega a la línea que ha cambiado del Dockerfile</strong>, una vez que encuentra algo distinto ya lo hace todo de nuevo (si cambiaramos el orden de los comandos RUN lo haría todo de nuevo)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.56.09.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.56.09\"></p>\n<h3 id=\"aadirtagaimagen\">Añadir TAG a imagen</h3>\n<p>Si nos fijamos cuando se termina de construir la imagen de nuestro contenedor, nos pone un ID cualquiera, algo que es dificil de recordar. Es posible indicarle un nombre identificativo a nuestro contenedor, para ello basta con:</p>\n<pre><code class=\"language-shell\">docker build -t aliasDockerHub/nombreImagen:latest .\n</code></pre>\n<p>Con el flag <strong>t</strong> indicamos que queremos <strong>taggear</strong> la imagen creada con un nombre, el cual es necesario que tenga la estructura que veis con el alias que tenemos en el marketplace de <strong>DockerHub</strong></p>\n<h3 id=\"crearimagenapartirdeuncontenedorexistente\">Crear Imagen a partir de un contenedor existente</h3>\n<p>La idea es configurar un contenedor como queramos y luego generar una imagen de ese contenedor para poder desplegarla. <strong>Es necesario indicarle cual el es comando de inicio</strong></p>\n<pre><code class=\"language-shell\">docker commit -c 'CMD[&quot;redis-server&quot;]' idContenedor\n</code></pre>\n<p>Con este comando generaríamos una imagen que arrancaría con el comando <strong>redis-server</strong>. Este nos genera una imagen con un id gigantesco, pero para usarla no tenemos que copiar todo el id con una parte inicial que sea única y ya docker la busca.</p>\n<h3 id=\"creandoimagenpropia20\">Creando imagen propia 2.0</h3>\n<p>Hasta ahora hemos visto como crear una imagen usando otra como base e instalando algún paquete extra, pero....y ¿si queremos añadir algún desarrollo o fichero nuestro? Vamos a crear una mini aplicación en <strong>NodeJS</strong> que nos devuelva una página. Para ello vamos a crearnos una nueva carpeta para almacenar todos los ficheros de este contenedor, por ejemplo la llamaremos <strong>miniapp</strong>.<br>\nDentro de esta carpeta lo primero nos creamos un fichero <strong>package.json</strong> que contendrá las dependencias y scripts de nuestro aplicación en NodeJS (quién no sepa de que estamos hablando puede revisarse los post de NodeJS donde lo explico todo con más detalle). Dentro de este fichero ponemos:</p>\n<pre><code class=\"language-json\">{\n  &quot;dependencies&quot;: {\n    &quot;express&quot;: &quot;*&quot;\n  },\n  &quot;scripts&quot;: {\n    &quot;start&quot;: &quot;node index.js&quot;\n  }\n}\n</code></pre>\n<p>Aquí le estamos diciendo que tenemos como dependencias cualquier versión de express y un script de npm para arrancar el fichero <strong>index.js</strong>.<br>\nAhora como os podéis imaginar tenemos que crear ese fichero, el <strong>index.js</strong></p>\n<pre><code class=\"language-javascript\">const express = require('express');\n \nconst app = express();\n \napp.get('/', (req, res) =&gt; {\n  res.send('How are you doing');\n});\n \napp.listen(8080, () =&gt; {\n  console.log('Listening on port 8080');\n});\n</code></pre>\n<p>Ya tenemos nuestro proyecto de node preparado, ahora vamos a crear nuestro fichero <strong>Dockerfile</strong> para indicarle los pasos necesarios</p>\n<pre><code class=\"language-docker\">#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)\nFROM node:alpine\n\n#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor\nCOPY ./ ./\n#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json\nRUN npm install\n\n# Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node\nCMD [&quot;npm&quot;,&quot;start&quot;]\n</code></pre>\n<p>Ahora tenemos como comando nuevo <strong>COPY</strong> este comando copia contenido de una carpeta local (relativa a la ejecución del comando de docker build) a una carpeta en el propio contenedor.</p>\n<h3 id=\"cambiandoeldirectoriodeejecucinenelcontenedor\">Cambiando el directorio de ejecución en el contenedor</h3>\n<p>Con la configuración anterior, en el contenedor anterior todo se copia y se ejecuta en la raiz (/), eso en linux <strong>NO ESTÁ BIEEEEN</strong>, tenemos que indicarle un directorio en condiciones a nuestra aplicación o a nuestro ficheros. Para ello tenemos el comando <strong>WORKDIR</strong>:</p>\n<pre><code class=\"language-docker\">WORKDIR pathDondeTrabajar\n</code></pre>\n<p>Básicamente cambia el path desde el que se está trabajando en el Dockerfile, una ruta posible para usar puede ser dentro de <strong>/usr/app</strong> (si no existe, nos crea la carpeta), por lo que quedaría así nuestro Dockerfile</p>\n<pre><code class=\"language-docker\">#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)\nFROM node:alpine\n\n# Directorio desde el que funcionarian las siguientes instrucciones\nWORKDIR /usr/app\n\n#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor\nCOPY ./ ./\n\n#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json\nRUN npm install\n\n# Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node\nCMD [&quot;npm&quot;,&quot;start&quot;]\n</code></pre>\n<p>Ahora si hicieramos un build del contenedor, arrancaramos una imagen y nos conectaramos con <strong>exec -it</strong> podríamos ver que nos conectamos directamente al directorio que hemos indicado como <strong>working directory</strong>....peeeeeero realmente todavía no funcionaria nuestra aplicación, veamos lo último para hacerla funcionar.</p>\n<h3 id=\"mapearpuertoslocalesapuertosdecontenedor\">Mapear puertos locales a puertos de contenedor</h3>\n<p>El que tenga algunos conocimientos de <strong>node</strong> se habrá dado cuenta de que lo que hacemos es arrancar una aplicación en el puerto <strong>8080</strong>, pero claro, <strong>es el puerto 8080 de nuestro contenedor no de nuestro equipo</strong>, tenemos que pensar que un contenedor es prácticamente como un equipo independiente, como si hablaramos de una <strong>máquina virtual</strong>.</p>\n<p>Para poder hacer uso de un puerto del contenedor, tenemos que <strong>mapear</strong> un puerto local a uno del contenedor, no tienen porque ser iguales, pero si tiene que estar libre nuestro puerto local (hace algo similar a una redirección).<br>\nEste <strong>mapeo</strong> se hace a la hora de <strong>crear el contenedor no al construir la imagen</strong>, es decir, el mapeo de puertos no se hace en nuestra fase de <strong>docker build</strong>, si no en la fase de <strong>docker create/docker run</strong> de la siguiente manera:</p>\n<pre><code class=\"language-bash\">docker run -p ptoLocal:ptoContenedor IDimagen\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-08-a-las-9.47.32.png\" alt=\"Captura-de-pantalla-2018-11-08-a-las-9.47.32\"></p>\n<p>Si ahora hacemos un <strong>docker ps</strong>, veremos que tenemos algo en la columna <strong>PORTS</strong></p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-08-a-las-9.48.20.png\" alt=\"Captura-de-pantalla-2018-11-08-a-las-9.48.20\"></p>\n<p>Con esto ya tendríamos nuestra app funcionando perfectamente, solo tenemos que entrar a <strong>localhost:5000</strong> en nuestro navegador y voilá.</p>\n<!--kg-card-end: markdown-->","url":"https://jlgarcia.fulldev.ninja/docker-ii-creando-contenedores/","canonical_url":null,"uuid":"7b00f84e-a39f-4326-8e7b-1142e2b505ff","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"5be1df0c2dd6610fd828c9ed","reading_time":6,"send_email_when_published":false,"email_subject":null,"childHtmlRehype":{"html":"<!--kg-card-begin: markdown--><p>Ahora vamos a empezar a trabajar con lo que se conoce como <strong>Dockerfile</strong>, que básicamente es un fichero con la configuración que queremos para un contenedor.</p>\n<p>Lo primero nos creamos un fichero llamado <strong>Dockerfile</strong>(sin extension)</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token comment\"># Indicamos una imagen base de la que partir. Usamos alpine que es una básica sin prácticamente nada</span>\n<span class=\"token instruction\"><span class=\"token keyword\">FROM</span> alpine</span>\n\n<span class=\"token comment\">#Descargamos e instalamos las dependencias que queramos. Usamos el copando apk que es un gestor de paquetes que viene dentro de la imagen de alpine</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> apk add --update redis</span>\n\n<span class=\"token comment\"># Le indicamos a la imagen que hacer o que comandos ejecutar cuando arranque</span>\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">\"redis-server\"</span>]</span>\n</code></pre></div>\n<p>Nos situamos en la consola (o linea de comandos), en la carpeta donde tenemos el fichero y ejecutamos</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker build <span class=\"token builtin class-name\">.</span>\n</code></pre></div>\n<p>Esto nos ejecuta los comandos que tiene nuestro fichero. Para que entendamos el proceso, si miramos detenidamente el log de salida del comando veremos como en cada paso nos genera una imagen intermedia, ejecuta el comando del paso que le toque crea un <strong>snapshot</strong> para el siguiente paso y elimina la imagen anterior<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.29.37.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.29.37\"></p>\n<p>Por último vemos que nos pone <strong>Successfully build .....</strong>, ese ID que nos aparece seria el nombre de nuestra imagen creada, a continuación solo tenemos que ejecutar</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker run IDimagen\n</code></pre></div>\n<p>Y veremos como se ejecuta perfectamente nuestro contenedor<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.32.26.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.32.26\"></p>\n<p>Y si miramos que se esta ejecutando, veremos un nuevo contenedor con un nombre aleatorio que parte de nuestra imagen anteriormente creada<br>\n<img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.37.28.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.37.28\"></p>\n<h3 id=\"usandolacach\">Usando la Caché</h3>\n<p>Si rehusamos un <strong>Dockerfile</strong> o creamo uno nuevo igual que otro o con los mismo pasos iniciales, Docker usará su caché para no tener que repetir procesos que ya ha ejecutado, veamoslo con un ejemplo. Si añadimos otra dependencia a la instalación:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token instruction\"><span class=\"token keyword\">FROM</span> alpine</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> apk add --update redis</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> apk add --update gcc</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> apk add --update vim</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">\"redis-server\"</span>]</span>\n</code></pre></div>\n<p>Y ejecutamos de nuevo el comando <strong>build</strong>, veremos como en los pasos iniciales nos sale <strong>Using cache</strong>, en algunas lineas, concretamente en las que se han repetido. <strong>OJO solo aparece hasta que llega a la línea que ha cambiado del Dockerfile</strong>, una vez que encuentra algo distinto ya lo hace todo de nuevo (si cambiaramos el orden de los comandos RUN lo haría todo de nuevo)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.56.09.png\" alt=\"Captura-de-pantalla-2018-11-07-a-las-9.56.09\"></p>\n<h3 id=\"aadirtagaimagen\">Añadir TAG a imagen</h3>\n<p>Si nos fijamos cuando se termina de construir la imagen de nuestro contenedor, nos pone un ID cualquiera, algo que es dificil de recordar. Es posible indicarle un nombre identificativo a nuestro contenedor, para ello basta con:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker build -t aliasDockerHub/nombreImagen:latest <span class=\"token builtin class-name\">.</span>\n</code></pre></div>\n<p>Con el flag <strong>t</strong> indicamos que queremos <strong>taggear</strong> la imagen creada con un nombre, el cual es necesario que tenga la estructura que veis con el alias que tenemos en el marketplace de <strong>DockerHub</strong></p>\n<h3 id=\"crearimagenapartirdeuncontenedorexistente\">Crear Imagen a partir de un contenedor existente</h3>\n<p>La idea es configurar un contenedor como queramos y luego generar una imagen de ese contenedor para poder desplegarla. <strong>Es necesario indicarle cual el es comando de inicio</strong></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">docker commit -c <span class=\"token string\">'CMD[\"redis-server\"]'</span> idContenedor\n</code></pre></div>\n<p>Con este comando generaríamos una imagen que arrancaría con el comando <strong>redis-server</strong>. Este nos genera una imagen con un id gigantesco, pero para usarla no tenemos que copiar todo el id con una parte inicial que sea única y ya docker la busca.</p>\n<h3 id=\"creandoimagenpropia20\">Creando imagen propia 2.0</h3>\n<p>Hasta ahora hemos visto como crear una imagen usando otra como base e instalando algún paquete extra, pero....y ¿si queremos añadir algún desarrollo o fichero nuestro? Vamos a crear una mini aplicación en <strong>NodeJS</strong> que nos devuelva una página. Para ello vamos a crearnos una nueva carpeta para almacenar todos los ficheros de este contenedor, por ejemplo la llamaremos <strong>miniapp</strong>.<br>\nDentro de esta carpeta lo primero nos creamos un fichero <strong>package.json</strong> que contendrá las dependencias y scripts de nuestro aplicación en NodeJS (quién no sepa de que estamos hablando puede revisarse los post de NodeJS donde lo explico todo con más detalle). Dentro de este fichero ponemos:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"dependencies\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"express\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"*\"</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"start\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"node index.js\"</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre></div>\n<p>Aquí le estamos diciendo que tenemos como dependencias cualquier versión de express y un script de npm para arrancar el fichero <strong>index.js</strong>.<br>\nAhora como os podéis imaginar tenemos que crear ese fichero, el <strong>index.js</strong></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> express <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'express'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n \n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n \napp<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">send</span><span class=\"token punctuation\">(</span><span class=\"token string\">'How are you doing'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n \napp<span class=\"token punctuation\">.</span><span class=\"token function\">listen</span><span class=\"token punctuation\">(</span><span class=\"token number\">8080</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Listening on port 8080'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre></div>\n<p>Ya tenemos nuestro proyecto de node preparado, ahora vamos a crear nuestro fichero <strong>Dockerfile</strong> para indicarle los pasos necesarios</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token comment\">#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)</span>\n<span class=\"token instruction\"><span class=\"token keyword\">FROM</span> node:alpine</span>\n\n<span class=\"token comment\">#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> ./ ./</span>\n<span class=\"token comment\">#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> npm install</span>\n\n<span class=\"token comment\"># Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node</span>\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">\"npm\"</span>,<span class=\"token string\">\"start\"</span>]</span>\n</code></pre></div>\n<p>Ahora tenemos como comando nuevo <strong>COPY</strong> este comando copia contenido de una carpeta local (relativa a la ejecución del comando de docker build) a una carpeta en el propio contenedor.</p>\n<h3 id=\"cambiandoeldirectoriodeejecucinenelcontenedor\">Cambiando el directorio de ejecución en el contenedor</h3>\n<p>Con la configuración anterior, en el contenedor anterior todo se copia y se ejecuta en la raiz (/), eso en linux <strong>NO ESTÁ BIEEEEN</strong>, tenemos que indicarle un directorio en condiciones a nuestra aplicación o a nuestro ficheros. Para ello tenemos el comando <strong>WORKDIR</strong>:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> pathDondeTrabajar</span>\n</code></pre></div>\n<p>Básicamente cambia el path desde el que se está trabajando en el Dockerfile, una ruta posible para usar puede ser dentro de <strong>/usr/app</strong> (si no existe, nos crea la carpeta), por lo que quedaría así nuestro Dockerfile</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"docker\"><pre class=\"language-docker\"><code class=\"language-docker\"><span class=\"token comment\">#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)</span>\n<span class=\"token instruction\"><span class=\"token keyword\">FROM</span> node:alpine</span>\n\n<span class=\"token comment\"># Directorio desde el que funcionarian las siguientes instrucciones</span>\n<span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> /usr/app</span>\n\n<span class=\"token comment\">#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> ./ ./</span>\n\n<span class=\"token comment\">#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> npm install</span>\n\n<span class=\"token comment\"># Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node</span>\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">\"npm\"</span>,<span class=\"token string\">\"start\"</span>]</span>\n</code></pre></div>\n<p>Ahora si hicieramos un build del contenedor, arrancaramos una imagen y nos conectaramos con <strong>exec -it</strong> podríamos ver que nos conectamos directamente al directorio que hemos indicado como <strong>working directory</strong>....peeeeeero realmente todavía no funcionaria nuestra aplicación, veamos lo último para hacerla funcionar.</p>\n<h3 id=\"mapearpuertoslocalesapuertosdecontenedor\">Mapear puertos locales a puertos de contenedor</h3>\n<p>El que tenga algunos conocimientos de <strong>node</strong> se habrá dado cuenta de que lo que hacemos es arrancar una aplicación en el puerto <strong>8080</strong>, pero claro, <strong>es el puerto 8080 de nuestro contenedor no de nuestro equipo</strong>, tenemos que pensar que un contenedor es prácticamente como un equipo independiente, como si hablaramos de una <strong>máquina virtual</strong>.</p>\n<p>Para poder hacer uso de un puerto del contenedor, tenemos que <strong>mapear</strong> un puerto local a uno del contenedor, no tienen porque ser iguales, pero si tiene que estar libre nuestro puerto local (hace algo similar a una redirección).<br>\nEste <strong>mapeo</strong> se hace a la hora de <strong>crear el contenedor no al construir la imagen</strong>, es decir, el mapeo de puertos no se hace en nuestra fase de <strong>docker build</strong>, si no en la fase de <strong>docker create/docker run</strong> de la siguiente manera:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">docker run -p ptoLocal:ptoContenedor IDimagen\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-08-a-las-9.47.32.png\" alt=\"Captura-de-pantalla-2018-11-08-a-las-9.47.32\"></p>\n<p>Si ahora hacemos un <strong>docker ps</strong>, veremos que tenemos algo en la columna <strong>PORTS</strong></p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-08-a-las-9.48.20.png\" alt=\"Captura-de-pantalla-2018-11-08-a-las-9.48.20\"></p>\n<p>Con esto ya tendríamos nuestra app funcionando perfectamente, solo tenemos que entrar a <strong>localhost:5000</strong> en nuestro navegador y voilá.</p>\n<!--kg-card-end: markdown-->","htmlAst":{"type":"root","children":[{"type":"comment","value":"kg-card-begin: markdown"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora vamos a empezar a trabajar con lo que se conoce como "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Dockerfile"}]},{"type":"text","value":", que básicamente es un fichero con la configuración que queremos para un contenedor."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Lo primero nos creamos un fichero llamado "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Dockerfile"}]},{"type":"text","value":"(sin extension)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"# Indicamos una imagen base de la que partir. Usamos alpine que es una básica sin prácticamente nada"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" alpine"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"#Descargamos e instalamos las dependencias que queramos. Usamos el copando apk que es un gestor de paquetes que viene dentro de la imagen de alpine"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" apk add --update redis"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"# Le indicamos a la imagen que hacer o que comandos ejecutar cuando arranque"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"CMD"}]},{"type":"text","value":" ["},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"redis-server\""}]},{"type":"text","value":"]"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Nos situamos en la consola (o linea de comandos), en la carpeta donde tenemos el fichero y ejecutamos"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker build "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos ejecuta los comandos que tiene nuestro fichero. Para que entendamos el proceso, si miramos detenidamente el log de salida del comando veremos como en cada paso nos genera una imagen intermedia, ejecuta el comando del paso que le toque crea un "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"snapshot"}]},{"type":"text","value":" para el siguiente paso y elimina la imagen anterior"},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\n"},{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.29.37.png","alt":"Captura-de-pantalla-2018-11-07-a-las-9.29.37"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Por último vemos que nos pone "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Successfully build ....."}]},{"type":"text","value":", ese ID que nos aparece seria el nombre de nuestra imagen creada, a continuación solo tenemos que ejecutar"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker run IDimagen\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y veremos como se ejecuta perfectamente nuestro contenedor"},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\n"},{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.32.26.png","alt":"Captura-de-pantalla-2018-11-07-a-las-9.32.26"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y si miramos que se esta ejecutando, veremos un nuevo contenedor con un nombre aleatorio que parte de nuestra imagen anteriormente creada"},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\n"},{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.37.28.png","alt":"Captura-de-pantalla-2018-11-07-a-las-9.37.28"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"usandolacach"},"children":[{"type":"text","value":"Usando la Caché"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si rehusamos un "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Dockerfile"}]},{"type":"text","value":" o creamo uno nuevo igual que otro o con los mismo pasos iniciales, Docker usará su caché para no tener que repetir procesos que ya ha ejecutado, veamoslo con un ejemplo. Si añadimos otra dependencia a la instalación:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" alpine"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" apk add --update redis"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" apk add --update gcc"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" apk add --update vim"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"CMD"}]},{"type":"text","value":" ["},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"redis-server\""}]},{"type":"text","value":"]"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y ejecutamos de nuevo el comando "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"build"}]},{"type":"text","value":", veremos como en los pasos iniciales nos sale "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Using cache"}]},{"type":"text","value":", en algunas lineas, concretamente en las que se han repetido. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"OJO solo aparece hasta que llega a la línea que ha cambiado del Dockerfile"}]},{"type":"text","value":", una vez que encuentra algo distinto ya lo hace todo de nuevo (si cambiaramos el orden de los comandos RUN lo haría todo de nuevo)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-07-a-las-9.56.09.png","alt":"Captura-de-pantalla-2018-11-07-a-las-9.56.09"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"aadirtagaimagen"},"children":[{"type":"text","value":"Añadir TAG a imagen"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si nos fijamos cuando se termina de construir la imagen de nuestro contenedor, nos pone un ID cualquiera, algo que es dificil de recordar. Es posible indicarle un nombre identificativo a nuestro contenedor, para ello basta con:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker build -t aliasDockerHub/nombreImagen:latest "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con el flag "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"t"}]},{"type":"text","value":" indicamos que queremos "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"taggear"}]},{"type":"text","value":" la imagen creada con un nombre, el cual es necesario que tenga la estructura que veis con el alias que tenemos en el marketplace de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"DockerHub"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"crearimagenapartirdeuncontenedorexistente"},"children":[{"type":"text","value":"Crear Imagen a partir de un contenedor existente"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"La idea es configurar un contenedor como queramos y luego generar una imagen de ese contenedor para poder desplegarla. "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Es necesario indicarle cual el es comando de inicio"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"docker commit -c "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'CMD[\"redis-server\"]'"}]},{"type":"text","value":" idContenedor\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con este comando generaríamos una imagen que arrancaría con el comando "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"redis-server"}]},{"type":"text","value":". Este nos genera una imagen con un id gigantesco, pero para usarla no tenemos que copiar todo el id con una parte inicial que sea única y ya docker la busca."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"creandoimagenpropia20"},"children":[{"type":"text","value":"Creando imagen propia 2.0"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Hasta ahora hemos visto como crear una imagen usando otra como base e instalando algún paquete extra, pero....y ¿si queremos añadir algún desarrollo o fichero nuestro? Vamos a crear una mini aplicación en "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"NodeJS"}]},{"type":"text","value":" que nos devuelva una página. Para ello vamos a crearnos una nueva carpeta para almacenar todos los ficheros de este contenedor, por ejemplo la llamaremos "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"miniapp"}]},{"type":"text","value":"."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nDentro de esta carpeta lo primero nos creamos un fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"package.json"}]},{"type":"text","value":" que contendrá las dependencias y scripts de nuestro aplicación en NodeJS (quién no sepa de que estamos hablando puede revisarse los post de NodeJS donde lo explico todo con más detalle). Dentro de este fichero ponemos:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"json"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-json"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"dependencies\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"express\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"*\""}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"scripts\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","property"]},"children":[{"type":"text","value":"\"start\""}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"node index.js\""}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Aquí le estamos diciendo que tenemos como dependencias cualquier versión de express y un script de npm para arrancar el fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index.js"}]},{"type":"text","value":"."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nAhora como os podéis imaginar tenemos que crear ese fichero, el "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"index.js"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"javascript"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-javascript"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-javascript"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" express "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"require"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'express'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n \n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" app "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"express"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n \napp"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"get"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'/'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"req"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" res"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  res"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"send"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'How are you doing'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n \napp"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"listen"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"8080"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  console"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"log"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'Listening on port 8080'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ya tenemos nuestro proyecto de node preparado, ahora vamos a crear nuestro fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Dockerfile"}]},{"type":"text","value":" para indicarle los pasos necesarios"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" node:alpine"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" ./ ./"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" npm install"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"# Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"CMD"}]},{"type":"text","value":" ["},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"npm\""}]},{"type":"text","value":","},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"start\""}]},{"type":"text","value":"]"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora tenemos como comando nuevo "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" este comando copia contenido de una carpeta local (relativa a la ejecución del comando de docker build) a una carpeta en el propio contenedor."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"cambiandoeldirectoriodeejecucinenelcontenedor"},"children":[{"type":"text","value":"Cambiando el directorio de ejecución en el contenedor"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con la configuración anterior, en el contenedor anterior todo se copia y se ejecuta en la raiz (/), eso en linux "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"NO ESTÁ BIEEEEN"}]},{"type":"text","value":", tenemos que indicarle un directorio en condiciones a nuestra aplicación o a nuestro ficheros. Para ello tenemos el comando "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"WORKDIR"}]},{"type":"text","value":":"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"WORKDIR"}]},{"type":"text","value":" pathDondeTrabajar"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Básicamente cambia el path desde el que se está trabajando en el Dockerfile, una ruta posible para usar puede ser dentro de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"/usr/app"}]},{"type":"text","value":" (si no existe, nos crea la carpeta), por lo que quedaría así nuestro Dockerfile"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"docker"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-docker"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"#Imagen de la que partimos. En este caso usamos una imagen de node en su versión alpine (es decir con lo mínimo posible)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"FROM"}]},{"type":"text","value":" node:alpine"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"# Directorio desde el que funcionarian las siguientes instrucciones"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"WORKDIR"}]},{"type":"text","value":" /usr/app"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"#Comando nuevo. Con este comando copiamos los ficheros de un origen en nuestro equipo local a una ruta en el contenedor"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"COPY"}]},{"type":"text","value":" ./ ./"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"#Ejecutamos la instalación de las dependencias que hemos indicado en el package.json"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"RUN"}]},{"type":"text","value":" npm install"}]},{"type":"text","value":"\n\n"},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"# Le indicamos como comando de arranque el script que hemos indicado en el package.json que básicamente inicia nuestra aplicación en Node"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","instruction"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"CMD"}]},{"type":"text","value":" ["},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"npm\""}]},{"type":"text","value":","},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"\"start\""}]},{"type":"text","value":"]"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora si hicieramos un build del contenedor, arrancaramos una imagen y nos conectaramos con "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"exec -it"}]},{"type":"text","value":" podríamos ver que nos conectamos directamente al directorio que hemos indicado como "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"working directory"}]},{"type":"text","value":"....peeeeeero realmente todavía no funcionaria nuestra aplicación, veamos lo último para hacerla funcionar."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"mapearpuertoslocalesapuertosdecontenedor"},"children":[{"type":"text","value":"Mapear puertos locales a puertos de contenedor"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"El que tenga algunos conocimientos de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"node"}]},{"type":"text","value":" se habrá dado cuenta de que lo que hacemos es arrancar una aplicación en el puerto "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"8080"}]},{"type":"text","value":", pero claro, "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"es el puerto 8080 de nuestro contenedor no de nuestro equipo"}]},{"type":"text","value":", tenemos que pensar que un contenedor es prácticamente como un equipo independiente, como si hablaramos de una "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"máquina virtual"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Para poder hacer uso de un puerto del contenedor, tenemos que "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"mapear"}]},{"type":"text","value":" un puerto local a uno del contenedor, no tienen porque ser iguales, pero si tiene que estar libre nuestro puerto local (hace algo similar a una redirección)."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nEste "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"mapeo"}]},{"type":"text","value":" se hace a la hora de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"crear el contenedor no al construir la imagen"}]},{"type":"text","value":", es decir, el mapeo de puertos no se hace en nuestra fase de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker build"}]},{"type":"text","value":", si no en la fase de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker create/docker run"}]},{"type":"text","value":" de la siguiente manera:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"bash"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-bash"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-bash"]},"children":[{"type":"text","value":"docker run -p ptoLocal:ptoContenedor IDimagen\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-08-a-las-9.47.32.png","alt":"Captura-de-pantalla-2018-11-08-a-las-9.47.32"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si ahora hacemos un "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"docker ps"}]},{"type":"text","value":", veremos que tenemos algo en la columna "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"PORTS"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2018/11/Captura-de-pantalla-2018-11-08-a-las-9.48.20.png","alt":"Captura-de-pantalla-2018-11-08-a-las-9.48.20"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con esto ya tendríamos nuestra app funcionando perfectamente, solo tenemos que entrar a "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"localhost:5000"}]},{"type":"text","value":" en nuestro navegador y voilá."}]},{"type":"text","value":"\n"},{"type":"comment","value":"kg-card-end: markdown"}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"usandolacach","heading":"Usando la Caché"},{"id":"aadirtagaimagen","heading":"Añadir TAG a imagen"},{"id":"crearimagenapartirdeuncontenedorexistente","heading":"Crear Imagen a partir de un contenedor existente"},{"id":"creandoimagenpropia20","heading":"Creando imagen propia 2.0"},{"id":"cambiandoeldirectoriodeejecucinenelcontenedor","heading":"Cambiando el directorio de ejecución en el contenedor"},{"id":"mapearpuertoslocalesapuertosdecontenedor","heading":"Mapear puertos locales a puertos de contenedor"}]},"featureImageSharp":{"base":"docker_facebook_share-1.png","publicURL":"/static/8250fb6cd246dd294ed3fc4d99218f71/docker_facebook_share-1.png","imageMeta":{"width":336,"height":287},"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADmElEQVQ4y21Ua0wUVxQ+s8PMvbvgo0YlaWP8UZvYGK2JsU39Y2IwNcZWiI+aqt2YIGjLuhRdtYDFBXlUBbQtLnQXV80uuCi0FNvaovFBUdEpxl9qamtTAR8FY+sfKbC3350dwFU3+XJ2zr3nO995zBA989M3N5GeczyGvJ9t+vYfbOo7LmKlBjHvL2Q/LshxQpDuCsfhuZ90sqKLpGdHSN9UH8PWFjOBluEfB7IqtuO00x58RLy6R+EV1214JlZwllj+KdIkcdZTxLr7KPEv/iAtvTr2vOXbMSCcpXu+m6Z/ciyVlXUKVnihOfGsoIlCEK+8QbZFUL7zfIL6roe09TWoqnGUkBWcI1bcQXr+KY1520j3NL/FdnUIVnJlkJUYT1jZrwIqg9zXTfqnJ19in52pSFhe4AAhqam5LyAEGdt1ycZ2thEue1lh+1VWfHnQJCw1BlgpCIsudiFZPdQv4ftvCfhCLK91hZYVcsjK4giRVdE+Oow+hKayogtdrOyqVPcfLzWGoA7oHDRJAQyrin95+z7Khuo2wzbfyW0pGYTWjBLyypsqr7ghSy/nB+4Iu6/7SWJ1dzTBdzfKfN1RHsMQ93X185p7Ancf6+7IQlJ1xRxIdkSJL7nEUHlhOybWuhaBQqn9t598vdG0pj+jkw/9HdX8fVF7oG/AHvxngFf9JXBvG1pEUzAgbIQi1cWXnN2gUJqX6L3cJEdJx63XAr+L5S09Q3MiD4aYvzdqr30o7IFegU0QWJWqpHZBSW1C0V11ilwvuToj+2guJ9bmdW+z+kpuIyW7gzMqf7pm9DzoE+XGQzG+Gor2/dbPyzov69u/f98R6jfj5CC0TH/8YmtYRgk41bE5ESKnn1burpdHygRPw1z34dNLZxe3LCZnzRQyBMmVsh98ZNM+PqJiulAYllAtKwlDcssVaeGYCDuPNh4hWuWjWd5vaNlXP9KkrceIbQq/reU0JWuZteZbZMbFYkYAYcqoIyu8CLYcSJeksE76MEAOd10Kc4VXIOnn8E3H2Zuwk4FVwKvAG0CmPMPK0TDzGtgNQBZw0rqwGvABeVbQXuASgtJg9wMBoA5olSIg6GXzXY7JDq2HU5KmAg1IsBZWKm7AmQc2Gff2WiRS2ddAEMgH9lgVSnHKcC9kUzPgXGe14IOYWrNHS6DKhSQpKFsDFlilbgRmArPhS9SGP2PPftfMFoz8Dz3VcDm8+CFYqqyY2Nn/8hrXL6If80cAAAAASUVORK5CYII=","aspectRatio":1.174496644295302,"src":"/static/8250fb6cd246dd294ed3fc4d99218f71/5742b/docker_facebook_share-1.png","srcSet":"/static/8250fb6cd246dd294ed3fc4d99218f71/847ef/docker_facebook_share-1.png 175w,\n/static/8250fb6cd246dd294ed3fc4d99218f71/5742b/docker_facebook_share-1.png 336w","srcWebp":"/static/8250fb6cd246dd294ed3fc4d99218f71/8cfb4/docker_facebook_share-1.webp","srcSetWebp":"/static/8250fb6cd246dd294ed3fc4d99218f71/9fca7/docker_facebook_share-1.webp 175w,\n/static/8250fb6cd246dd294ed3fc4d99218f71/8cfb4/docker_facebook_share-1.webp 336w","sizes":"(max-width: 336px) 100vw, 336px"}}}},"allGhostPost":{"edges":[{"node":{"id":"Ghost__Post__600ac23ff73ef66cb4cf74dc","title":"Docker XIV - Kubernetes IX: Trabajando en Cloud","slug":"docker-xiii-kubernetes-viii-pensando-en-un-entorno-con-microservicios-usando-ingress-2","featured":false,"feature_image":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/learning-docker-and-k8s-by-practice-5.jpg","excerpt":"Bien hasta ahora hemos estado haciendo todo en nuestro entorno local de pruebas,\nveamos como podemos trabajar con un entorno en cloud desde nuestro terminal con\nkubectl\n\nKubectl es una herramienta de linea de comandos para trabajar con cualquier tipo\nde cluster de kubernetes, en nuestro caso nos hemos creado un minientorno de\npruebas usando minikube. Al hacer la instalación de minikube y kubectl\nautomáticamente se configura para que estemos usando ese entorno por defecto (el\nde minikube) pero no","custom_excerpt":null,"visibility":"public","created_at_pretty":"22 Jan 2021","published_at_pretty":"15 Feb 2021","updated_at_pretty":"15 Feb 2021","created_at":"2021-01-22T13:17:03.000+01:00","published_at":"2021-02-15T10:30:00.000+01:00","updated_at":"2021-02-15T10:30:00.000+01:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"Perfil.jpg","publicURL":"/static/b0de6281fb28a266510b3b09b9243e5a/Perfil.jpg","imageMeta":{"width":307,"height":307},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAUDBAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGzw6zC6zHn+cLYP//EAB0QAAICAQUAAAAAAAAAAAAAAAEDAAIEEyEiIzL/2gAIAQEAAQUCifca8KgcKWVfUpkHsG5pxX//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAdEAACAgEFAAAAAAAAAAAAAAAAARARcQISIUFR/9oACAEBAAY/AhU88xkb7N06a8P/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUFRYXH/2gAIAQEAAT8hR2pq40aqb+xIAeXibhW9JXr8joF4TBcSNe0//9oADAMBAAIAAwAAABDzDwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFhcfD/2gAIAQEAAT8QyItrELaTlatLwU63MvEW6vUNdy4LZQDn7iVApV9VLtANdWwKkuYq4Er1VZ//2Q==","aspectRatio":1,"src":"/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg","srcSet":"/static/b0de6281fb28a266510b3b09b9243e5a/f340b/Perfil.jpg 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/22d64/Perfil.jpg 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/aa249/Perfil.jpg 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/0dc33/Perfil.jpg 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/60667/Perfil.jpg 307w","srcWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp","srcSetWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/59cda/Perfil.webp 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/7da75/Perfil.webp 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f282e/Perfil.webp 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/a7b21/Perfil.webp 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f59af/Perfil.webp 307w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},"tags":[{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},{"slug":"kubernetes","url":"https://jlgarcia.fulldev.ninja/tag/kubernetes/","name":"kubernetes","visibility":"public","feature_image":null,"description":null,"meta_title":null,"meta_description":null,"featureImageSharp":null},{"slug":"cloud","url":"https://jlgarcia.fulldev.ninja/tag/cloud/","name":"cloud","visibility":"public","feature_image":null,"description":null,"meta_title":"Cloud Ninjas","meta_description":"Veremos temas relacionados con los tres sistemas cloud más usados: Azure, AWS y Firebase.","featureImageSharp":null}],"plaintext":"Bien hasta ahora hemos estado haciendo todo en nuestro entorno local de pruebas,\nveamos como podemos trabajar con un entorno en cloud desde nuestro terminal con\nkubectl\n\nKubectl es una herramienta de linea de comandos para trabajar con cualquier tipo\nde cluster de kubernetes, en nuestro caso nos hemos creado un minientorno de\npruebas usando minikube. Al hacer la instalación de minikube y kubectl\nautomáticamente se configura para que estemos usando ese entorno por defecto (el\nde minikube) pero nosotros podemos cambiarlo en cualquier comento.\n\nPara ver la configuración que tenemos actualmente podemos ejecutar el comando\n\nkubectl config view\n\nEsto nos debe mostrar algo similar a\n\nEn esta configuración tenemos 4 cosas importantes:\n\n * clusters: Información de todos los cluster que tenemos configurados, con su\n   certificado, su dirección o hostname  y el propio nombre que le hemos dado al\n   cluster en cuestión\n * contexts: Todos los contextos que tenemos configurados. Esto puede parecer\n   redundante, pero no es así, realmente nosotros trabajamos a través de los\n   contextos y estos apuntan a un cluster en específico. Esto es así porque\n   podemos tener distintos usuarios de acceso según el contexto y podemos querer\n   configurar un namespace diferente del de por defecto (namespace es solo una\n   separación lógica de objetos dentro de kubernetes)\n * current-context: El contexto seleccionado actualmente\n * users: Usuarios con sus nombres y sus métodos de acceso.\n\nEstos serían los puntos relevantes que tenemos que tener en  cuenta a la hora de\nintentar conectarnos a otro cluster. \n\nComo tal kubectl nos provee de los comandos necesarios para cambiar el contexto,\nver el contexto actual, configurar nuevo cluster, cualquier cosa que necesitemos\nrealmente. Por ejemplo:\n\nkubectl config current-context\n\nPara ver el contexto actualEstos serían todos los comandos bajo config\n\nVisto más o menos que nos ofrece kubectl para seleccionar una configuración u\notra veamos como hacer despliegue de kubernetes en Google\n\nHacia Google Cloud\nGoogle cloud es el servicio cloud que mejor funciona con kubernetes (nació allí\npor lo que tiene sentido no?). En este sentido es el servicio con el que más\nrápido podremos configurar nuestro cluster de kubernetes, vamos a por ello:\n\nPrimero sería darnos de alta en google cloud, si no tenéis cuenta sería el\nmomento de hacerlo. Una vez que tengamos nuestra cuenta lo que debemos hacer es\ninstalar la linea de comandos de google (gcloud). Buscar en internet como\nrealizar esta instalación en el caso de que no uséis brew para instalar\npaquetes, si lo tenéis solo es necesario hacer \n\nbrew install google-cloud-sdk\n\nPara empezar, lo primero que tenemos que hacer una vez instalado es iniciar la\nconfiguración de nuestro servicio:\n\ngcloud init\n\nCon este estableceremos una configuración nueva o seleccionaremos una existente\nsi ya la tenemos, aquí cada uno puede configurar lo que quiera y como quiera.\nComo tal yo tengo un proyecto creado que es: kuber-project-example\n\nSi en algún caso os falla cualquier comando, puede ser por la versión que ha\ninstalada brew del SDK, seguir estos pasos:\n\n * Cambiar versión de python de la console\n\nexport CLOUDSDK_PYTHON=python2\n\n * Actualizar los componentes\n\ngcloud components update\n\nCon esto ya os funcionaria todo (o debería). Continuemos:\n\nAhora lo que haremos será seleccionar ese proyecto como base para el resto de\ncomandos\n\ngcloud config set project kuber-project-example\n\nY también es necesaria tener una región de computo configurada\n\ngcloud config set compute/zone us-west1-a\n\nEn este punto puede que os pida seleccionar una cuenta de facturación (por si te\nvuelves loco creando cosas jajajajaja). Esto lo más fácil es irse a la consola\nde google, irse al menú de facturación y seleccionar una de las cuentas (o\ncrearla)\n\nUna vez seleccionada una cuenta en la consola de google, volvemos a ejecutar el\ncomando, nos pedirá habilitar la API de \"compute.googleapis.com\", la\nhabilitamos(tardará un poco) y ya nos confirmará que la configuración se ha\nactualizado o añadido\n\nA continuación creamos nuestro cluster de kubernetes que también puede que nos\nde error porque necesitamos habilitar el servicio de contenedores en nuestro\nproyecto\n\ngcloud container clusters create ninja-cluster --num-nodes=1\n\nPor lo que si nos aparece este error habilitaremos ese servicio también\n\ngcloud services enable container.googleapis.com\n\nY ya si todo va bien y no nos pide nada más ejecutamos de nuevo \n\ngcloud container clusters create ninja-cluster --num-nodes=1\n\nY ya cuando termine el proceso nos confirmará los datos pertinentes según lo que\nhayamos solicitado\n\nAhora lo siguiente es configurar nuestro kubectl local para que apunte a este\ncluster.... que lo podíamos hacer a mano, pero en esta caso google nos provee de\nun comando mágico que nos añade la configuración a la kube config\n\ngcloud container clusters get-credentials ninja-cluster\n\nBásicamente es solicitarle las credenciales sobre un cluster en específico y\nlisto\n\nVeamos la config\n\nkubectl config view\n\nComo vemos ahora tenemos otro cluster, otro contexto y otro usuario y si nos\nfijamos en nuestro current-context ya no estamos en minikube si no en el de\ngoogle\n\nVeamos que tenemos ahora con nuestro comando para ver los nodos\n\nkubectl get nodes\n\nAhora tenemos nuestro nodo de cluster de kubernetes que le hemos indicado que\ncree. Probemos ahora a crear un deploy, usaremos el de ejemplo de kubernetes que\nes una imagen básica de google\n\nkubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0\n\nY comprobemos que nos ha creado\n\nComo tal podemos ejecutar en este caso todos los comandos que hemos ido viendo\nhasta ahora. Añadamos un service para poder acceder a nuestro deploy, en este\ncaso vamos a añadir un service de tipo LoadBalancer (por fin jejejejje) que esto\nlo que hará será que google cloud nos creará un balanceador de carga y\naccederemos a nuestro pod desde él.\n\nUna vez que tengamos ip externa\n\nYa deberíamos poder acceder a nuestro pod, ojo en este caso tenemos que acceder\npor IP y al puerto 8080  (si habéis usado mi ejemplo porque el puerto del\ncomando ha sido el 8080)\n\nEn este caso podemos comprobar como de facil es trabajar con kubernetes en\ngoogle cloud y la configuración no es complicada. Si queremos ver que realmente\nno es mentira podemos irnos a la consola de kubernetes y ver que es lo que\ntenemos creado\n\nY ya para aseguraros de que no miento podemos ver que nos ha creado también un\nbalanceador de carga apuntando a nuestro cluster\n\nLista de balanceadores de cargaDetalles del balanceador de cargaCreo que queda\nmás o menos claro como trabajar con la cloud de google y kubernetes, que es\nrealmente sencillo. Con esto ya podríamos crear infraestructuras tan complejas\ncomo quisiéramos que con unos cuantos comandos ya se encarga google de hacerte\nla magia.\n\nY hasta aquí la parte de google cloud (NO SE OS OLVIDE BORRAR LAS COSAS DE\nGOOGLEEEEEE CLOUUUUD JEJEJJEJEJE)\n\nAWS\nPara hacerlo en aws...... pues realmente es más fácil que lo veáis directamente\nde la documentación porque es realmente igual de trivial, lo único que nos\nofrece 2 opciones:\n\n 1. Usar eksctl: Es una herramienta de línea de comandos específica de Amazon\n    EKS (servicio de kubernetes de AWS) que es bastante fácil de usar y a su vez\n    nos da la opción de crear nuestro cluster con Fargate o no. Ver esta opción\n    [https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html]\n 2. Usar el CLI de AWS y la consola de administración: Aquí se hace uso de \n    CloudFormation otro servicio interesante de AWS que nos facilita la\n    orquestación de algunas cosas, esta opción es algo más manual porque es\n    necesario manejar algunos permisos de IAM y cosas así pero se ve algo más de\n    que es lo que estamos montando. Ver esta opción\n    [https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html]\n\nAmbas opciones realmente son sencillas y para probar son más que suficientes,\nrecomiendo que intentar cualquiera de ellas y ver que es realmente lo que se\nmonta en la consola de AWS (BORRARLOOOOOO que al final cobran por tener el\nespacio en uso indefinidamente tras el año gratuito)\n\nBueno y hasta aquí lo que veremos de Docker y Kubernetes por el momento, han\nsido un montón de posts al respecto espero que os hayan parecido interesantes,\nse que nos faltaría ver como ser haría con Azure pero realmente es una\nplataforma que no me gusta mucho (es personal ojo no digo que sea mala ni nada\npor el estilo) por lo que no lo veremos aquí ni lo comentare, seguro que existe\nmuy buena documentación al respecto para quien esté interesado.\n\nClausuro esta serie de posts pero no descarto en ningún momento plantear más\ncosas sobre kubernetes y docker en un futuro cercano, sin mucho más que deciros\nNOS VEMOS EN EL SIGUIENTEEEEEEE un abraazoooooooo","html":"<p>Bien hasta ahora hemos estado haciendo todo en nuestro entorno local de pruebas, veamos como podemos trabajar con un entorno en cloud desde nuestro terminal con kubectl</p><p>Kubectl es una herramienta de linea de comandos para trabajar con cualquier tipo de cluster de kubernetes, en nuestro caso nos hemos creado un minientorno de pruebas usando <em>minikube.</em> Al hacer la instalación de minikube y kubectl automáticamente se configura para que estemos usando ese entorno por defecto (el de minikube) pero nosotros podemos cambiarlo en cualquier comento.</p><p>Para ver la configuración que tenemos actualmente podemos ejecutar el comando</p><pre><code class=\"language-shell\">kubectl config view</code></pre><p>Esto nos debe mostrar algo similar a</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image.png 723w\" sizes=\"(min-width: 720px) 720px\"></figure><p>En esta configuración tenemos 4 cosas importantes:</p><ul><li><strong>clusters</strong>: Información de todos los cluster que tenemos configurados, con su certificado, su dirección o hostname  y el propio nombre que le hemos dado al cluster en cuestión</li><li><strong>contexts</strong>: Todos los contextos que tenemos configurados. Esto puede parecer redundante, pero no es así, realmente nosotros trabajamos a través de los contextos y estos apuntan a un cluster en específico. Esto es así porque podemos tener distintos usuarios de acceso según el contexto y podemos querer configurar un <em>namespace</em> diferente del de por defecto (namespace es solo una separación lógica de objetos dentro de kubernetes)</li><li><strong>current-context</strong>: El contexto seleccionado actualmente</li><li><strong>users</strong>: Usuarios con sus nombres y sus métodos de acceso.</li></ul><p>Estos serían los puntos relevantes que tenemos que tener en  cuenta a la hora de intentar conectarnos a otro cluster. </p><p>Como tal <em>kubectl nos provee de los comandos necesarios para cambiar el contexto, ver el contexto actual, configurar nuevo cluster, cualquier cosa que necesitemos realmente. Por ejemplo:</em></p><figure class=\"kg-card kg-code-card\"><pre><code class=\"language-shell\">kubectl config current-context</code></pre><figcaption>Para ver el contexto actual</figcaption></figure><p>Estos serían todos los comandos bajo <strong>config</strong></p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-1.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-1.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-1.png 773w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Visto más o menos que nos ofrece kubectl para seleccionar una configuración u otra veamos como hacer despliegue de kubernetes en Google</p><h2 id=\"hacia-google-cloud\">Hacia Google Cloud</h2><p>Google cloud es el servicio cloud que mejor funciona con kubernetes (nació allí por lo que tiene sentido no?). En este sentido es el servicio con el que más rápido podremos configurar nuestro cluster de kubernetes, vamos a por ello:</p><p>Primero sería darnos de alta en google cloud, si no tenéis cuenta sería el momento de hacerlo. Una vez que tengamos nuestra cuenta lo que debemos hacer es instalar la linea de comandos de google (gcloud). Buscar en internet como realizar esta instalación en el caso de que no uséis <em>brew</em> para instalar paquetes, si lo tenéis solo es necesario hacer </p><pre><code class=\"language-shell\">brew install google-cloud-sdk</code></pre><p>Para empezar, lo primero que tenemos que hacer una vez instalado es iniciar la configuración de nuestro servicio:</p><pre><code class=\"language-shell\">gcloud init</code></pre><p>Con este estableceremos una configuración nueva o seleccionaremos una existente si ya la tenemos, aquí cada uno puede configurar lo que quiera y como quiera. Como tal yo tengo un proyecto creado que es: <strong>kuber-project-example</strong></p><p>Si en algún caso os falla cualquier comando, puede ser por la versión que ha instalada brew del SDK, seguir estos pasos:</p><ul><li>Cambiar versión de python de la console</li></ul><pre><code class=\"language-shell\">export CLOUDSDK_PYTHON=python2</code></pre><ul><li>Actualizar los componentes</li></ul><pre><code class=\"language-shell\">gcloud components update</code></pre><p>Con esto ya os funcionaria todo (o debería). Continuemos:</p><p>Ahora lo que haremos será seleccionar ese proyecto como base para el resto de comandos</p><pre><code class=\"language-shell\">gcloud config set project kuber-project-example</code></pre><p>Y también es necesaria tener una región de computo configurada</p><pre><code class=\"language-shell\">gcloud config set compute/zone us-west1-a</code></pre><p>En este punto puede que os pida seleccionar una cuenta de facturación (por si te vuelves loco creando cosas jajajajaja). Esto lo más fácil es irse a la consola de google, irse al menú de facturación y seleccionar una de las cuentas (o crearla)</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-2.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-2.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-2.png 765w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Una vez seleccionada una cuenta en la consola de google, volvemos a ejecutar el comando, nos pedirá habilitar la API de \"compute.googleapis.com\", la habilitamos(tardará un poco) y ya nos confirmará que la configuración se ha actualizado o añadido</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-3.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-3.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-3.png 926w\" sizes=\"(min-width: 720px) 720px\"></figure><p>A continuación creamos nuestro cluster de kubernetes que también puede que nos de error porque necesitamos habilitar el servicio de contenedores en nuestro proyecto</p><pre><code class=\"language-shell\">gcloud container clusters create ninja-cluster --num-nodes=1</code></pre><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-4.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-4.png 600w, https://jlgarcia.fulldev.ninja/assets/images/size/w1000/2021/01/image-4.png 1000w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-4.png 1158w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Por lo que si nos aparece este error habilitaremos ese servicio también</p><pre><code class=\"language-shell\">gcloud services enable container.googleapis.com</code></pre><p>Y ya si todo va bien y no nos pide nada más ejecutamos de nuevo </p><pre><code class=\"language-shell\">gcloud container clusters create ninja-cluster --num-nodes=1</code></pre><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-5.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-5.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-5.png 811w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Y ya cuando termine el proceso nos confirmará los datos pertinentes según lo que hayamos solicitado</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-6.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-6.png 600w, https://jlgarcia.fulldev.ninja/assets/images/size/w1000/2021/01/image-6.png 1000w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-6.png 1015w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Ahora lo siguiente es configurar nuestro kubectl local para que apunte a este cluster.... que lo podíamos hacer a mano, pero en esta caso google nos provee de un comando mágico que nos añade la configuración a la kube config</p><pre><code class=\"language-shell\">gcloud container clusters get-credentials ninja-cluster</code></pre><p>Básicamente es solicitarle las credenciales sobre un cluster en específico y listo</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-7.png\" class=\"kg-image\" alt></figure><p>Veamos la config</p><pre><code class=\"language-shell\">kubectl config view</code></pre><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-8.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-8.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-8.png 627w\"></figure><p>Como vemos ahora tenemos otro cluster, otro contexto y otro usuario y si nos fijamos en nuestro <strong>current-context </strong>ya no estamos en minikube si no en el de google</p><p>Veamos que tenemos ahora con nuestro comando para ver los nodos</p><pre><code class=\"language-shell\">kubectl get nodes</code></pre><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-9.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-9.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-9.png 829w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Ahora tenemos nuestro nodo de cluster de kubernetes que le hemos indicado que cree. Probemos ahora a crear un deploy, usaremos el de ejemplo de kubernetes que es una imagen básica de google</p><pre><code class=\"language-shell\">kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0</code></pre><p>Y comprobemos que nos ha creado</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-10.png\" class=\"kg-image\" alt></figure><p>Como tal podemos ejecutar en este caso todos los comandos que hemos ido viendo hasta ahora. Añadamos un service para poder acceder a nuestro deploy, en este caso vamos a añadir un <strong>service de tipo LoadBalancer</strong> (por fin jejejejje) que esto lo que hará será que google cloud nos creará un balanceador de carga y accederemos a nuestro pod desde él.</p><p>Una vez que tengamos ip externa</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-11.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-11.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-11.png 700w\"></figure><p>Ya deberíamos poder acceder a nuestro pod, ojo en este caso tenemos que acceder por IP y al puerto 8080  (si habéis usado mi ejemplo porque el puerto del comando ha sido el 8080)</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-12.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-12.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-12.png 606w\"></figure><p>En este caso podemos comprobar como de facil es trabajar con kubernetes en google cloud y la configuración no es complicada. Si queremos ver que realmente no es mentira podemos irnos a la consola de kubernetes y ver que es lo que tenemos creado</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png 684w\"></figure><p>Y ya para aseguraros de que no miento podemos ver que nos ha creado también un balanceador de carga apuntando a nuestro cluster</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png 993w\" sizes=\"(min-width: 720px) 720px\"><figcaption>Lista de balanceadores de carga</figcaption></figure><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png\" class=\"kg-image\" alt srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png 778w\" sizes=\"(min-width: 720px) 720px\"><figcaption>Detalles del balanceador de carga</figcaption></figure><p>Creo que queda más o menos claro como trabajar con la cloud de google y kubernetes, que es realmente sencillo. Con esto ya podríamos crear infraestructuras tan complejas como quisiéramos que con unos cuantos comandos ya se encarga google de hacerte la magia.</p><p>Y hasta aquí la parte de google cloud<strong> (NO SE OS OLVIDE BORRAR LAS COSAS DE GOOGLEEEEEE CLOUUUUD JEJEJJEJEJE)</strong></p><h2 id=\"aws\">AWS</h2><p>Para hacerlo en aws...... pues realmente es más fácil que lo veáis directamente de la documentación porque es realmente igual de trivial, lo único que nos ofrece 2 opciones:</p><ol><li><strong>Usar eksctl: </strong>Es una herramienta de línea de comandos específica de <strong>Amazon EKS </strong>(servicio de kubernetes de AWS) que es bastante fácil de usar y a su vez nos da la opción de crear nuestro cluster con <strong>Fargate</strong> o no. <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html\">Ver esta opción</a></li><li><strong>Usar el CLI de AWS y la consola de administración: </strong>Aquí se hace uso de <strong>CloudFormation</strong> otro servicio interesante de AWS que nos facilita la orquestación de algunas cosas, esta opción es algo más manual porque es necesario manejar algunos permisos de IAM y cosas así pero se ve algo más de que es lo que estamos montando. <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html\">Ver esta opción</a></li></ol><p>Ambas opciones realmente son sencillas y para probar son más que suficientes, recomiendo que intentar cualquiera de ellas y ver que es realmente lo que se monta en la consola de AWS <strong>(BORRARLOOOOOO que al final cobran por tener el espacio en uso indefinidamente tras el año gratuito)</strong></p><p>Bueno y hasta aquí lo que veremos de Docker y Kubernetes por el momento, han sido un montón de posts al respecto espero que os hayan parecido interesantes, se que nos faltaría ver como ser haría con Azure pero realmente es una plataforma que no me gusta mucho (es personal ojo no digo que sea mala ni nada por el estilo) por lo que no lo veremos aquí ni lo comentare, seguro que existe muy buena documentación al respecto para quien esté interesado.</p><p>Clausuro esta serie de posts pero no descarto en ningún momento plantear más cosas sobre kubernetes y docker en un futuro cercano, sin mucho más que deciros NOS VEMOS EN EL SIGUIENTEEEEEEE un abraazoooooooo</p>","url":"https://jlgarcia.fulldev.ninja/docker-xiii-kubernetes-viii-pensando-en-un-entorno-con-microservicios-usando-ingress-2/","canonical_url":null,"uuid":"a4c45341-2549-49c8-b416-d4448f1ee6f0","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"600ac23ff73ef66cb4cf74dc","reading_time":7,"send_email_when_published":false,"email_subject":null,"childHtmlRehype":{"html":"<p>Bien hasta ahora hemos estado haciendo todo en nuestro entorno local de pruebas, veamos como podemos trabajar con un entorno en cloud desde nuestro terminal con kubectl</p><p>Kubectl es una herramienta de linea de comandos para trabajar con cualquier tipo de cluster de kubernetes, en nuestro caso nos hemos creado un minientorno de pruebas usando <em>minikube.</em> Al hacer la instalación de minikube y kubectl automáticamente se configura para que estemos usando ese entorno por defecto (el de minikube) pero nosotros podemos cambiarlo en cualquier comento.</p><p>Para ver la configuración que tenemos actualmente podemos ejecutar el comando</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl config view</code></pre></div><p>Esto nos debe mostrar algo similar a</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image.png 723w\" sizes=\"(min-width: 720px) 720px\"></figure><p>En esta configuración tenemos 4 cosas importantes:</p><ul><li><strong>clusters</strong>: Información de todos los cluster que tenemos configurados, con su certificado, su dirección o hostname  y el propio nombre que le hemos dado al cluster en cuestión</li><li><strong>contexts</strong>: Todos los contextos que tenemos configurados. Esto puede parecer redundante, pero no es así, realmente nosotros trabajamos a través de los contextos y estos apuntan a un cluster en específico. Esto es así porque podemos tener distintos usuarios de acceso según el contexto y podemos querer configurar un <em>namespace</em> diferente del de por defecto (namespace es solo una separación lógica de objetos dentro de kubernetes)</li><li><strong>current-context</strong>: El contexto seleccionado actualmente</li><li><strong>users</strong>: Usuarios con sus nombres y sus métodos de acceso.</li></ul><p>Estos serían los puntos relevantes que tenemos que tener en  cuenta a la hora de intentar conectarnos a otro cluster. </p><p>Como tal <em>kubectl nos provee de los comandos necesarios para cambiar el contexto, ver el contexto actual, configurar nuevo cluster, cualquier cosa que necesitemos realmente. Por ejemplo:</em></p><figure class=\"kg-card kg-code-card\"><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl config current-context</code></pre></div><figcaption>Para ver el contexto actual</figcaption></figure><p>Estos serían todos los comandos bajo <strong>config</strong></p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-1.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-1.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-1.png 773w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Visto más o menos que nos ofrece kubectl para seleccionar una configuración u otra veamos como hacer despliegue de kubernetes en Google</p><h2 id=\"hacia-google-cloud\">Hacia Google Cloud</h2><p>Google cloud es el servicio cloud que mejor funciona con kubernetes (nació allí por lo que tiene sentido no?). En este sentido es el servicio con el que más rápido podremos configurar nuestro cluster de kubernetes, vamos a por ello:</p><p>Primero sería darnos de alta en google cloud, si no tenéis cuenta sería el momento de hacerlo. Una vez que tengamos nuestra cuenta lo que debemos hacer es instalar la linea de comandos de google (gcloud). Buscar en internet como realizar esta instalación en el caso de que no uséis <em>brew</em> para instalar paquetes, si lo tenéis solo es necesario hacer </p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">brew <span class=\"token function\">install</span> google-cloud-sdk</code></pre></div><p>Para empezar, lo primero que tenemos que hacer una vez instalado es iniciar la configuración de nuestro servicio:</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud init</code></pre></div><p>Con este estableceremos una configuración nueva o seleccionaremos una existente si ya la tenemos, aquí cada uno puede configurar lo que quiera y como quiera. Como tal yo tengo un proyecto creado que es: <strong>kuber-project-example</strong></p><p>Si en algún caso os falla cualquier comando, puede ser por la versión que ha instalada brew del SDK, seguir estos pasos:</p><ul><li>Cambiar versión de python de la console</li></ul><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\">CLOUDSDK_PYTHON</span><span class=\"token operator\">=</span>python2</code></pre></div><ul><li>Actualizar los componentes</li></ul><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud components update</code></pre></div><p>Con esto ya os funcionaria todo (o debería). Continuemos:</p><p>Ahora lo que haremos será seleccionar ese proyecto como base para el resto de comandos</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud config <span class=\"token builtin class-name\">set</span> project kuber-project-example</code></pre></div><p>Y también es necesaria tener una región de computo configurada</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud config <span class=\"token builtin class-name\">set</span> compute/zone us-west1-a</code></pre></div><p>En este punto puede que os pida seleccionar una cuenta de facturación (por si te vuelves loco creando cosas jajajajaja). Esto lo más fácil es irse a la consola de google, irse al menú de facturación y seleccionar una de las cuentas (o crearla)</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-2.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-2.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-2.png 765w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Una vez seleccionada una cuenta en la consola de google, volvemos a ejecutar el comando, nos pedirá habilitar la API de \"compute.googleapis.com\", la habilitamos(tardará un poco) y ya nos confirmará que la configuración se ha actualizado o añadido</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-3.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-3.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-3.png 926w\" sizes=\"(min-width: 720px) 720px\"></figure><p>A continuación creamos nuestro cluster de kubernetes que también puede que nos de error porque necesitamos habilitar el servicio de contenedores en nuestro proyecto</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud container clusters create ninja-cluster --num-nodes<span class=\"token operator\">=</span><span class=\"token number\">1</span></code></pre></div><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-4.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-4.png 600w, https://jlgarcia.fulldev.ninja/assets/images/size/w1000/2021/01/image-4.png 1000w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-4.png 1158w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Por lo que si nos aparece este error habilitaremos ese servicio también</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud services <span class=\"token builtin class-name\">enable</span> container.googleapis.com</code></pre></div><p>Y ya si todo va bien y no nos pide nada más ejecutamos de nuevo </p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud container clusters create ninja-cluster --num-nodes<span class=\"token operator\">=</span><span class=\"token number\">1</span></code></pre></div><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-5.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-5.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-5.png 811w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Y ya cuando termine el proceso nos confirmará los datos pertinentes según lo que hayamos solicitado</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-6.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-6.png 600w, https://jlgarcia.fulldev.ninja/assets/images/size/w1000/2021/01/image-6.png 1000w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-6.png 1015w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Ahora lo siguiente es configurar nuestro kubectl local para que apunte a este cluster.... que lo podíamos hacer a mano, pero en esta caso google nos provee de un comando mágico que nos añade la configuración a la kube config</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">gcloud container clusters get-credentials ninja-cluster</code></pre></div><p>Básicamente es solicitarle las credenciales sobre un cluster en específico y listo</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-7.png\" class=\"kg-image\" alt=\"\"></figure><p>Veamos la config</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl config view</code></pre></div><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-8.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-8.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-8.png 627w\"></figure><p>Como vemos ahora tenemos otro cluster, otro contexto y otro usuario y si nos fijamos en nuestro <strong>current-context </strong>ya no estamos en minikube si no en el de google</p><p>Veamos que tenemos ahora con nuestro comando para ver los nodos</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl get nodes</code></pre></div><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-9.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-9.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-9.png 829w\" sizes=\"(min-width: 720px) 720px\"></figure><p>Ahora tenemos nuestro nodo de cluster de kubernetes que le hemos indicado que cree. Probemos ahora a crear un deploy, usaremos el de ejemplo de kubernetes que es una imagen básica de google</p><div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl create deployment web --image<span class=\"token operator\">=</span>gcr.io/google-samples/hello-app:1.0</code></pre></div><p>Y comprobemos que nos ha creado</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-10.png\" class=\"kg-image\" alt=\"\"></figure><p>Como tal podemos ejecutar en este caso todos los comandos que hemos ido viendo hasta ahora. Añadamos un service para poder acceder a nuestro deploy, en este caso vamos a añadir un <strong>service de tipo LoadBalancer</strong> (por fin jejejejje) que esto lo que hará será que google cloud nos creará un balanceador de carga y accederemos a nuestro pod desde él.</p><p>Una vez que tengamos ip externa</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-11.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-11.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-11.png 700w\"></figure><p>Ya deberíamos poder acceder a nuestro pod, ojo en este caso tenemos que acceder por IP y al puerto 8080  (si habéis usado mi ejemplo porque el puerto del comando ha sido el 8080)</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-12.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-12.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-12.png 606w\"></figure><p>En este caso podemos comprobar como de facil es trabajar con kubernetes en google cloud y la configuración no es complicada. Si queremos ver que realmente no es mentira podemos irnos a la consola de kubernetes y ver que es lo que tenemos creado</p><figure class=\"kg-card kg-image-card\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png 684w\"></figure><p>Y ya para aseguraros de que no miento podemos ver que nos ha creado también un balanceador de carga apuntando a nuestro cluster</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png 993w\" sizes=\"(min-width: 720px) 720px\"><figcaption>Lista de balanceadores de carga</figcaption></figure><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png\" class=\"kg-image\" alt=\"\" srcset=\"https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png 600w, https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png 778w\" sizes=\"(min-width: 720px) 720px\"><figcaption>Detalles del balanceador de carga</figcaption></figure><p>Creo que queda más o menos claro como trabajar con la cloud de google y kubernetes, que es realmente sencillo. Con esto ya podríamos crear infraestructuras tan complejas como quisiéramos que con unos cuantos comandos ya se encarga google de hacerte la magia.</p><p>Y hasta aquí la parte de google cloud<strong> (NO SE OS OLVIDE BORRAR LAS COSAS DE GOOGLEEEEEE CLOUUUUD JEJEJJEJEJE)</strong></p><h2 id=\"aws\">AWS</h2><p>Para hacerlo en aws...... pues realmente es más fácil que lo veáis directamente de la documentación porque es realmente igual de trivial, lo único que nos ofrece 2 opciones:</p><ol><li><strong>Usar eksctl: </strong>Es una herramienta de línea de comandos específica de <strong>Amazon EKS </strong>(servicio de kubernetes de AWS) que es bastante fácil de usar y a su vez nos da la opción de crear nuestro cluster con <strong>Fargate</strong> o no. <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html\">Ver esta opción</a></li><li><strong>Usar el CLI de AWS y la consola de administración: </strong>Aquí se hace uso de <strong>CloudFormation</strong> otro servicio interesante de AWS que nos facilita la orquestación de algunas cosas, esta opción es algo más manual porque es necesario manejar algunos permisos de IAM y cosas así pero se ve algo más de que es lo que estamos montando. <a href=\"https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html\">Ver esta opción</a></li></ol><p>Ambas opciones realmente son sencillas y para probar son más que suficientes, recomiendo que intentar cualquiera de ellas y ver que es realmente lo que se monta en la consola de AWS <strong>(BORRARLOOOOOO que al final cobran por tener el espacio en uso indefinidamente tras el año gratuito)</strong></p><p>Bueno y hasta aquí lo que veremos de Docker y Kubernetes por el momento, han sido un montón de posts al respecto espero que os hayan parecido interesantes, se que nos faltaría ver como ser haría con Azure pero realmente es una plataforma que no me gusta mucho (es personal ojo no digo que sea mala ni nada por el estilo) por lo que no lo veremos aquí ni lo comentare, seguro que existe muy buena documentación al respecto para quien esté interesado.</p><p>Clausuro esta serie de posts pero no descarto en ningún momento plantear más cosas sobre kubernetes y docker en un futuro cercano, sin mucho más que deciros NOS VEMOS EN EL SIGUIENTEEEEEEE un abraazoooooooo</p>","htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Bien hasta ahora hemos estado haciendo todo en nuestro entorno local de pruebas, veamos como podemos trabajar con un entorno en cloud desde nuestro terminal con kubectl"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Kubectl es una herramienta de linea de comandos para trabajar con cualquier tipo de cluster de kubernetes, en nuestro caso nos hemos creado un minientorno de pruebas usando "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"minikube."}]},{"type":"text","value":" Al hacer la instalación de minikube y kubectl automáticamente se configura para que estemos usando ese entorno por defecto (el de minikube) pero nosotros podemos cambiarlo en cualquier comento."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Para ver la configuración que tenemos actualmente podemos ejecutar el comando"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl config view"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos debe mostrar algo similar a"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image.png 723w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"En esta configuración tenemos 4 cosas importantes:"}]},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"clusters"}]},{"type":"text","value":": Información de todos los cluster que tenemos configurados, con su certificado, su dirección o hostname  y el propio nombre que le hemos dado al cluster en cuestión"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"contexts"}]},{"type":"text","value":": Todos los contextos que tenemos configurados. Esto puede parecer redundante, pero no es así, realmente nosotros trabajamos a través de los contextos y estos apuntan a un cluster en específico. Esto es así porque podemos tener distintos usuarios de acceso según el contexto y podemos querer configurar un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"namespace"}]},{"type":"text","value":" diferente del de por defecto (namespace es solo una separación lógica de objetos dentro de kubernetes)"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"current-context"}]},{"type":"text","value":": El contexto seleccionado actualmente"}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"users"}]},{"type":"text","value":": Usuarios con sus nombres y sus métodos de acceso."}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Estos serían los puntos relevantes que tenemos que tener en  cuenta a la hora de intentar conectarnos a otro cluster. "}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como tal "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"kubectl nos provee de los comandos necesarios para cambiar el contexto, ver el contexto actual, configurar nuevo cluster, cualquier cosa que necesitemos realmente. Por ejemplo:"}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-code-card"]},"children":[{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl config current-context"}]}]}]},{"type":"element","tagName":"figcaption","properties":{},"children":[{"type":"text","value":"Para ver el contexto actual"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Estos serían todos los comandos bajo "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"config"}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-1.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-1.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-1.png 773w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Visto más o menos que nos ofrece kubectl para seleccionar una configuración u otra veamos como hacer despliegue de kubernetes en Google"}]},{"type":"element","tagName":"h2","properties":{"id":"hacia-google-cloud"},"children":[{"type":"text","value":"Hacia Google Cloud"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Google cloud es el servicio cloud que mejor funciona con kubernetes (nació allí por lo que tiene sentido no?). En este sentido es el servicio con el que más rápido podremos configurar nuestro cluster de kubernetes, vamos a por ello:"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Primero sería darnos de alta en google cloud, si no tenéis cuenta sería el momento de hacerlo. Una vez que tengamos nuestra cuenta lo que debemos hacer es instalar la linea de comandos de google (gcloud). Buscar en internet como realizar esta instalación en el caso de que no uséis "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"brew"}]},{"type":"text","value":" para instalar paquetes, si lo tenéis solo es necesario hacer "}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"brew "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"install"}]},{"type":"text","value":" google-cloud-sdk"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Para empezar, lo primero que tenemos que hacer una vez instalado es iniciar la configuración de nuestro servicio:"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud init"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con este estableceremos una configuración nueva o seleccionaremos una existente si ya la tenemos, aquí cada uno puede configurar lo que quiera y como quiera. Como tal yo tengo un proyecto creado que es: "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"kuber-project-example"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si en algún caso os falla cualquier comando, puede ser por la versión que ha instalada brew del SDK, seguir estos pasos:"}]},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Cambiar versión de python de la console"}]}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"export"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","assign-left","variable"]},"children":[{"type":"text","value":"CLOUDSDK_PYTHON"}]},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":"python2"}]}]}]},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Actualizar los componentes"}]}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud components update"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con esto ya os funcionaria todo (o debería). Continuemos:"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora lo que haremos será seleccionar ese proyecto como base para el resto de comandos"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud config "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"set"}]},{"type":"text","value":" project kuber-project-example"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y también es necesaria tener una región de computo configurada"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud config "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"set"}]},{"type":"text","value":" compute/zone us-west1-a"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"En este punto puede que os pida seleccionar una cuenta de facturación (por si te vuelves loco creando cosas jajajajaja). Esto lo más fácil es irse a la consola de google, irse al menú de facturación y seleccionar una de las cuentas (o crearla)"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-2.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-2.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-2.png 765w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez seleccionada una cuenta en la consola de google, volvemos a ejecutar el comando, nos pedirá habilitar la API de \"compute.googleapis.com\", la habilitamos(tardará un poco) y ya nos confirmará que la configuración se ha actualizado o añadido"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-3.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-3.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-3.png 926w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A continuación creamos nuestro cluster de kubernetes que también puede que nos de error porque necesitamos habilitar el servicio de contenedores en nuestro proyecto"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud container clusters create ninja-cluster --num-nodes"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"1"}]}]}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-4.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-4.png 600w","https://jlgarcia.fulldev.ninja/assets/images/size/w1000/2021/01/image-4.png 1000w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-4.png 1158w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Por lo que si nos aparece este error habilitaremos ese servicio también"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud services "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"enable"}]},{"type":"text","value":" container.googleapis.com"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y ya si todo va bien y no nos pide nada más ejecutamos de nuevo "}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud container clusters create ninja-cluster --num-nodes"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"1"}]}]}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-5.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-5.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-5.png 811w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y ya cuando termine el proceso nos confirmará los datos pertinentes según lo que hayamos solicitado"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-6.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-6.png 600w","https://jlgarcia.fulldev.ninja/assets/images/size/w1000/2021/01/image-6.png 1000w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-6.png 1015w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora lo siguiente es configurar nuestro kubectl local para que apunte a este cluster.... que lo podíamos hacer a mano, pero en esta caso google nos provee de un comando mágico que nos añade la configuración a la kube config"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"gcloud container clusters get-credentials ninja-cluster"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Básicamente es solicitarle las credenciales sobre un cluster en específico y listo"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-7.png","className":["kg-image"],"alt":""},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Veamos la config"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl config view"}]}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-8.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-8.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-8.png 627w"]},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como vemos ahora tenemos otro cluster, otro contexto y otro usuario y si nos fijamos en nuestro "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"current-context "}]},{"type":"text","value":"ya no estamos en minikube si no en el de google"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Veamos que tenemos ahora con nuestro comando para ver los nodos"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl get nodes"}]}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-9.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-9.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-9.png 829w"],"sizes":"(min-width: 720px) 720px"},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora tenemos nuestro nodo de cluster de kubernetes que le hemos indicado que cree. Probemos ahora a crear un deploy, usaremos el de ejemplo de kubernetes que es una imagen básica de google"}]},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl create deployment web --image"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":"gcr.io/google-samples/hello-app:1.0"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y comprobemos que nos ha creado"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-10.png","className":["kg-image"],"alt":""},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como tal podemos ejecutar en este caso todos los comandos que hemos ido viendo hasta ahora. Añadamos un service para poder acceder a nuestro deploy, en este caso vamos a añadir un "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"service de tipo LoadBalancer"}]},{"type":"text","value":" (por fin jejejejje) que esto lo que hará será que google cloud nos creará un balanceador de carga y accederemos a nuestro pod desde él."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez que tengamos ip externa"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-11.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-11.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-11.png 700w"]},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ya deberíamos poder acceder a nuestro pod, ojo en este caso tenemos que acceder por IP y al puerto 8080  (si habéis usado mi ejemplo porque el puerto del comando ha sido el 8080)"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-12.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/image-12.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/image-12.png 606w"]},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"En este caso podemos comprobar como de facil es trabajar con kubernetes en google cloud y la configuración no es complicada. Si queremos ver que realmente no es mentira podemos irnos a la consola de kubernetes y ver que es lo que tenemos creado"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.49.33.png 684w"]},"children":[]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y ya para aseguraros de que no miento podemos ver que nos ha creado también un balanceador de carga apuntando a nuestro cluster"}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card","kg-card-hascaption"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.25.png 993w"],"sizes":"(min-width: 720px) 720px"},"children":[]},{"type":"element","tagName":"figcaption","properties":{},"children":[{"type":"text","value":"Lista de balanceadores de carga"}]}]},{"type":"element","tagName":"figure","properties":{"className":["kg-card","kg-image-card","kg-card-hascaption"]},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png","className":["kg-image"],"alt":"","srcSet":["https://jlgarcia.fulldev.ninja/assets/images/size/w600/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png 600w","https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-14.54.36.png 778w"],"sizes":"(min-width: 720px) 720px"},"children":[]},{"type":"element","tagName":"figcaption","properties":{},"children":[{"type":"text","value":"Detalles del balanceador de carga"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Creo que queda más o menos claro como trabajar con la cloud de google y kubernetes, que es realmente sencillo. Con esto ya podríamos crear infraestructuras tan complejas como quisiéramos que con unos cuantos comandos ya se encarga google de hacerte la magia."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y hasta aquí la parte de google cloud"},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":" (NO SE OS OLVIDE BORRAR LAS COSAS DE GOOGLEEEEEE CLOUUUUD JEJEJJEJEJE)"}]}]},{"type":"element","tagName":"h2","properties":{"id":"aws"},"children":[{"type":"text","value":"AWS"}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Para hacerlo en aws...... pues realmente es más fácil que lo veáis directamente de la documentación porque es realmente igual de trivial, lo único que nos ofrece 2 opciones:"}]},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Usar eksctl: "}]},{"type":"text","value":"Es una herramienta de línea de comandos específica de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Amazon EKS "}]},{"type":"text","value":"(servicio de kubernetes de AWS) que es bastante fácil de usar y a su vez nos da la opción de crear nuestro cluster con "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Fargate"}]},{"type":"text","value":" o no. "},{"type":"element","tagName":"a","properties":{"href":"https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html"},"children":[{"type":"text","value":"Ver esta opción"}]}]},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Usar el CLI de AWS y la consola de administración: "}]},{"type":"text","value":"Aquí se hace uso de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"CloudFormation"}]},{"type":"text","value":" otro servicio interesante de AWS que nos facilita la orquestación de algunas cosas, esta opción es algo más manual porque es necesario manejar algunos permisos de IAM y cosas así pero se ve algo más de que es lo que estamos montando. "},{"type":"element","tagName":"a","properties":{"href":"https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html"},"children":[{"type":"text","value":"Ver esta opción"}]}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ambas opciones realmente son sencillas y para probar son más que suficientes, recomiendo que intentar cualquiera de ellas y ver que es realmente lo que se monta en la consola de AWS "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"(BORRARLOOOOOO que al final cobran por tener el espacio en uso indefinidamente tras el año gratuito)"}]}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Bueno y hasta aquí lo que veremos de Docker y Kubernetes por el momento, han sido un montón de posts al respecto espero que os hayan parecido interesantes, se que nos faltaría ver como ser haría con Azure pero realmente es una plataforma que no me gusta mucho (es personal ojo no digo que sea mala ni nada por el estilo) por lo que no lo veremos aquí ni lo comentare, seguro que existe muy buena documentación al respecto para quien esté interesado."}]},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Clausuro esta serie de posts pero no descarto en ningún momento plantear más cosas sobre kubernetes y docker en un futuro cercano, sin mucho más que deciros NOS VEMOS EN EL SIGUIENTEEEEEEE un abraazoooooooo"}]}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"hacia-google-cloud","heading":"Hacia Google Cloud"},{"id":"aws","heading":"AWS"}]},"featureImageSharp":{"base":"learning-docker-and-k8s-by-practice-5.jpg","publicURL":"/static/a8e77ba5b1852585391f8ffab9564f47/learning-docker-and-k8s-by-practice-5.jpg","imageMeta":{"width":480,"height":270},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHXm8RRgf/EABoQAQABBQAAAAAAAAAAAAAAAAIBAxAREhP/2gAIAQEAAQUClrpUesGcm/8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhABAQEAAAAAAAAAAAAAAAAAAREg/9oACAEBAAY/AoBNf//EABsQAAICAwEAAAAAAAAAAAAAAAERACEgQWFx/9oACAEBAAE/IRg7EmEoQL9qLjT2sP/aAAwDAQACAAMAAAAQ8M//xAAWEQADAAAAAAAAAAAAAAAAAAAQEWH/2gAIAQMBAT8QVH//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEx/9oACAECAQE/EBpyP//EABoQAQEBAQADAAAAAAAAAAAAAAERADEQIUH/2gAIAQEAAT8QZBiMqOC0uIouvYaltghUH6eEHoOAOE3/2Q==","aspectRatio":1.7857142857142858,"src":"/static/a8e77ba5b1852585391f8ffab9564f47/e3903/learning-docker-and-k8s-by-practice-5.jpg","srcSet":"/static/a8e77ba5b1852585391f8ffab9564f47/477ba/learning-docker-and-k8s-by-practice-5.jpg 175w,\n/static/a8e77ba5b1852585391f8ffab9564f47/06776/learning-docker-and-k8s-by-practice-5.jpg 350w,\n/static/a8e77ba5b1852585391f8ffab9564f47/e3903/learning-docker-and-k8s-by-practice-5.jpg 480w","srcWebp":"/static/a8e77ba5b1852585391f8ffab9564f47/61162/learning-docker-and-k8s-by-practice-5.webp","srcSetWebp":"/static/a8e77ba5b1852585391f8ffab9564f47/9fca7/learning-docker-and-k8s-by-practice-5.webp 175w,\n/static/a8e77ba5b1852585391f8ffab9564f47/37a4e/learning-docker-and-k8s-by-practice-5.webp 350w,\n/static/a8e77ba5b1852585391f8ffab9564f47/61162/learning-docker-and-k8s-by-practice-5.webp 480w","sizes":"(max-width: 480px) 100vw, 480px"}}}}},{"node":{"id":"Ghost__Post__6004188da6c0f5058bff4dbd","title":"Docker XIII - Kubernetes VIII: Pensando en un entorno con microservicios usando INGRESS","slug":"docker-xiii-kubernetes-viii-pensando-en-un-entorno-con-microservicios-usando-ingress","featured":false,"feature_image":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/learning-docker-and-k8s-by-practice-4.jpg","excerpt":"Hasta ahora hemos planteado nuestro entorno pensando solo en una o varías\naplicaciones que realmente funcionan de manera autonóma, es decir, si pensamos\nen nuestro POD con el fork del juego de SUPER MARIO vemos como realmente no\ntiene comunicación con nada más y en un primer momento no la necesita (o\nnosotros a la hora de usarlo tampoco).\n\nAhora propongo otro escenario, como pudiera ser que tenemos un pequeño portal\ndonde el usuario necesita hacer login antes de poder acceder a nuestro juego. Si","custom_excerpt":null,"visibility":"public","created_at_pretty":"17 Jan 2021","published_at_pretty":"8 Feb 2021","updated_at_pretty":"8 Feb 2021","created_at":"2021-01-17T11:59:25.000+01:00","published_at":"2021-02-08T10:37:00.000+01:00","updated_at":"2021-02-08T10:37:00.000+01:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"Perfil.jpg","publicURL":"/static/b0de6281fb28a266510b3b09b9243e5a/Perfil.jpg","imageMeta":{"width":307,"height":307},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAUDBAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGzw6zC6zHn+cLYP//EAB0QAAICAQUAAAAAAAAAAAAAAAEDAAIEEyEiIzL/2gAIAQEAAQUCifca8KgcKWVfUpkHsG5pxX//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAdEAACAgEFAAAAAAAAAAAAAAAAARARcQISIUFR/9oACAEBAAY/AhU88xkb7N06a8P/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUFRYXH/2gAIAQEAAT8hR2pq40aqb+xIAeXibhW9JXr8joF4TBcSNe0//9oADAMBAAIAAwAAABDzDwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFhcfD/2gAIAQEAAT8QyItrELaTlatLwU63MvEW6vUNdy4LZQDn7iVApV9VLtANdWwKkuYq4Er1VZ//2Q==","aspectRatio":1,"src":"/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg","srcSet":"/static/b0de6281fb28a266510b3b09b9243e5a/f340b/Perfil.jpg 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/22d64/Perfil.jpg 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/aa249/Perfil.jpg 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/0dc33/Perfil.jpg 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/60667/Perfil.jpg 307w","srcWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp","srcSetWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/59cda/Perfil.webp 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/7da75/Perfil.webp 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f282e/Perfil.webp 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/a7b21/Perfil.webp 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f59af/Perfil.webp 307w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},"tags":[{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},{"slug":"kubernetes","url":"https://jlgarcia.fulldev.ninja/tag/kubernetes/","name":"kubernetes","visibility":"public","feature_image":null,"description":null,"meta_title":null,"meta_description":null,"featureImageSharp":null}],"plaintext":"Hasta ahora hemos planteado nuestro entorno pensando solo en una o varías\naplicaciones que realmente funcionan de manera autonóma, es decir, si pensamos\nen nuestro POD con el fork del juego de SUPER MARIO vemos como realmente no\ntiene comunicación con nada más y en un primer momento no la necesita (o\nnosotros a la hora de usarlo tampoco).\n\nAhora propongo otro escenario, como pudiera ser que tenemos un pequeño portal\ndonde el usuario necesita hacer login antes de poder acceder a nuestro juego. Si\nlo planteamos como si realmente fueran servicios distintos tendriamos por\nejemplo:\n\n * Lo que es el front, que no sería más que una miniweb con un login y a lo\n   mejor una lista de posibles juegos.\n * Puede que un microservicio que gestionaría los usuarios y nos permitiría\n   hacer login o un microservicio que fuera realmente la API con la lista de\n   juegos disponibles\n * Y por dejarlo simple un microservicio que sería el fork de nuestro juego de\n   SUPER MARIO y otro que puede ser un fork del SONIC por ejemplo.\n\nEs posible que este escenario no tenga ningún sentido ojo, por el tema de la\nautenticación y demás, es solo para que entendamos que tendríamos varios\nmicroservicios distintos que gestionar. Esto con los objetos y configuraciones\nque hemos visto hasta ahora puede que lo pudieramos conseguir si tenemos un\nobjeto de tipo SERVICE:NODEPORT por cada microservicio y el microservicio de\nfront sepa donde esta todo (superfeo) o a lo mejor hacerlo con subdominios y con\nDNS o cosas así (se me están ocurriendo sobre la marcha pueden no tener\nsentido). El caso es que a no ser que lo gestionaramos en un servicio cloud que\nnos pudiera hacer el enrutamiento no tenemos una forma clara de hacerlo. Bueno\npues para esto tenemos en kubernetes el objeto de tipo INGRESS.\n\nEste objeto lo que hace es definir como se deben gestionar las peticiones que\nvienen desde fuera de nuestro cluster, es decir, hacia donde debe redireccionar\ncada petición. Veamos una diagrama de la documentación oficial donde creo yo que\nse explica bastante bien cual es la tarea de nuestro INGRESS\n\n\n\nEste diagrama que vemos resume un escenario típico que tendríamos en producción:\n\n 1. El cliente envía una petición que lo primero que hace es llegar al\n    balanceador de carga de nuestro cloud. Este balanceador de carga está\n    manejado por un INGRESS CONTROLLER que es el que se encarga de asegurarse de\n    que se cumplan las reglas que definimos en nuestro objeto INGRESS\n 2. Según las reglas que hayamos definido nuestro INGRESS CONTROLLER decide a\n    que servicio (objeto de tipo SERVICE) de nuestro cluster debe enviar la\n    petición en cuestión\n 3. Por último este service envía la petición al primero de sus PODS que este\n    disponible\n\nUn detalle que no hemos dejado claro todavía es que las reglas que definimos en\nel objeto de tipo INGRESS no son manejadas realmente por kubernetes, si no que\nnecesitamos de un controlador externo (INGRESS CONTROLLER para que las gestione)\n. Como tal existen opciones de configuración de ingress disponibles para NGINX,\nHAPROXY, TRAEFIK... para la mayoría de los servicios de proxy, uno de los más\ntípicos es el de nginx. En un entorno en cloud seria necesario tener un POD con\nnginx que sería nuestro INGRESS CONTROLLER.\n\nComentar que el entorno completo en cloud con su loadbalancer, su ingress y\ndemás no lo veremos de momento, en la documentación oficial\n[https://kubernetes.io/docs/concepts/services-networking/ingress/] viene todo lo\nnecesario creo yo, por ahora con motivos puramente de prueba o académicos\nusaremos la opción que nos provee minikube para tener nuestro propio INGRESS\nCONTROLLER.\n\nVeamos lo primero un ejemplo de objeto tipo INGRESS, como no parece existir de\nmomento comando dry-run para verlo, nos tocará copiar un ejemplo como este\n\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: mario-ingress\nspec:\n  rules:\n  - host: mariogame.fulldev.ninja\n    http:\n      paths:\n      - path: /mario\n        pathType: Prefix\n        backend:\n          service:\n            name: mario-service\n            port:\n              number: 8000\n  defaultBackend:\n    service:\n      name: default-service\n      port:\n        number: 8001\n\n\nCreo que es bastante intuitivo lo que hace cada cosa pero hablemos un poco en\ndetalle de algunos puntos:\n\n * apiVersion: networking.k8s.io/v1: Aquí si nos fijamos tenemos un cambio\n   respecto a lo que soliamos indicar en el resto de objetos, en este caso\n   debemos especificar que pertenece a la api de networking de kubernetes.\n * spec: Como siempre dentro de spec tenemos siempre la definición de lo que\n   queremos hacer.\n * rules: Dentro de rules como su propio nombre indica es donde definiremos las\n   reglas que queremos usar dentro de nuestro ingress * host: hostname al cual enviará el cliente las peticiones\n    * http -> paths: Estos serán los paths que usará nuestro ingress controller\n      para redireccionar la petición a un servicio o a otro.\n    * pathType: Como se filtrarán los paths, el caso más típico es el de Prefix \n      que buscará coincidencias en el path completo separandolo por /\n    * backend -> service: El servicio que debe responder al path indicado\n   \n   \n * defaultBackend: Aquí definimos el servicio por defecto en caso de que no se\n   encontraran coincidencias en el resto de reglas\n\nComo siempre existen muchas más reglas pero estas son las básicas y que me han\nparecido relevantes para empezar (vuelvo a recomendar leer la documentación\noficial)\n\nUna vez visto lo básico, veamos como configurar un ingress controller en\nminikube (recordemos que en un entorno cloud tenemos que tener nuestro propio\npod que haga de controller).\nPara ello lo primero es hablar de los addons de minikube. Minikube dispone de\nciertos extras que podemos habilitar según nos insterese, uno de ellos es el \ningress controller, este ingress que nos provee minikube es una propia intancia\nde nginx preparada para este uso y que en este caso no podremos ver como uno de\nlos pods que hemos estado viendo hasta ahora (en un momento veremos lo que pasa)\n\nOJO esta parte solo funciona si has iniciado minikube con algún gestor de\nmáquinas virtuales como virtualbox doc oficial\n[https://minikube.sigs.k8s.io/docs/reference/drivers/virtualbox/]. Ver al final\ndel post el apartodo INICIAR MINIKUBE CON VIRTUALBOX o también es posible jugar\ncon este playground [https://labs.play-with-k8s.com/]\n\nPrimero veamos la lista de addons, para ello solo es necesario que ejecutemos\neste comando:\n\nminikube addons list\n\n\nEsto nos mostrará una lista de los addons disponibles indicándonos si están\nhabilitados o no (algunos estarán habilitados por defecto según nuestra\ninstalación)\n\n\n\nSi os fijáis uno de los addons que aparecen es el de ingress, habilitemoslo\n\nminikube addons enable ingress\n\n\n\n\nCon esto ya tendríamos habilitado nuestro ingress controller con nginx, pero\ncomo he comentado no nos aparece como un pod normal en este entorno.\n\n\n\nMinikube en este caso lo crea como un pod propio del sistema de minikube por lo\nque para verlo tendríamos que listar los pods que pertenecen a minikube, para\nello\n\nkubectl get pods -n kube-system\n\n\nY esto nos listará todos los pods que va creando minikube\n\n\n\nSi nos fijamos tenemos uno en específico que se llama nginx-controller.\n\nBien con esto ya tendríamos nuestro ingress controller, ahora vamos a trabajar\ncon los mismos ejemplos que la documentación oficial (el de mario da algunos\nproblemas con los direccionamientos y por no entrenernos), primero el deploy\n\nexample-deploy.yaml\n\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: web\n  name: web\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: web\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: web\n    spec:\n      containers:\n      - image: gcr.io/google-samples/hello-app:1.0\n        name: hello-app\n        resources: {}\nstatus: {}\n\n\nEl service\n\nmario-service.yaml\n\napiVersion: v1\nkind: Service\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: web\n  name: web\nspec:\n  ports:\n  - port: 8080\n    protocol: TCP\n    targetPort: 8080\n  selector:\n    app: web\n  type: NodePort\nstatus:\n  loadBalancer: {}\n\n\nHacemos los create pertinentes que ya hemos visto anteriormente\n\nkubectl create -f example-deploy.yaml\nkubectl create -f example-service.yaml\n\n\nY por último preparamos nuestro ingress\n\nexample-ingress.yaml\n\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: example-ingress\nspec:\n  rules:\n    - host: hello-world.info\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: web\n                port:\n                  number: 8080\n\n\n\nY creamos nuestro ingress\n\nkubectl create -f example-ingress.yaml\n\n\nEsto nos creará nuestro ingress tardará un poco en tenerlo todo (fijaos en el\nADDRESS)\n\n\n\nComo véis tenemos nuestro ingress creado, un detalle para que no nos alarmemos\nes el warning que aparece. Nosotros en nuestra definición de ingress hemos\ndefinido el uso de la api de kubernetes v1, lo que pasa es que el comando get\ningress comprueba la existencia de ingress desde la primera versión que tiene\ndisponible, por eso el aviso, no quiere decir que nuestra definición de ingress\nesté realmente mal.\n\nBien ahora que ya tenemos nuestro ingress desplegado veamos como podemos acceder\na el. La primera intención puede ser intentar acceder por la IP, pero si lo\nintentamos veremos un 404 de nginx. Esto no es del todo malo, comprobamos que\ntenemos comunicación con nuestro ingress controller\n\n\n\nPara el que no sepa realmente como funcionan los proxys como nginx, lo que hacen\nnormalmente es esperar que el acceso se produzca con un nombre de host\nespecífico, es posible que tengamos varios hostname distintos bajo el mismo\nnginx. En nuestro mismo caso podemos querer tener otro hostname para otro\nservicio que bye-world.info\n\nEntonces lo que tenemos que hacer es modificar nuestra definición local de\nredirecciones IP/HOSTNAME, en mi caso lo tengo que hacer en el fichero \n/etc/hosts\n\n\n\nUna vez modificado si accedemos al hostname en cuestión veremos como accedemos\nsin problema al servicio pertinente\n\n\n\nCon esto tendríamos funcionando un ingress controller que puede redireccionar\nmultiples peticiones a distintos servicios basandonse en el nombre del hosts o\nun path (en nuestro hemos puesto / pero podría ser cualquier otro)\n\nCon esto habríamos visto lo básico para trabajar con ingress, el resto ya es\nrevisar la documentación y ver las distintas opciones que nos ofrece kubernetes\npara cumplir con nuestras necesidades, sin mucho más terminamos este post, nos\nvemos en el siguiente un abrazooooorrrr\n\nINICIAR MINIKUBE CON VIRTUALBOX\nEsta es una sección para los que hayan estado jugando con el propio driver de\ndocker (el que nos provee docker-desktop) para probar kubernetes que al final\nllega un punto en el que algunas cosas no funcionan de ninguna forma (depende de\nlas versiones, van incluyendo poco a poco funcionalidades compatibles).\n\nLo primero que tenemos que hacer es borrar minikube de nuestro entorno,\nrecomiendo borrar todo y hacer una instalación limpia para ello lo primero es:\n\nminikube delete --all --purge\n\n\nEsto nos borra todo lo que tenemos de minikube\n\n\n\nA continuación instalamos virtualbox -> Downlaads\n[https://www.virtualbox.org/wiki/Downloads]\n\nUna vez instalado todo lo que hacemos es arrancar minikube con el driver de\nvirtualbox\n\nminikube start --driver=virtualbox\n\n\n\n\nEsto tardara un rato hasta crea las máquinas virtuales pertinentes\n\n\n\nCon esto ya tendriamos arrancado nuestro minikube con un cluster de maquines\nvirtuales. Well done!!!","html":"<!--kg-card-begin: markdown--><p>Hasta ahora hemos planteado nuestro entorno pensando solo en una o varías aplicaciones que realmente funcionan de manera autonóma, es decir, si pensamos en nuestro POD con el fork del juego de SUPER MARIO vemos como realmente no tiene comunicación con nada más y en un primer momento no la necesita (o nosotros a la hora de usarlo tampoco).</p>\n<p>Ahora propongo otro escenario, como pudiera ser que tenemos un pequeño portal donde el usuario necesita hacer login antes de poder acceder a nuestro juego. Si lo planteamos como si realmente fueran servicios distintos tendriamos por ejemplo:</p>\n<ul>\n<li>Lo que es el front, que no sería más que una miniweb con un login y a lo mejor una lista de posibles juegos.</li>\n<li>Puede que un microservicio que gestionaría los usuarios y nos permitiría hacer login o un microservicio que fuera realmente la API con la lista de juegos disponibles</li>\n<li>Y por dejarlo simple un microservicio que sería el fork de nuestro juego de SUPER MARIO y otro que puede ser un fork del SONIC por ejemplo.</li>\n</ul>\n<p>Es posible que este escenario no tenga ningún sentido ojo, por el tema de la autenticación y demás, es solo para que entendamos que tendríamos varios microservicios distintos que gestionar. Esto con los objetos y configuraciones que hemos visto hasta ahora puede que lo pudieramos conseguir si tenemos un objeto de tipo <em>SERVICE:NODEPORT</em> por cada microservicio y el microservicio de front sepa donde esta todo (superfeo) o a lo mejor hacerlo con subdominios y con DNS o cosas así (se me están ocurriendo sobre la marcha pueden no tener sentido). El caso es que a no ser que lo gestionaramos en un servicio cloud que nos pudiera hacer el enrutamiento no tenemos una forma clara de hacerlo. Bueno pues para esto tenemos en kubernetes el objeto de tipo <strong>INGRESS</strong>.</p>\n<p>Este objeto lo que hace es definir como se deben gestionar las peticiones que vienen desde fuera de nuestro cluster, es decir, hacia donde debe redireccionar cada petición. Veamos una diagrama de la documentación oficial donde creo yo que se explica bastante bien cual es la tarea de nuestro INGRESS</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-17-a-las-18.58.18.png\" alt=\"Captura-de-pantalla-2021-01-17-a-las-18.58.18\"></p>\n<p>Este diagrama que vemos resume un escenario típico que tendríamos en producción:</p>\n<ol>\n<li>El cliente envía una petición que lo primero que hace es llegar al balanceador de carga de nuestro cloud. Este balanceador de carga está manejado por un <strong>INGRESS CONTROLLER</strong> que es el que se encarga de asegurarse de que se cumplan las reglas que definimos en nuestro objeto <em>INGRESS</em></li>\n<li>Según las reglas que hayamos definido nuestro INGRESS CONTROLLER decide a que servicio (objeto de tipo <em>SERVICE</em>) de nuestro cluster debe enviar la petición en cuestión</li>\n<li>Por último este <em>service</em> envía la petición al primero de sus PODS que este disponible</li>\n</ol>\n<p>Un detalle que no hemos dejado claro todavía es que <strong>las reglas que definimos en el objeto de tipo <em>INGRESS</em> no son manejadas realmente por kubernetes, si no que necesitamos de un controlador externo (<em>INGRESS CONTROLLER</em> para que las gestione)</strong>. Como tal existen opciones de configuración de <em>ingress</em> disponibles para NGINX, HAPROXY, TRAEFIK... para la mayoría de los servicios de proxy, uno de los más típicos es el de nginx. En un entorno en cloud seria necesario tener un POD con nginx que sería nuestro INGRESS CONTROLLER.</p>\n<p>Comentar que el entorno completo en cloud con su loadbalancer, su ingress y demás no lo veremos de momento, en la <a href=\"https://kubernetes.io/docs/concepts/services-networking/ingress/\">documentación oficial</a> viene todo lo necesario creo yo, por ahora con motivos puramente de prueba o académicos usaremos la opción que nos provee <em>minikube</em> para tener nuestro propio INGRESS CONTROLLER.</p>\n<p>Veamos lo primero un ejemplo de objeto tipo <em>INGRESS</em>, como no parece existir de momento comando <em>dry-run</em> para verlo, nos tocará copiar un ejemplo como este</p>\n<pre><code>apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: mario-ingress\nspec:\n  rules:\n  - host: mariogame.fulldev.ninja\n    http:\n      paths:\n      - path: /mario\n        pathType: Prefix\n        backend:\n          service:\n            name: mario-service\n            port:\n              number: 8000\n  defaultBackend:\n    service:\n      name: default-service\n      port:\n        number: 8001\n</code></pre>\n<p>Creo que es bastante intuitivo lo que hace cada cosa pero hablemos un poco en detalle de algunos puntos:</p>\n<ul>\n<li><strong>apiVersion: networking.k8s.io/v1</strong>: Aquí si nos fijamos tenemos un cambio respecto a lo que soliamos indicar en el resto de objetos, en este caso debemos especificar que pertenece a la api de networking de kubernetes.</li>\n<li><strong>spec</strong>: Como siempre dentro de spec tenemos siempre la definición de lo que queremos hacer.</li>\n<li><strong>rules</strong>: Dentro de rules como su propio nombre indica es donde definiremos las reglas que queremos usar dentro de nuestro ingress\n<ul>\n<li><strong>host</strong>: hostname al cual enviará el cliente las peticiones</li>\n<li><strong>http -&gt; paths</strong>: Estos serán los paths que usará nuestro ingress controller para redireccionar la petición a un servicio o a otro.</li>\n<li><strong>pathType</strong>: Como se filtrarán los paths, el caso más típico es el de <em>Prefix</em> que buscará coincidencias en el path completo separandolo por /</li>\n<li><strong>backend -&gt; service</strong>: El servicio que debe responder al path indicado</li>\n</ul>\n</li>\n<li><strong>defaultBackend</strong>: Aquí definimos el servicio por defecto en caso de que no se encontraran coincidencias en el resto de reglas</li>\n</ul>\n<p>Como siempre existen muchas más reglas pero estas son las básicas y que me han parecido relevantes para empezar (vuelvo a recomendar leer la documentación oficial)</p>\n<p>Una vez visto lo básico, veamos como configurar un <em>ingress controller</em> en minikube (recordemos que en un entorno cloud tenemos que tener nuestro propio pod que haga de controller).<br>\nPara ello lo primero es hablar de los <strong>addons</strong> de minikube. Minikube dispone de ciertos <em>extras</em> que podemos habilitar según nos insterese, uno de ellos es el <em>ingress controller</em>, este ingress que nos provee minikube es una propia intancia de nginx preparada para este uso y que en este caso no podremos ver como uno de los pods que hemos estado viendo hasta ahora (en un momento veremos lo que pasa)</p>\n<p><strong>OJO esta parte solo funciona si has iniciado minikube con algún gestor de máquinas virtuales como virtualbox <a href=\"https://minikube.sigs.k8s.io/docs/reference/drivers/virtualbox/\">doc oficial</a>. Ver al final del post el apartodo INICIAR MINIKUBE CON VIRTUALBOX o también es posible jugar con <a href=\"https://labs.play-with-k8s.com/\">este playground</a></strong></p>\n<p>Primero veamos la lista de addons, para ello solo es necesario que ejecutemos este comando:</p>\n<pre><code class=\"language-shell\">minikube addons list\n</code></pre>\n<p>Esto nos mostrará una lista de los addons disponibles indicándonos si están habilitados o no (algunos estarán habilitados por defecto según nuestra instalación)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-10.42.44.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-10.42.44\"></p>\n<p>Si os fijáis uno de los addons que aparecen es el de <em>ingress</em>, habilitemoslo</p>\n<pre><code class=\"language-shell\">minikube addons enable ingress\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.08.49.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.08.49\"></p>\n<p>Con esto ya tendríamos habilitado nuestro ingress controller con nginx, pero como he comentado no nos aparece como un pod normal en este entorno.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.10.12.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.10.12\"></p>\n<p>Minikube en este caso lo crea como un pod propio del sistema de minikube por lo que para verlo tendríamos que listar los pods que pertenecen a minikube, para ello</p>\n<pre><code class=\"language-shell\">kubectl get pods -n kube-system\n</code></pre>\n<p>Y esto nos listará todos los pods que va creando minikube</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.12.23.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.12.23\"></p>\n<p>Si nos fijamos tenemos uno en específico que se llama <em>nginx-controller</em>.</p>\n<p>Bien con esto ya tendríamos nuestro ingress controller, ahora vamos a trabajar con los mismos ejemplos que la documentación oficial (el de mario da algunos problemas con los direccionamientos y por no entrenernos), primero el deploy</p>\n<p><strong>example-deploy.yaml</strong></p>\n<pre><code class=\"language-yaml\">apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: web\n  name: web\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: web\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: web\n    spec:\n      containers:\n      - image: gcr.io/google-samples/hello-app:1.0\n        name: hello-app\n        resources: {}\nstatus: {}\n</code></pre>\n<p>El service</p>\n<p><strong>mario-service.yaml</strong></p>\n<pre><code class=\"language-yaml\">apiVersion: v1\nkind: Service\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: web\n  name: web\nspec:\n  ports:\n  - port: 8080\n    protocol: TCP\n    targetPort: 8080\n  selector:\n    app: web\n  type: NodePort\nstatus:\n  loadBalancer: {}\n</code></pre>\n<p>Hacemos los create pertinentes que ya hemos visto anteriormente</p>\n<pre><code class=\"language-shell\">kubectl create -f example-deploy.yaml\nkubectl create -f example-service.yaml\n</code></pre>\n<p>Y por último preparamos nuestro ingress</p>\n<p><strong>example-ingress.yaml</strong></p>\n<pre><code class=\"language-yaml\">apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: example-ingress\nspec:\n  rules:\n    - host: hello-world.info\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: web\n                port:\n                  number: 8080\n\n</code></pre>\n<p>Y creamos nuestro ingress</p>\n<pre><code class=\"language-shell\">kubectl create -f example-ingress.yaml\n</code></pre>\n<p>Esto nos creará nuestro ingress tardará un poco en tenerlo todo (fijaos en el ADDRESS)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.26.20.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-12.26.20\"></p>\n<p>Como véis tenemos nuestro ingress creado, un detalle para que no nos alarmemos es el warning que aparece. Nosotros en nuestra definición de ingress hemos definido el uso de la api de kubernetes v1, lo que pasa es que el comando <em>get ingress</em> comprueba la existencia de ingress desde la primera versión que tiene disponible, por eso el aviso, no quiere decir que nuestra definición de ingress esté realmente mal.</p>\n<p>Bien ahora que ya tenemos nuestro ingress desplegado veamos como podemos acceder a el. La primera intención puede ser intentar acceder por la IP, pero si lo intentamos veremos un 404 de nginx. Esto no es del todo malo, comprobamos que tenemos comunicación con nuestro ingress controller</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.35.58.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.35.58\"></p>\n<p>Para el que no sepa realmente como funcionan los proxys como nginx, lo que hacen normalmente es esperar que el acceso se produzca con un nombre de host específico, es posible que tengamos varios hostname distintos bajo el mismo nginx. En nuestro mismo caso podemos querer tener otro hostname para otro servicio que <strong>bye-world.info</strong></p>\n<p>Entonces lo que tenemos que hacer es modificar nuestra definición local de redirecciones IP/HOSTNAME, en mi caso lo tengo que hacer en el fichero <strong>/etc/hosts</strong></p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.26.20-1.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-12.26.20-1\"></p>\n<p>Una vez modificado si accedemos al hostname en cuestión veremos como accedemos sin problema al servicio pertinente</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.28.09.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-12.28.09\"></p>\n<p>Con esto tendríamos funcionando un ingress controller que puede redireccionar multiples peticiones a distintos servicios basandonse en el nombre del hosts o un path (en nuestro hemos puesto <strong>/</strong> pero podría ser cualquier otro)</p>\n<p>Con esto habríamos visto lo básico para trabajar con ingress, el resto ya es revisar la documentación y ver las distintas opciones que nos ofrece kubernetes para cumplir con nuestras necesidades, sin mucho más terminamos este post, nos vemos en el siguiente un abrazooooorrrr</p>\n<h3 id=\"iniciarminikubeconvirtualbox\">INICIAR MINIKUBE CON VIRTUALBOX</h3>\n<p>Esta es una sección para los que hayan estado jugando con el propio driver de docker (el que nos provee docker-desktop) para probar kubernetes que al final llega un punto en el que algunas cosas no funcionan de ninguna forma (depende de las versiones, van incluyendo poco a poco funcionalidades compatibles).</p>\n<p>Lo primero que tenemos que hacer es borrar minikube de nuestro entorno, recomiendo borrar todo y hacer una instalación limpia para ello lo primero es:</p>\n<pre><code class=\"language-shell\">minikube delete --all --purge\n</code></pre>\n<p>Esto nos borra todo lo que tenemos de minikube</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.02.33.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.02.33\"></p>\n<p>A continuación instalamos virtualbox -&gt; <a href=\"https://www.virtualbox.org/wiki/Downloads\">Downlaads</a></p>\n<p>Una vez instalado todo lo que hacemos es arrancar minikube con el driver de virtualbox</p>\n<pre><code class=\"language-shell\">minikube start --driver=virtualbox\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.03.47.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.03.47\"></p>\n<p>Esto tardara un rato hasta crea las máquinas virtuales pertinentes</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.06.40.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.06.40\"></p>\n<p>Con esto ya tendriamos arrancado nuestro minikube con un cluster de maquines virtuales. Well done!!!</p>\n<!--kg-card-end: markdown-->","url":"https://jlgarcia.fulldev.ninja/docker-xiii-kubernetes-viii-pensando-en-un-entorno-con-microservicios-usando-ingress/","canonical_url":null,"uuid":"7aac306c-9aa6-4c27-a68d-ce54b498122e","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"6004188da6c0f5058bff4dbd","reading_time":8,"send_email_when_published":false,"email_subject":null,"childHtmlRehype":{"html":"<!--kg-card-begin: markdown--><p>Hasta ahora hemos planteado nuestro entorno pensando solo en una o varías aplicaciones que realmente funcionan de manera autonóma, es decir, si pensamos en nuestro POD con el fork del juego de SUPER MARIO vemos como realmente no tiene comunicación con nada más y en un primer momento no la necesita (o nosotros a la hora de usarlo tampoco).</p>\n<p>Ahora propongo otro escenario, como pudiera ser que tenemos un pequeño portal donde el usuario necesita hacer login antes de poder acceder a nuestro juego. Si lo planteamos como si realmente fueran servicios distintos tendriamos por ejemplo:</p>\n<ul>\n<li>Lo que es el front, que no sería más que una miniweb con un login y a lo mejor una lista de posibles juegos.</li>\n<li>Puede que un microservicio que gestionaría los usuarios y nos permitiría hacer login o un microservicio que fuera realmente la API con la lista de juegos disponibles</li>\n<li>Y por dejarlo simple un microservicio que sería el fork de nuestro juego de SUPER MARIO y otro que puede ser un fork del SONIC por ejemplo.</li>\n</ul>\n<p>Es posible que este escenario no tenga ningún sentido ojo, por el tema de la autenticación y demás, es solo para que entendamos que tendríamos varios microservicios distintos que gestionar. Esto con los objetos y configuraciones que hemos visto hasta ahora puede que lo pudieramos conseguir si tenemos un objeto de tipo <em>SERVICE:NODEPORT</em> por cada microservicio y el microservicio de front sepa donde esta todo (superfeo) o a lo mejor hacerlo con subdominios y con DNS o cosas así (se me están ocurriendo sobre la marcha pueden no tener sentido). El caso es que a no ser que lo gestionaramos en un servicio cloud que nos pudiera hacer el enrutamiento no tenemos una forma clara de hacerlo. Bueno pues para esto tenemos en kubernetes el objeto de tipo <strong>INGRESS</strong>.</p>\n<p>Este objeto lo que hace es definir como se deben gestionar las peticiones que vienen desde fuera de nuestro cluster, es decir, hacia donde debe redireccionar cada petición. Veamos una diagrama de la documentación oficial donde creo yo que se explica bastante bien cual es la tarea de nuestro INGRESS</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-17-a-las-18.58.18.png\" alt=\"Captura-de-pantalla-2021-01-17-a-las-18.58.18\"></p>\n<p>Este diagrama que vemos resume un escenario típico que tendríamos en producción:</p>\n<ol>\n<li>El cliente envía una petición que lo primero que hace es llegar al balanceador de carga de nuestro cloud. Este balanceador de carga está manejado por un <strong>INGRESS CONTROLLER</strong> que es el que se encarga de asegurarse de que se cumplan las reglas que definimos en nuestro objeto <em>INGRESS</em></li>\n<li>Según las reglas que hayamos definido nuestro INGRESS CONTROLLER decide a que servicio (objeto de tipo <em>SERVICE</em>) de nuestro cluster debe enviar la petición en cuestión</li>\n<li>Por último este <em>service</em> envía la petición al primero de sus PODS que este disponible</li>\n</ol>\n<p>Un detalle que no hemos dejado claro todavía es que <strong>las reglas que definimos en el objeto de tipo <em>INGRESS</em> no son manejadas realmente por kubernetes, si no que necesitamos de un controlador externo (<em>INGRESS CONTROLLER</em> para que las gestione)</strong>. Como tal existen opciones de configuración de <em>ingress</em> disponibles para NGINX, HAPROXY, TRAEFIK... para la mayoría de los servicios de proxy, uno de los más típicos es el de nginx. En un entorno en cloud seria necesario tener un POD con nginx que sería nuestro INGRESS CONTROLLER.</p>\n<p>Comentar que el entorno completo en cloud con su loadbalancer, su ingress y demás no lo veremos de momento, en la <a href=\"https://kubernetes.io/docs/concepts/services-networking/ingress/\">documentación oficial</a> viene todo lo necesario creo yo, por ahora con motivos puramente de prueba o académicos usaremos la opción que nos provee <em>minikube</em> para tener nuestro propio INGRESS CONTROLLER.</p>\n<p>Veamos lo primero un ejemplo de objeto tipo <em>INGRESS</em>, como no parece existir de momento comando <em>dry-run</em> para verlo, nos tocará copiar un ejemplo como este</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: mario-ingress\nspec:\n  rules:\n  - host: mariogame.fulldev.ninja\n    http:\n      paths:\n      - path: /mario\n        pathType: Prefix\n        backend:\n          service:\n            name: mario-service\n            port:\n              number: 8000\n  defaultBackend:\n    service:\n      name: default-service\n      port:\n        number: 8001\n</code></pre></div>\n<p>Creo que es bastante intuitivo lo que hace cada cosa pero hablemos un poco en detalle de algunos puntos:</p>\n<ul>\n<li><strong>apiVersion: networking.k8s.io/v1</strong>: Aquí si nos fijamos tenemos un cambio respecto a lo que soliamos indicar en el resto de objetos, en este caso debemos especificar que pertenece a la api de networking de kubernetes.</li>\n<li><strong>spec</strong>: Como siempre dentro de spec tenemos siempre la definición de lo que queremos hacer.</li>\n<li><strong>rules</strong>: Dentro de rules como su propio nombre indica es donde definiremos las reglas que queremos usar dentro de nuestro ingress\n<ul>\n<li><strong>host</strong>: hostname al cual enviará el cliente las peticiones</li>\n<li><strong>http -> paths</strong>: Estos serán los paths que usará nuestro ingress controller para redireccionar la petición a un servicio o a otro.</li>\n<li><strong>pathType</strong>: Como se filtrarán los paths, el caso más típico es el de <em>Prefix</em> que buscará coincidencias en el path completo separandolo por /</li>\n<li><strong>backend -> service</strong>: El servicio que debe responder al path indicado</li>\n</ul>\n</li>\n<li><strong>defaultBackend</strong>: Aquí definimos el servicio por defecto en caso de que no se encontraran coincidencias en el resto de reglas</li>\n</ul>\n<p>Como siempre existen muchas más reglas pero estas son las básicas y que me han parecido relevantes para empezar (vuelvo a recomendar leer la documentación oficial)</p>\n<p>Una vez visto lo básico, veamos como configurar un <em>ingress controller</em> en minikube (recordemos que en un entorno cloud tenemos que tener nuestro propio pod que haga de controller).<br>\nPara ello lo primero es hablar de los <strong>addons</strong> de minikube. Minikube dispone de ciertos <em>extras</em> que podemos habilitar según nos insterese, uno de ellos es el <em>ingress controller</em>, este ingress que nos provee minikube es una propia intancia de nginx preparada para este uso y que en este caso no podremos ver como uno de los pods que hemos estado viendo hasta ahora (en un momento veremos lo que pasa)</p>\n<p><strong>OJO esta parte solo funciona si has iniciado minikube con algún gestor de máquinas virtuales como virtualbox <a href=\"https://minikube.sigs.k8s.io/docs/reference/drivers/virtualbox/\">doc oficial</a>. Ver al final del post el apartodo INICIAR MINIKUBE CON VIRTUALBOX o también es posible jugar con <a href=\"https://labs.play-with-k8s.com/\">este playground</a></strong></p>\n<p>Primero veamos la lista de addons, para ello solo es necesario que ejecutemos este comando:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">minikube addons list\n</code></pre></div>\n<p>Esto nos mostrará una lista de los addons disponibles indicándonos si están habilitados o no (algunos estarán habilitados por defecto según nuestra instalación)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-10.42.44.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-10.42.44\"></p>\n<p>Si os fijáis uno de los addons que aparecen es el de <em>ingress</em>, habilitemoslo</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">minikube addons <span class=\"token builtin class-name\">enable</span> ingress\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.08.49.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.08.49\"></p>\n<p>Con esto ya tendríamos habilitado nuestro ingress controller con nginx, pero como he comentado no nos aparece como un pod normal en este entorno.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.10.12.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.10.12\"></p>\n<p>Minikube en este caso lo crea como un pod propio del sistema de minikube por lo que para verlo tendríamos que listar los pods que pertenecen a minikube, para ello</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl get pods -n kube-system\n</code></pre></div>\n<p>Y esto nos listará todos los pods que va creando minikube</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.12.23.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.12.23\"></p>\n<p>Si nos fijamos tenemos uno en específico que se llama <em>nginx-controller</em>.</p>\n<p>Bien con esto ya tendríamos nuestro ingress controller, ahora vamos a trabajar con los mismos ejemplos que la documentación oficial (el de mario da algunos problemas con los direccionamientos y por no entrenernos), primero el deploy</p>\n<p><strong>example-deploy.yaml</strong></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> apps/v1\n<span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Deployment\n<span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n  <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> web\n  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> web\n<span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">replicas</span><span class=\"token punctuation\">:</span> <span class=\"token number\">2</span>\n  <span class=\"token key atrule\">selector</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">matchLabels</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> web\n  <span class=\"token key atrule\">strategy</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">template</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n      <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> web\n    <span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">containers</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> gcr.io/google<span class=\"token punctuation\">-</span>samples/hello<span class=\"token punctuation\">-</span>app<span class=\"token punctuation\">:</span><span class=\"token number\">1.0</span>\n        <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> hello<span class=\"token punctuation\">-</span>app\n        <span class=\"token key atrule\">resources</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token key atrule\">status</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre></div>\n<p>El service</p>\n<p><strong>mario-service.yaml</strong></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> v1\n<span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Service\n<span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n  <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> web\n  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> web\n<span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n  <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">port</span><span class=\"token punctuation\">:</span> <span class=\"token number\">8080</span>\n    <span class=\"token key atrule\">protocol</span><span class=\"token punctuation\">:</span> TCP\n    <span class=\"token key atrule\">targetPort</span><span class=\"token punctuation\">:</span> <span class=\"token number\">8080</span>\n  <span class=\"token key atrule\">selector</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> web\n  <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> NodePort\n<span class=\"token key atrule\">status</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">loadBalancer</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre></div>\n<p>Hacemos los create pertinentes que ya hemos visto anteriormente</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl create -f example-deploy.yaml\nkubectl create -f example-service.yaml\n</code></pre></div>\n<p>Y por último preparamos nuestro ingress</p>\n<p><strong>example-ingress.yaml</strong></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> networking.k8s.io/v1\n<span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Ingress\n<span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> example<span class=\"token punctuation\">-</span>ingress\n<span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">rules</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">host</span><span class=\"token punctuation\">:</span> hello<span class=\"token punctuation\">-</span>world.info\n      <span class=\"token key atrule\">http</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">paths</span><span class=\"token punctuation\">:</span>\n          <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">path</span><span class=\"token punctuation\">:</span> /\n            <span class=\"token key atrule\">pathType</span><span class=\"token punctuation\">:</span> Prefix\n            <span class=\"token key atrule\">backend</span><span class=\"token punctuation\">:</span>\n              <span class=\"token key atrule\">service</span><span class=\"token punctuation\">:</span>\n                <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> web\n                <span class=\"token key atrule\">port</span><span class=\"token punctuation\">:</span>\n                  <span class=\"token key atrule\">number</span><span class=\"token punctuation\">:</span> <span class=\"token number\">8080</span>\n\n</code></pre></div>\n<p>Y creamos nuestro ingress</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl create -f example-ingress.yaml\n</code></pre></div>\n<p>Esto nos creará nuestro ingress tardará un poco en tenerlo todo (fijaos en el ADDRESS)</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.26.20.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-12.26.20\"></p>\n<p>Como véis tenemos nuestro ingress creado, un detalle para que no nos alarmemos es el warning que aparece. Nosotros en nuestra definición de ingress hemos definido el uso de la api de kubernetes v1, lo que pasa es que el comando <em>get ingress</em> comprueba la existencia de ingress desde la primera versión que tiene disponible, por eso el aviso, no quiere decir que nuestra definición de ingress esté realmente mal.</p>\n<p>Bien ahora que ya tenemos nuestro ingress desplegado veamos como podemos acceder a el. La primera intención puede ser intentar acceder por la IP, pero si lo intentamos veremos un 404 de nginx. Esto no es del todo malo, comprobamos que tenemos comunicación con nuestro ingress controller</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.35.58.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.35.58\"></p>\n<p>Para el que no sepa realmente como funcionan los proxys como nginx, lo que hacen normalmente es esperar que el acceso se produzca con un nombre de host específico, es posible que tengamos varios hostname distintos bajo el mismo nginx. En nuestro mismo caso podemos querer tener otro hostname para otro servicio que <strong>bye-world.info</strong></p>\n<p>Entonces lo que tenemos que hacer es modificar nuestra definición local de redirecciones IP/HOSTNAME, en mi caso lo tengo que hacer en el fichero <strong>/etc/hosts</strong></p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.26.20-1.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-12.26.20-1\"></p>\n<p>Una vez modificado si accedemos al hostname en cuestión veremos como accedemos sin problema al servicio pertinente</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.28.09.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-12.28.09\"></p>\n<p>Con esto tendríamos funcionando un ingress controller que puede redireccionar multiples peticiones a distintos servicios basandonse en el nombre del hosts o un path (en nuestro hemos puesto <strong>/</strong> pero podría ser cualquier otro)</p>\n<p>Con esto habríamos visto lo básico para trabajar con ingress, el resto ya es revisar la documentación y ver las distintas opciones que nos ofrece kubernetes para cumplir con nuestras necesidades, sin mucho más terminamos este post, nos vemos en el siguiente un abrazooooorrrr</p>\n<h3 id=\"iniciarminikubeconvirtualbox\">INICIAR MINIKUBE CON VIRTUALBOX</h3>\n<p>Esta es una sección para los que hayan estado jugando con el propio driver de docker (el que nos provee docker-desktop) para probar kubernetes que al final llega un punto en el que algunas cosas no funcionan de ninguna forma (depende de las versiones, van incluyendo poco a poco funcionalidades compatibles).</p>\n<p>Lo primero que tenemos que hacer es borrar minikube de nuestro entorno, recomiendo borrar todo y hacer una instalación limpia para ello lo primero es:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">minikube delete --all --purge\n</code></pre></div>\n<p>Esto nos borra todo lo que tenemos de minikube</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.02.33.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.02.33\"></p>\n<p>A continuación instalamos virtualbox -> <a href=\"https://www.virtualbox.org/wiki/Downloads\">Downlaads</a></p>\n<p>Una vez instalado todo lo que hacemos es arrancar minikube con el driver de virtualbox</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">minikube start --driver<span class=\"token operator\">=</span>virtualbox\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.03.47.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.03.47\"></p>\n<p>Esto tardara un rato hasta crea las máquinas virtuales pertinentes</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.06.40.png\" alt=\"Captura-de-pantalla-2021-01-22-a-las-11.06.40\"></p>\n<p>Con esto ya tendriamos arrancado nuestro minikube con un cluster de maquines virtuales. Well done!!!</p>\n<!--kg-card-end: markdown-->","htmlAst":{"type":"root","children":[{"type":"comment","value":"kg-card-begin: markdown"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Hasta ahora hemos planteado nuestro entorno pensando solo en una o varías aplicaciones que realmente funcionan de manera autonóma, es decir, si pensamos en nuestro POD con el fork del juego de SUPER MARIO vemos como realmente no tiene comunicación con nada más y en un primer momento no la necesita (o nosotros a la hora de usarlo tampoco)."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ahora propongo otro escenario, como pudiera ser que tenemos un pequeño portal donde el usuario necesita hacer login antes de poder acceder a nuestro juego. Si lo planteamos como si realmente fueran servicios distintos tendriamos por ejemplo:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Lo que es el front, que no sería más que una miniweb con un login y a lo mejor una lista de posibles juegos."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Puede que un microservicio que gestionaría los usuarios y nos permitiría hacer login o un microservicio que fuera realmente la API con la lista de juegos disponibles"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Y por dejarlo simple un microservicio que sería el fork de nuestro juego de SUPER MARIO y otro que puede ser un fork del SONIC por ejemplo."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Es posible que este escenario no tenga ningún sentido ojo, por el tema de la autenticación y demás, es solo para que entendamos que tendríamos varios microservicios distintos que gestionar. Esto con los objetos y configuraciones que hemos visto hasta ahora puede que lo pudieramos conseguir si tenemos un objeto de tipo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"SERVICE:NODEPORT"}]},{"type":"text","value":" por cada microservicio y el microservicio de front sepa donde esta todo (superfeo) o a lo mejor hacerlo con subdominios y con DNS o cosas así (se me están ocurriendo sobre la marcha pueden no tener sentido). El caso es que a no ser que lo gestionaramos en un servicio cloud que nos pudiera hacer el enrutamiento no tenemos una forma clara de hacerlo. Bueno pues para esto tenemos en kubernetes el objeto de tipo "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"INGRESS"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Este objeto lo que hace es definir como se deben gestionar las peticiones que vienen desde fuera de nuestro cluster, es decir, hacia donde debe redireccionar cada petición. Veamos una diagrama de la documentación oficial donde creo yo que se explica bastante bien cual es la tarea de nuestro INGRESS"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-17-a-las-18.58.18.png","alt":"Captura-de-pantalla-2021-01-17-a-las-18.58.18"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Este diagrama que vemos resume un escenario típico que tendríamos en producción:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"El cliente envía una petición que lo primero que hace es llegar al balanceador de carga de nuestro cloud. Este balanceador de carga está manejado por un "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"INGRESS CONTROLLER"}]},{"type":"text","value":" que es el que se encarga de asegurarse de que se cumplan las reglas que definimos en nuestro objeto "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"INGRESS"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Según las reglas que hayamos definido nuestro INGRESS CONTROLLER decide a que servicio (objeto de tipo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"SERVICE"}]},{"type":"text","value":") de nuestro cluster debe enviar la petición en cuestión"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Por último este "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"service"}]},{"type":"text","value":" envía la petición al primero de sus PODS que este disponible"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Un detalle que no hemos dejado claro todavía es que "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"las reglas que definimos en el objeto de tipo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"INGRESS"}]},{"type":"text","value":" no son manejadas realmente por kubernetes, si no que necesitamos de un controlador externo ("},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"INGRESS CONTROLLER"}]},{"type":"text","value":" para que las gestione)"}]},{"type":"text","value":". Como tal existen opciones de configuración de "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"ingress"}]},{"type":"text","value":" disponibles para NGINX, HAPROXY, TRAEFIK... para la mayoría de los servicios de proxy, uno de los más típicos es el de nginx. En un entorno en cloud seria necesario tener un POD con nginx que sería nuestro INGRESS CONTROLLER."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Comentar que el entorno completo en cloud con su loadbalancer, su ingress y demás no lo veremos de momento, en la "},{"type":"element","tagName":"a","properties":{"href":"https://kubernetes.io/docs/concepts/services-networking/ingress/"},"children":[{"type":"text","value":"documentación oficial"}]},{"type":"text","value":" viene todo lo necesario creo yo, por ahora con motivos puramente de prueba o académicos usaremos la opción que nos provee "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"minikube"}]},{"type":"text","value":" para tener nuestro propio INGRESS CONTROLLER."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Veamos lo primero un ejemplo de objeto tipo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"INGRESS"}]},{"type":"text","value":", como no parece existir de momento comando "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dry-run"}]},{"type":"text","value":" para verlo, nos tocará copiar un ejemplo como este"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"text"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-text"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-text"]},"children":[{"type":"text","value":"apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: mario-ingress\nspec:\n  rules:\n  - host: mariogame.fulldev.ninja\n    http:\n      paths:\n      - path: /mario\n        pathType: Prefix\n        backend:\n          service:\n            name: mario-service\n            port:\n              number: 8000\n  defaultBackend:\n    service:\n      name: default-service\n      port:\n        number: 8001\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Creo que es bastante intuitivo lo que hace cada cosa pero hablemos un poco en detalle de algunos puntos:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"apiVersion: networking.k8s.io/v1"}]},{"type":"text","value":": Aquí si nos fijamos tenemos un cambio respecto a lo que soliamos indicar en el resto de objetos, en este caso debemos especificar que pertenece a la api de networking de kubernetes."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"spec"}]},{"type":"text","value":": Como siempre dentro de spec tenemos siempre la definición de lo que queremos hacer."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"rules"}]},{"type":"text","value":": Dentro de rules como su propio nombre indica es donde definiremos las reglas que queremos usar dentro de nuestro ingress\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"host"}]},{"type":"text","value":": hostname al cual enviará el cliente las peticiones"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"http -> paths"}]},{"type":"text","value":": Estos serán los paths que usará nuestro ingress controller para redireccionar la petición a un servicio o a otro."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"pathType"}]},{"type":"text","value":": Como se filtrarán los paths, el caso más típico es el de "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Prefix"}]},{"type":"text","value":" que buscará coincidencias en el path completo separandolo por /"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"backend -> service"}]},{"type":"text","value":": El servicio que debe responder al path indicado"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"defaultBackend"}]},{"type":"text","value":": Aquí definimos el servicio por defecto en caso de que no se encontraran coincidencias en el resto de reglas"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como siempre existen muchas más reglas pero estas son las básicas y que me han parecido relevantes para empezar (vuelvo a recomendar leer la documentación oficial)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez visto lo básico, veamos como configurar un "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"ingress controller"}]},{"type":"text","value":" en minikube (recordemos que en un entorno cloud tenemos que tener nuestro propio pod que haga de controller)."},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nPara ello lo primero es hablar de los "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"addons"}]},{"type":"text","value":" de minikube. Minikube dispone de ciertos "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"extras"}]},{"type":"text","value":" que podemos habilitar según nos insterese, uno de ellos es el "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"ingress controller"}]},{"type":"text","value":", este ingress que nos provee minikube es una propia intancia de nginx preparada para este uso y que en este caso no podremos ver como uno de los pods que hemos estado viendo hasta ahora (en un momento veremos lo que pasa)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"OJO esta parte solo funciona si has iniciado minikube con algún gestor de máquinas virtuales como virtualbox "},{"type":"element","tagName":"a","properties":{"href":"https://minikube.sigs.k8s.io/docs/reference/drivers/virtualbox/"},"children":[{"type":"text","value":"doc oficial"}]},{"type":"text","value":". Ver al final del post el apartodo INICIAR MINIKUBE CON VIRTUALBOX o también es posible jugar con "},{"type":"element","tagName":"a","properties":{"href":"https://labs.play-with-k8s.com/"},"children":[{"type":"text","value":"este playground"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Primero veamos la lista de addons, para ello solo es necesario que ejecutemos este comando:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"minikube addons list\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos mostrará una lista de los addons disponibles indicándonos si están habilitados o no (algunos estarán habilitados por defecto según nuestra instalación)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-10.42.44.png","alt":"Captura-de-pantalla-2021-01-22-a-las-10.42.44"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si os fijáis uno de los addons que aparecen es el de "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"ingress"}]},{"type":"text","value":", habilitemoslo"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"minikube addons "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"enable"}]},{"type":"text","value":" ingress\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.08.49.png","alt":"Captura-de-pantalla-2021-01-22-a-las-11.08.49"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con esto ya tendríamos habilitado nuestro ingress controller con nginx, pero como he comentado no nos aparece como un pod normal en este entorno."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.10.12.png","alt":"Captura-de-pantalla-2021-01-22-a-las-11.10.12"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Minikube en este caso lo crea como un pod propio del sistema de minikube por lo que para verlo tendríamos que listar los pods que pertenecen a minikube, para ello"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl get pods -n kube-system\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y esto nos listará todos los pods que va creando minikube"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.12.23.png","alt":"Captura-de-pantalla-2021-01-22-a-las-11.12.23"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si nos fijamos tenemos uno en específico que se llama "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"nginx-controller"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Bien con esto ya tendríamos nuestro ingress controller, ahora vamos a trabajar con los mismos ejemplos que la documentación oficial (el de mario da algunos problemas con los direccionamientos y por no entrenernos), primero el deploy"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"example-deploy.yaml"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"apiVersion"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" apps/v1\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"kind"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Deployment\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"replicas"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"2"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"selector"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"matchLabels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"strategy"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"template"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"containers"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"image"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" gcr.io/google"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"samples/hello"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"app"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"1.0"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" hello"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"app\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"resources"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"status"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"El service"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"mario-service.yaml"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"apiVersion"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" v1\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"kind"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Service\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"port"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"8080"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"protocol"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" TCP\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"targetPort"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"8080"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"selector"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"type"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" NodePort\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"status"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"loadBalancer"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Hacemos los create pertinentes que ya hemos visto anteriormente"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl create -f example-deploy.yaml\nkubectl create -f example-service.yaml\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y por último preparamos nuestro ingress"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"example-ingress.yaml"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"apiVersion"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" networking.k8s.io/v1\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"kind"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Ingress\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" example"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"ingress\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"rules"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"host"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" hello"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"world.info\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"http"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"paths"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n          "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"path"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" /\n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"pathType"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Prefix\n            "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"backend"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n              "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"service"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n                "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" web\n                "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"port"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n                  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"number"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"8080"}]},{"type":"text","value":"\n\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y creamos nuestro ingress"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl create -f example-ingress.yaml\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos creará nuestro ingress tardará un poco en tenerlo todo (fijaos en el ADDRESS)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.26.20.png","alt":"Captura-de-pantalla-2021-01-22-a-las-12.26.20"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como véis tenemos nuestro ingress creado, un detalle para que no nos alarmemos es el warning que aparece. Nosotros en nuestra definición de ingress hemos definido el uso de la api de kubernetes v1, lo que pasa es que el comando "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"get ingress"}]},{"type":"text","value":" comprueba la existencia de ingress desde la primera versión que tiene disponible, por eso el aviso, no quiere decir que nuestra definición de ingress esté realmente mal."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Bien ahora que ya tenemos nuestro ingress desplegado veamos como podemos acceder a el. La primera intención puede ser intentar acceder por la IP, pero si lo intentamos veremos un 404 de nginx. Esto no es del todo malo, comprobamos que tenemos comunicación con nuestro ingress controller"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.35.58.png","alt":"Captura-de-pantalla-2021-01-22-a-las-11.35.58"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Para el que no sepa realmente como funcionan los proxys como nginx, lo que hacen normalmente es esperar que el acceso se produzca con un nombre de host específico, es posible que tengamos varios hostname distintos bajo el mismo nginx. En nuestro mismo caso podemos querer tener otro hostname para otro servicio que "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"bye-world.info"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Entonces lo que tenemos que hacer es modificar nuestra definición local de redirecciones IP/HOSTNAME, en mi caso lo tengo que hacer en el fichero "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"/etc/hosts"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.26.20-1.png","alt":"Captura-de-pantalla-2021-01-22-a-las-12.26.20-1"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez modificado si accedemos al hostname en cuestión veremos como accedemos sin problema al servicio pertinente"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-12.28.09.png","alt":"Captura-de-pantalla-2021-01-22-a-las-12.28.09"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con esto tendríamos funcionando un ingress controller que puede redireccionar multiples peticiones a distintos servicios basandonse en el nombre del hosts o un path (en nuestro hemos puesto "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"/"}]},{"type":"text","value":" pero podría ser cualquier otro)"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con esto habríamos visto lo básico para trabajar con ingress, el resto ya es revisar la documentación y ver las distintas opciones que nos ofrece kubernetes para cumplir con nuestras necesidades, sin mucho más terminamos este post, nos vemos en el siguiente un abrazooooorrrr"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"iniciarminikubeconvirtualbox"},"children":[{"type":"text","value":"INICIAR MINIKUBE CON VIRTUALBOX"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esta es una sección para los que hayan estado jugando con el propio driver de docker (el que nos provee docker-desktop) para probar kubernetes que al final llega un punto en el que algunas cosas no funcionan de ninguna forma (depende de las versiones, van incluyendo poco a poco funcionalidades compatibles)."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Lo primero que tenemos que hacer es borrar minikube de nuestro entorno, recomiendo borrar todo y hacer una instalación limpia para ello lo primero es:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"minikube delete --all --purge\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos borra todo lo que tenemos de minikube"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.02.33.png","alt":"Captura-de-pantalla-2021-01-22-a-las-11.02.33"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"A continuación instalamos virtualbox -> "},{"type":"element","tagName":"a","properties":{"href":"https://www.virtualbox.org/wiki/Downloads"},"children":[{"type":"text","value":"Downlaads"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez instalado todo lo que hacemos es arrancar minikube con el driver de virtualbox"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"minikube start --driver"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":"virtualbox\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.03.47.png","alt":"Captura-de-pantalla-2021-01-22-a-las-11.03.47"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto tardara un rato hasta crea las máquinas virtuales pertinentes"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-22-a-las-11.06.40.png","alt":"Captura-de-pantalla-2021-01-22-a-las-11.06.40"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Con esto ya tendriamos arrancado nuestro minikube con un cluster de maquines virtuales. Well done!!!"}]},{"type":"text","value":"\n"},{"type":"comment","value":"kg-card-end: markdown"}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"iniciarminikubeconvirtualbox","heading":"INICIAR MINIKUBE CON VIRTUALBOX"}]},"featureImageSharp":{"base":"learning-docker-and-k8s-by-practice-4.jpg","publicURL":"/static/a8e77ba5b1852585391f8ffab9564f47/learning-docker-and-k8s-by-practice-4.jpg","imageMeta":{"width":480,"height":270},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHXm8RRgf/EABoQAQABBQAAAAAAAAAAAAAAAAIBAxAREhP/2gAIAQEAAQUClrpUesGcm/8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhABAQEAAAAAAAAAAAAAAAAAAREg/9oACAEBAAY/AoBNf//EABsQAAICAwEAAAAAAAAAAAAAAAERACEgQWFx/9oACAEBAAE/IRg7EmEoQL9qLjT2sP/aAAwDAQACAAMAAAAQ8M//xAAWEQADAAAAAAAAAAAAAAAAAAAQEWH/2gAIAQMBAT8QVH//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEx/9oACAECAQE/EBpyP//EABoQAQEBAQADAAAAAAAAAAAAAAERADEQIUH/2gAIAQEAAT8QZBiMqOC0uIouvYaltghUH6eEHoOAOE3/2Q==","aspectRatio":1.7857142857142858,"src":"/static/a8e77ba5b1852585391f8ffab9564f47/e3903/learning-docker-and-k8s-by-practice-4.jpg","srcSet":"/static/a8e77ba5b1852585391f8ffab9564f47/477ba/learning-docker-and-k8s-by-practice-4.jpg 175w,\n/static/a8e77ba5b1852585391f8ffab9564f47/06776/learning-docker-and-k8s-by-practice-4.jpg 350w,\n/static/a8e77ba5b1852585391f8ffab9564f47/e3903/learning-docker-and-k8s-by-practice-4.jpg 480w","srcWebp":"/static/a8e77ba5b1852585391f8ffab9564f47/61162/learning-docker-and-k8s-by-practice-4.webp","srcSetWebp":"/static/a8e77ba5b1852585391f8ffab9564f47/9fca7/learning-docker-and-k8s-by-practice-4.webp 175w,\n/static/a8e77ba5b1852585391f8ffab9564f47/37a4e/learning-docker-and-k8s-by-practice-4.webp 350w,\n/static/a8e77ba5b1852585391f8ffab9564f47/61162/learning-docker-and-k8s-by-practice-4.webp 480w","sizes":"(max-width: 480px) 100vw, 480px"}}}}},{"node":{"id":"Ghost__Post__6003341fa6c0f5058bff4d9e","title":"Docker XII - Kubernetes VII: Escalando PODS","slug":"docker-xii-kubernetes-vii-multiplicando-pods","featured":false,"feature_image":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/learning-docker-and-k8s-by-practice-3.jpg","excerpt":"Hasta ahora en los post anteriores hemos estado trabajando con solo un POD o una\ninstancia de cada POD. Pero para realmente tener ese escenario no necesitamos \nkubernetes podiamos hacerlo igual con docker o con docker-conpose. Veamos ahora\nrealmente algo de lo que nos ofrece realmente kubernetes que es la posibilidad\nde gestionar multiples instancias de un mismo POD.\n\nEsto lo hace kubernetes posible haciendo uso de los objetos con tipo Deployment,\nes un objeto muy similar al POD con el que hemos","custom_excerpt":null,"visibility":"public","created_at_pretty":"16 Jan 2021","published_at_pretty":"1 Feb 2021","updated_at_pretty":"1 Feb 2021","created_at":"2021-01-16T19:44:47.000+01:00","published_at":"2021-02-01T10:39:00.000+01:00","updated_at":"2021-02-01T10:39:00.000+01:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":null}],"primary_author":{"slug":"jlgarcia","url":"https://jlgarcia.fulldev.ninja/author/jlgarcia/","name":"Juan Luis Garcia Aparicio","bio":null,"cover_image":null,"profile_image":"https://jlgarcia.fulldev.ninja/assets/images/2017/12/Perfil.jpg","location":null,"website":null,"twitter":null,"facebook":null,"meta_title":null,"meta_description":null,"coverImageSharp":null,"profileImageSharp":{"base":"Perfil.jpg","publicURL":"/static/b0de6281fb28a266510b3b09b9243e5a/Perfil.jpg","imageMeta":{"width":307,"height":307},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAUDBAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGzw6zC6zHn+cLYP//EAB0QAAICAQUAAAAAAAAAAAAAAAEDAAIEEyEiIzL/2gAIAQEAAQUCifca8KgcKWVfUpkHsG5pxX//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAdEAACAgEFAAAAAAAAAAAAAAAAARARcQISIUFR/9oACAEBAAY/AhU88xkb7N06a8P/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUFRYXH/2gAIAQEAAT8hR2pq40aqb+xIAeXibhW9JXr8joF4TBcSNe0//9oADAMBAAIAAwAAABDzDwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFhcfD/2gAIAQEAAT8QyItrELaTlatLwU63MvEW6vUNdy4LZQDn7iVApV9VLtANdWwKkuYq4Er1VZ//2Q==","aspectRatio":1,"src":"/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg","srcSet":"/static/b0de6281fb28a266510b3b09b9243e5a/f340b/Perfil.jpg 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/22d64/Perfil.jpg 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/31709/Perfil.jpg 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/aa249/Perfil.jpg 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/0dc33/Perfil.jpg 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/60667/Perfil.jpg 307w","srcWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp","srcSetWebp":"/static/b0de6281fb28a266510b3b09b9243e5a/59cda/Perfil.webp 28w,\n/static/b0de6281fb28a266510b3b09b9243e5a/7da75/Perfil.webp 55w,\n/static/b0de6281fb28a266510b3b09b9243e5a/8678c/Perfil.webp 110w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f282e/Perfil.webp 165w,\n/static/b0de6281fb28a266510b3b09b9243e5a/a7b21/Perfil.webp 220w,\n/static/b0de6281fb28a266510b3b09b9243e5a/f59af/Perfil.webp 307w","sizes":"(max-width: 110px) 100vw, 110px"}}}},"primary_tag":{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},"tags":[{"slug":"docker","url":"https://jlgarcia.fulldev.ninja/tag/docker/","name":"Docker","visibility":"public","feature_image":null,"description":null,"meta_title":"Docker Issues","meta_description":"Veremos algunos de los problemas que me he ido encontrando trabajando con contenedores y que no es tan fácil encontrar la solución en internet.","featureImageSharp":null},{"slug":"kubernetes","url":"https://jlgarcia.fulldev.ninja/tag/kubernetes/","name":"kubernetes","visibility":"public","feature_image":null,"description":null,"meta_title":null,"meta_description":null,"featureImageSharp":null}],"plaintext":"Hasta ahora en los post anteriores hemos estado trabajando con solo un POD o una\ninstancia de cada POD. Pero para realmente tener ese escenario no necesitamos \nkubernetes podiamos hacerlo igual con docker o con docker-conpose. Veamos ahora\nrealmente algo de lo que nos ofrece realmente kubernetes que es la posibilidad\nde gestionar multiples instancias de un mismo POD.\n\nEsto lo hace kubernetes posible haciendo uso de los objetos con tipo Deployment,\nes un objeto muy similar al POD con el que hemos estado trabajando solo que nos\npermite indicarle cuantas instancias queremos de un mismo pod. Para ver un\nejemplo podemos hacer, como siempre, uso de un comando dry-run:\n\nkubectl create deployment mario-deploy --image=pengbai/docker-supermario:latest --port=8080 --dry-run=client -o yaml > mario-deployment.yaml\n\n\nEsto nos creará un documento similar a este:\n\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: mario-deploy\n  name: mario-deploy\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: mario-deploy\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: mario-deploy\n    spec:\n      containers:\n      - image: pengbai/docker-supermario:latest\n        name: docker-supermario\n        ports:\n        - containerPort: 8080\n        resources: {}\nstatus: {}\n\n\nVeamos un poco que es lo que tenemos aquí:\n\n * metadata: Ya hemos visto en el resto de posts que es esto. Contiene labels \n   identificativos que nos pueden ser utiles para hacer referencia desde otros\n   objetos del cluster de kubernetes. Y también tiene su name para identificar\n   este objeto como tal.\n\nDentro del spec, que recordemos que es donde realmente se define la magia \ntenemos nuevo:\n\n * replicas: Creo que es autoexplicativo. Podemos indicar la cantidad de\n   instancias que queremos de un tipo de POD.\n * selector: Al igual que pasaba con el objeto de tipo service, dentro de este\n   selector indicamos los labels que tienen los pods que debe gestionar este\n   objeto de tipo deployment.\n * template: Aquí es donde definimos el POD en cuestión que tiene que usar como\n   plantilla, es decir, usando un objeto de tipo deployment ya no es necesario\n   tener por otro lado un objeto de tipo POD, puede estar definido dentro de\n   esta template (si los comparamos la definión debe ser muy similar)\n\nUna vez repasadas más o menos las diferencias veamos que pasa, cambiemos la\ncantidad de replicas a 2 y ejecutemos el típico create\n\n\n\nSi comprobamos los pods que tenemos ahora veremos algo distinto\n\n\n\nVemos como ahora ahora los pods se crean de otra manera, contienen el nombre del\nobjeto deploy y además una conjunto alfanumerico, eso convierte el nombre del\npod un identificador único. Además de comprobar los pods, ahora podemos usar\notro comando específico para deployments\n\nkubectl get deployments\n\n\n\n\nQue como podemos comprar nos muestra cuando pods tiene el deployment, cuandos\nestan activos y cuantos estan actualizados con la última versión (ya hablaremos\nde esto). Si ahora nosotros eliminamos uno de los pods, veremos como\nautomáticamente nos crea uno nuevo.\n\n\n\nKubernetes, como siempre, en su ciclo de vida (llamada bucle de reconciliación)\ncomprueba que el estado de los objetos que están en funcionamiento cumple con\nsus definiones/plantillas y si no, pues actua en consecuencia, en este caso con\nla creación de un nuevo POD usando la plantilla que tiene el objeto de tipo \ndeployment.\n\nPero claro ahora mismo hemos definido de antemano el número de replicas que\nqueremos de un mismo POD dentro de la plantilla de deploy, pero... ¿y si tengo\nque escalar mi aplicación ahora mismo entando en producción? pues para ello\ntenemos varias opciones:\n\n * Editar el estado actual de nuestro deploy en la API\n\nkubectl edit deploy mario-deploy\n\n\nNos aparecerá la definición que está actualmente en la API de kubernetes, lo que\npodemos hacer es editar el número de réplicas y esto nos debería escalar el\nnúmero de instancias de pods\n\n * Otra forma es usando el comando scale\n\nkubectl scale deployment mario-deploy --replicas=3\n\n\nEn cualquiera de los 2 casos si comprobamos los pods que tenemos deberían haber\naumentado\n\n\n\nY lo mismo podemos hacer para reducirlos, si disminuimos el número de replicas\nnos eliminará el número de instancias activas\n\nkubectl scale deployment mario-deploy --replicas=1\n\n\nComprobamos que empieza con la eliminación de 2 de las instancias y al final nos\ndeja solo con una\n\n\n\nActualizando versiones de nuestra aplicación y rollback\nTodo lo que hemos visto en este post está muy bien, pero normalmente ninguna\naplicación se mantiene en la misma versión para siempre, como tal, es necesario\nactualizarla en algún momento, en esta sección veremos como cambiar la versión\nde nuestra imagen y también como hacer rollback en caso de que algo falle. Todo\nesto es realmente sencillo gracias a kubernetes.\n\nPrimero de todo vamos a seleccionar una imagen que tenga varias versiones, para\nno complicarnos creando nosotros un contenedor específico me he decantado por\nusar la imagen oficial de NGINX [https://hub.docker.com/_/nginx], como se puede\nver entre los tags de versiones tenemos dos que son fáciles de diferencias \nstable y latest\n\n\n\nPrimero creemos nuestro deployment con la imagen stable, para ello crearemos un\nfichero yaml con estos datos\n\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: nginx-deploy\n  name: nginx-deploy\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: nginx-deploy\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: nginx-deploy\n    spec:\n      containers:\n      - image: nginx:stable \n        name: nginx-pod \n        ports:\n        - containerPort: 80\n        resources: {}\nstatus: {}\n\n\nComo véis tenemos 2 replicas de la versión nginx:stable, ejecutemos el comando\ncrear y comprobemos que pasa (recomiendo eliminar todo lo que hicimos\nanteriormente, recordar con kubectl delete ....)\n\nkubectl create -f nginx-deploy.yaml\n\n\n\n\nYa tenemos nuestros 2 pods que hemos definido en el deploy, ahora comprobemos la\nversión con la que se han creado con el comando describe\n\nkubectl describe deploy nginx-deploy\n\n\n\n\nSi nos fijamos tenemos dentro de containers la imagen seleccionada que es \nnginx:stable, ahora veamos las opciones para actualizar de versión\n\n 1. Usar el comando apply\n    Tenemos la posibilidad de actualizar la configuración actual de un objeto en\n    kubernetes usando el comando apply, modificando nuestro fichero de deploy.\n    Cambiemos la versión en nuestro fichero\n\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: nginx-deploy\n  name: nginx-deploy\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: nginx-deploy\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: nginx-deploy\n    spec:\n      containers:\n      - image: nginx:latest \n        name: nginx-pod \n        ports:\n        - containerPort: 80\n        resources: {}\nstatus: {}\n\n\nY ejecutemos el comando\n\nkubectl apply -f nginx-deploy.yaml\n\n\nSi ahora ejecutamos nuestros comandos get pods y describe veremos como tenemos\notra versión ya desplegada.\n\n\n\n 2. Es posible, al igual que hicimos antes, usar el comando edit, para modificar\n    el estado actual que está en la api de kubernetes (en teoría funciona pero a\n    mi me da problemas, aviso por si acaso)\n\nkubectl edit deploy nginx-deploy\n\n\n 3. Tenemos la opción de modificar directamente la imagen de un deployment con\n    la opción set\n\nkubectl set image deployments/nginx-deploy nginx-pod=nginx:latest\n\n\nEste comando indicamos que modifique la imagen dentro del \ndeployment/nginx-deploy de los pods con nombre nginx-pod usando la imagen \nnginx-latest. Si nos fijamos en nuestra definición de nuestro objeto deployment \nel nombre que yo le he puesto es el de nginx-pod\n\nSi comprobaramos nuestros ya típicos get pods y describe otra vez veríamos lo\nsiguiente\n\n\n\nOtra vez de nuevo nos ha cambiado la versión correctamente.\n\nComo tal tenemos otro comando para comprobar que la actualización ha ido\ncorrectamente que es el comando rollout\n\nkubectl rollout status deployment nginx-deploy\n\n\n\n\nEste comando nos confirma que la actualización ha ido correctamente.\n\nHaciendo rollback\nYa hemos visto como actualizar pero... ¿y si todo se ha roto al actualizar y\nqueremos volver atras? Pues en este caso es tan sencillo como...\n\nkubectl rollout undo deployment nginx-deploy\n\n\nEt voilá\n\n\n\nNuestra versión vuelve a ser la de nginx:stable. Creo que en este caso no tengo\nmucho más que decir, solo comentar que esto se podría automatizar con los \nhealthchecks que vimos en post anteriores (revisar la doc oficial que yo no he\nentrado en tanto detalle de momento), esto han sido unos primeros pasos para ver\ncomo lo podemos hacer nosotros todo manualmente.\n\nNo me enrollo más, espero que os haya gustado este post con el escalado y el\nrollback de nuestras aplicaciones, sin mucho más nos vemos en el siguienteeeee\nun abrazoooor","html":"<!--kg-card-begin: markdown--><p>Hasta ahora en los post anteriores hemos estado trabajando con solo un POD o una instancia de cada POD. Pero para realmente tener ese escenario no necesitamos <em>kubernetes</em> podiamos hacerlo igual con <em>docker</em> o con <em>docker-conpose</em>. Veamos ahora realmente algo de lo que nos ofrece realmente kubernetes que es la posibilidad de gestionar multiples instancias de un mismo POD.</p>\n<p>Esto lo hace kubernetes posible haciendo uso de los objetos con tipo <em>Deployment</em>, es un objeto muy similar al POD con el que hemos estado trabajando solo que nos permite indicarle cuantas instancias queremos de un mismo pod. Para ver un ejemplo podemos hacer, como siempre, uso de un comando <em>dry-run</em>:</p>\n<pre><code class=\"language-shell\">kubectl create deployment mario-deploy --image=pengbai/docker-supermario:latest --port=8080 --dry-run=client -o yaml &gt; mario-deployment.yaml\n</code></pre>\n<p>Esto nos creará un documento similar a este:</p>\n<pre><code class=\"language-yaml\">apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: mario-deploy\n  name: mario-deploy\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: mario-deploy\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: mario-deploy\n    spec:\n      containers:\n      - image: pengbai/docker-supermario:latest\n        name: docker-supermario\n        ports:\n        - containerPort: 8080\n        resources: {}\nstatus: {}\n</code></pre>\n<p>Veamos un poco que es lo que tenemos aquí:</p>\n<ul>\n<li><strong>metadata</strong>: Ya hemos visto en el resto de posts que es esto. Contiene <em>labels</em> identificativos que nos pueden ser utiles para hacer referencia desde otros objetos del cluster de kubernetes. Y también tiene su <em>name</em> para identificar este objeto como tal.</li>\n</ul>\n<p>Dentro del <strong>spec</strong>, que recordemos que es donde realmente se define la <em>magia</em> tenemos nuevo:</p>\n<ul>\n<li><strong>replicas</strong>: Creo que es autoexplicativo. Podemos indicar la cantidad de instancias que queremos de un tipo de POD.</li>\n<li><strong>selector</strong>: Al igual que pasaba con el objeto de tipo <em>service</em>, dentro de este selector indicamos los labels que tienen los pods que debe gestionar este objeto de tipo deployment.</li>\n<li><strong>template</strong>: Aquí es donde definimos el POD en cuestión que tiene que usar como plantilla, es decir, usando un objeto de tipo deployment ya no es necesario tener por otro lado un objeto de tipo POD, puede estar definido dentro de esta template (si los comparamos la definión debe ser muy similar)</li>\n</ul>\n<p>Una vez repasadas más o menos las diferencias veamos que pasa, <strong>cambiemos la cantidad de replicas a 2</strong> y ejecutemos el típico create</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.05.28.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.05.28\"></p>\n<p>Si comprobamos los pods que tenemos ahora veremos algo distinto</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.07.34.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.07.34\"></p>\n<p>Vemos como ahora ahora los pods se crean de otra manera, contienen el nombre del objeto deploy y además una conjunto alfanumerico, eso convierte el nombre del pod un identificador único. Además de comprobar los pods, ahora podemos usar otro comando específico para deployments</p>\n<pre><code class=\"language-shell\">kubectl get deployments\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.10.29.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.10.29\"></p>\n<p>Que como podemos comprar nos muestra cuando pods tiene el deployment, cuandos estan activos y cuantos estan actualizados con la última versión (ya hablaremos de esto). Si ahora nosotros eliminamos uno de los pods, veremos como automáticamente nos crea uno nuevo.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.12.43.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.12.43\"></p>\n<p>Kubernetes, como siempre, en su ciclo de vida (llamada bucle de reconciliación) comprueba que el estado de los objetos que están en funcionamiento cumple con sus definiones/plantillas y si no, pues actua en consecuencia, en este caso con la creación de un nuevo POD usando la plantilla que tiene el objeto de tipo <em>deployment</em>.</p>\n<p>Pero claro ahora mismo hemos definido de antemano el número de replicas que queremos de un mismo POD dentro de la plantilla de deploy, pero... ¿y si tengo que <em>escalar</em> mi aplicación ahora mismo entando en producción? pues para ello tenemos varias opciones:</p>\n<ul>\n<li>Editar el estado actual de nuestro deploy en la API</li>\n</ul>\n<pre><code class=\"language-shell\">kubectl edit deploy mario-deploy\n</code></pre>\n<p>Nos aparecerá la definición que está actualmente en la API de kubernetes, lo que podemos hacer es editar el número de réplicas y esto nos debería escalar el número de instancias de pods</p>\n<ul>\n<li>Otra forma es usando el comando <em>scale</em></li>\n</ul>\n<pre><code class=\"language-shell\">kubectl scale deployment mario-deploy --replicas=3\n</code></pre>\n<p>En cualquiera de los 2 casos si comprobamos los pods que tenemos deberían haber aumentado</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.28.51.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.28.51\"></p>\n<p>Y lo mismo podemos hacer para reducirlos, si disminuimos el número de replicas nos eliminará el número de instancias activas</p>\n<pre><code class=\"language-shell\">kubectl scale deployment mario-deploy --replicas=1\n</code></pre>\n<p>Comprobamos que empieza con la eliminación de 2 de las instancias y al final nos deja solo con una</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.30.59.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.30.59\"></p>\n<h3 id=\"actualizandoversionesdenuestraaplicacinyrollback\">Actualizando versiones de nuestra aplicación y rollback</h3>\n<p>Todo lo que hemos visto en este post está muy bien, pero normalmente ninguna aplicación se mantiene en la misma versión para siempre, como tal, es necesario actualizarla en algún momento, en esta sección veremos como cambiar la versión de nuestra imagen y también como hacer rollback en caso de que algo falle. Todo esto es realmente sencillo gracias a kubernetes.</p>\n<p>Primero de todo vamos a seleccionar una imagen que tenga varias versiones, para no complicarnos creando nosotros un contenedor específico me he decantado por usar la imagen oficial de <a href=\"https://hub.docker.com/_/nginx\">NGINX</a>, como se puede ver entre los tags de versiones tenemos dos que son fáciles de diferencias <em>stable</em> y <em>latest</em></p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.06.18.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.06.18\"></p>\n<p>Primero creemos nuestro deployment con la imagen stable, para ello crearemos un fichero yaml con estos datos</p>\n<pre><code class=\"language-yaml\">apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: nginx-deploy\n  name: nginx-deploy\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: nginx-deploy\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: nginx-deploy\n    spec:\n      containers:\n      - image: nginx:stable \n        name: nginx-pod \n        ports:\n        - containerPort: 80\n        resources: {}\nstatus: {}\n</code></pre>\n<p>Como véis tenemos 2 replicas de la versión <strong>nginx:stable</strong>, ejecutemos el comando crear y comprobemos que pasa (recomiendo eliminar todo lo que hicimos anteriormente, recordar con <em>kubectl delete ....</em>)</p>\n<pre><code class=\"language-shell\">kubectl create -f nginx-deploy.yaml\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.10.17.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.10.17\"></p>\n<p>Ya tenemos nuestros 2 pods que hemos definido en el deploy, ahora comprobemos la versión con la que se han creado con el comando describe</p>\n<pre><code class=\"language-shell\">kubectl describe deploy nginx-deploy\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.11.36.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.11.36\"></p>\n<p>Si nos fijamos tenemos dentro de <em>containers</em> la imagen seleccionada que es <em>nginx:stable</em>, ahora veamos las opciones para actualizar de versión</p>\n<ol>\n<li><strong>Usar el comando apply</strong><br>\nTenemos la posibilidad de actualizar la configuración actual de un objeto en kubernetes usando el comando <em>apply</em>, modificando nuestro fichero de deploy. Cambiemos la versión en nuestro fichero</li>\n</ol>\n<pre><code class=\"language-yaml\">apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  creationTimestamp: null\n  labels:\n    app: nginx-deploy\n  name: nginx-deploy\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: nginx-deploy\n  strategy: {}\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: nginx-deploy\n    spec:\n      containers:\n      - image: nginx:latest \n        name: nginx-pod \n        ports:\n        - containerPort: 80\n        resources: {}\nstatus: {}\n</code></pre>\n<p>Y ejecutemos el comando</p>\n<pre><code class=\"language-shell\">kubectl apply -f nginx-deploy.yaml\n</code></pre>\n<p>Si ahora ejecutamos nuestros comandos <em>get pods y describe</em> veremos como tenemos otra versión ya desplegada.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.18.58.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.18.58\"></p>\n<ol start=\"2\">\n<li>Es posible, al igual que hicimos antes, usar el comando edit, para modificar el estado actual que está en la api de kubernetes (en teoría funciona pero a mi me da problemas, aviso por si acaso)</li>\n</ol>\n<pre><code class=\"language-shell\">kubectl edit deploy nginx-deploy\n</code></pre>\n<ol start=\"3\">\n<li>Tenemos la opción de modificar directamente la imagen de un deployment con la opción <em>set</em></li>\n</ol>\n<pre><code class=\"language-shell\">kubectl set image deployments/nginx-deploy nginx-pod=nginx:latest\n</code></pre>\n<p>Este comando indicamos que modifique la imagen dentro del <strong>deployment/nginx-deploy</strong> de los pods con nombre <strong>nginx-pod</strong> usando la imagen <strong>nginx-latest</strong>. Si nos fijamos en nuestra definición de nuestro objeto <em>deployment</em> el nombre que yo le he puesto es el de <strong>nginx-pod</strong></p>\n<p>Si comprobaramos nuestros ya típicos <em>get pods y describe</em> otra vez veríamos lo siguiente</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.26.13.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.26.13\"></p>\n<p>Otra vez de nuevo nos ha cambiado la versión correctamente.</p>\n<p>Como tal tenemos otro comando para comprobar que la actualización ha ido correctamente que es el comando <em>rollout</em></p>\n<pre><code class=\"language-shell\">kubectl rollout status deployment nginx-deploy\n</code></pre>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.28.29.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.28.29\"></p>\n<p>Este comando nos confirma que la actualización ha ido correctamente.</p>\n<h4 id=\"haciendorollback\">Haciendo rollback</h4>\n<p>Ya hemos visto como actualizar pero... ¿y si todo se ha roto al actualizar y queremos volver atras? Pues en este caso es tan sencillo como...</p>\n<pre><code class=\"language-shell\">kubectl rollout undo deployment nginx-deploy\n</code></pre>\n<p>Et voilá</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.31.00.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.31.00\"></p>\n<p>Nuestra versión vuelve a ser la de <strong>nginx:stable</strong>. Creo que en este caso no tengo mucho más que decir, solo comentar que esto se podría automatizar con los <em>healthchecks</em> que vimos en post anteriores (revisar la doc oficial que yo no he entrado en tanto detalle de momento), esto han sido unos primeros pasos para ver como lo podemos hacer nosotros todo manualmente.</p>\n<p>No me enrollo más, espero que os haya gustado este post con el escalado y el rollback de nuestras aplicaciones, sin mucho más nos vemos en el siguienteeeee un abrazoooor</p>\n<!--kg-card-end: markdown-->","url":"https://jlgarcia.fulldev.ninja/docker-xii-kubernetes-vii-multiplicando-pods/","canonical_url":null,"uuid":"6370af59-a16a-42d5-baa7-364d28f09892","codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"6003341fa6c0f5058bff4d9e","reading_time":6,"send_email_when_published":false,"email_subject":null,"childHtmlRehype":{"html":"<!--kg-card-begin: markdown--><p>Hasta ahora en los post anteriores hemos estado trabajando con solo un POD o una instancia de cada POD. Pero para realmente tener ese escenario no necesitamos <em>kubernetes</em> podiamos hacerlo igual con <em>docker</em> o con <em>docker-conpose</em>. Veamos ahora realmente algo de lo que nos ofrece realmente kubernetes que es la posibilidad de gestionar multiples instancias de un mismo POD.</p>\n<p>Esto lo hace kubernetes posible haciendo uso de los objetos con tipo <em>Deployment</em>, es un objeto muy similar al POD con el que hemos estado trabajando solo que nos permite indicarle cuantas instancias queremos de un mismo pod. Para ver un ejemplo podemos hacer, como siempre, uso de un comando <em>dry-run</em>:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl create deployment mario-deploy --image<span class=\"token operator\">=</span>pengbai/docker-supermario:latest --port<span class=\"token operator\">=</span><span class=\"token number\">8080</span> --dry-run<span class=\"token operator\">=</span>client -o yaml <span class=\"token operator\">></span> mario-deployment.yaml\n</code></pre></div>\n<p>Esto nos creará un documento similar a este:</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> apps/v1\n<span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Deployment\n<span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n  <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> mario<span class=\"token punctuation\">-</span>deploy\n  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> mario<span class=\"token punctuation\">-</span>deploy\n<span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">replicas</span><span class=\"token punctuation\">:</span> <span class=\"token number\">1</span>\n  <span class=\"token key atrule\">selector</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">matchLabels</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> mario<span class=\"token punctuation\">-</span>deploy\n  <span class=\"token key atrule\">strategy</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">template</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n      <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> mario<span class=\"token punctuation\">-</span>deploy\n    <span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">containers</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> pengbai/docker<span class=\"token punctuation\">-</span>supermario<span class=\"token punctuation\">:</span>latest\n        <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> docker<span class=\"token punctuation\">-</span>supermario\n        <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">containerPort</span><span class=\"token punctuation\">:</span> <span class=\"token number\">8080</span>\n        <span class=\"token key atrule\">resources</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token key atrule\">status</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre></div>\n<p>Veamos un poco que es lo que tenemos aquí:</p>\n<ul>\n<li><strong>metadata</strong>: Ya hemos visto en el resto de posts que es esto. Contiene <em>labels</em> identificativos que nos pueden ser utiles para hacer referencia desde otros objetos del cluster de kubernetes. Y también tiene su <em>name</em> para identificar este objeto como tal.</li>\n</ul>\n<p>Dentro del <strong>spec</strong>, que recordemos que es donde realmente se define la <em>magia</em> tenemos nuevo:</p>\n<ul>\n<li><strong>replicas</strong>: Creo que es autoexplicativo. Podemos indicar la cantidad de instancias que queremos de un tipo de POD.</li>\n<li><strong>selector</strong>: Al igual que pasaba con el objeto de tipo <em>service</em>, dentro de este selector indicamos los labels que tienen los pods que debe gestionar este objeto de tipo deployment.</li>\n<li><strong>template</strong>: Aquí es donde definimos el POD en cuestión que tiene que usar como plantilla, es decir, usando un objeto de tipo deployment ya no es necesario tener por otro lado un objeto de tipo POD, puede estar definido dentro de esta template (si los comparamos la definión debe ser muy similar)</li>\n</ul>\n<p>Una vez repasadas más o menos las diferencias veamos que pasa, <strong>cambiemos la cantidad de replicas a 2</strong> y ejecutemos el típico create</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.05.28.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.05.28\"></p>\n<p>Si comprobamos los pods que tenemos ahora veremos algo distinto</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.07.34.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.07.34\"></p>\n<p>Vemos como ahora ahora los pods se crean de otra manera, contienen el nombre del objeto deploy y además una conjunto alfanumerico, eso convierte el nombre del pod un identificador único. Además de comprobar los pods, ahora podemos usar otro comando específico para deployments</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl get deployments\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.10.29.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.10.29\"></p>\n<p>Que como podemos comprar nos muestra cuando pods tiene el deployment, cuandos estan activos y cuantos estan actualizados con la última versión (ya hablaremos de esto). Si ahora nosotros eliminamos uno de los pods, veremos como automáticamente nos crea uno nuevo.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.12.43.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.12.43\"></p>\n<p>Kubernetes, como siempre, en su ciclo de vida (llamada bucle de reconciliación) comprueba que el estado de los objetos que están en funcionamiento cumple con sus definiones/plantillas y si no, pues actua en consecuencia, en este caso con la creación de un nuevo POD usando la plantilla que tiene el objeto de tipo <em>deployment</em>.</p>\n<p>Pero claro ahora mismo hemos definido de antemano el número de replicas que queremos de un mismo POD dentro de la plantilla de deploy, pero... ¿y si tengo que <em>escalar</em> mi aplicación ahora mismo entando en producción? pues para ello tenemos varias opciones:</p>\n<ul>\n<li>Editar el estado actual de nuestro deploy en la API</li>\n</ul>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl edit deploy mario-deploy\n</code></pre></div>\n<p>Nos aparecerá la definición que está actualmente en la API de kubernetes, lo que podemos hacer es editar el número de réplicas y esto nos debería escalar el número de instancias de pods</p>\n<ul>\n<li>Otra forma es usando el comando <em>scale</em></li>\n</ul>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl scale deployment mario-deploy --replicas<span class=\"token operator\">=</span><span class=\"token number\">3</span>\n</code></pre></div>\n<p>En cualquiera de los 2 casos si comprobamos los pods que tenemos deberían haber aumentado</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.28.51.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.28.51\"></p>\n<p>Y lo mismo podemos hacer para reducirlos, si disminuimos el número de replicas nos eliminará el número de instancias activas</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl scale deployment mario-deploy --replicas<span class=\"token operator\">=</span><span class=\"token number\">1</span>\n</code></pre></div>\n<p>Comprobamos que empieza con la eliminación de 2 de las instancias y al final nos deja solo con una</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.30.59.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-20.30.59\"></p>\n<h3 id=\"actualizandoversionesdenuestraaplicacinyrollback\">Actualizando versiones de nuestra aplicación y rollback</h3>\n<p>Todo lo que hemos visto en este post está muy bien, pero normalmente ninguna aplicación se mantiene en la misma versión para siempre, como tal, es necesario actualizarla en algún momento, en esta sección veremos como cambiar la versión de nuestra imagen y también como hacer rollback en caso de que algo falle. Todo esto es realmente sencillo gracias a kubernetes.</p>\n<p>Primero de todo vamos a seleccionar una imagen que tenga varias versiones, para no complicarnos creando nosotros un contenedor específico me he decantado por usar la imagen oficial de <a href=\"https://hub.docker.com/_/nginx\">NGINX</a>, como se puede ver entre los tags de versiones tenemos dos que son fáciles de diferencias <em>stable</em> y <em>latest</em></p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.06.18.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.06.18\"></p>\n<p>Primero creemos nuestro deployment con la imagen stable, para ello crearemos un fichero yaml con estos datos</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> apps/v1\n<span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Deployment\n<span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n  <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n<span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">replicas</span><span class=\"token punctuation\">:</span> <span class=\"token number\">2</span>\n  <span class=\"token key atrule\">selector</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">matchLabels</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n  <span class=\"token key atrule\">strategy</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">template</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n      <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n    <span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">containers</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">:</span>stable \n        <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>pod \n        <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">containerPort</span><span class=\"token punctuation\">:</span> <span class=\"token number\">80</span>\n        <span class=\"token key atrule\">resources</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token key atrule\">status</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre></div>\n<p>Como véis tenemos 2 replicas de la versión <strong>nginx:stable</strong>, ejecutemos el comando crear y comprobemos que pasa (recomiendo eliminar todo lo que hicimos anteriormente, recordar con <em>kubectl delete ....</em>)</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl create -f nginx-deploy.yaml\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.10.17.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.10.17\"></p>\n<p>Ya tenemos nuestros 2 pods que hemos definido en el deploy, ahora comprobemos la versión con la que se han creado con el comando describe</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl describe deploy nginx-deploy\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.11.36.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.11.36\"></p>\n<p>Si nos fijamos tenemos dentro de <em>containers</em> la imagen seleccionada que es <em>nginx:stable</em>, ahora veamos las opciones para actualizar de versión</p>\n<ol>\n<li><strong>Usar el comando apply</strong><br>\nTenemos la posibilidad de actualizar la configuración actual de un objeto en kubernetes usando el comando <em>apply</em>, modificando nuestro fichero de deploy. Cambiemos la versión en nuestro fichero</li>\n</ol>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> apps/v1\n<span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Deployment\n<span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n  <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n<span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">replicas</span><span class=\"token punctuation\">:</span> <span class=\"token number\">2</span>\n  <span class=\"token key atrule\">selector</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">matchLabels</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n  <span class=\"token key atrule\">strategy</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">template</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n      <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>deploy\n    <span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">containers</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">:</span>latest \n        <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> nginx<span class=\"token punctuation\">-</span>pod \n        <span class=\"token key atrule\">ports</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">containerPort</span><span class=\"token punctuation\">:</span> <span class=\"token number\">80</span>\n        <span class=\"token key atrule\">resources</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token key atrule\">status</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre></div>\n<p>Y ejecutemos el comando</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl apply -f nginx-deploy.yaml\n</code></pre></div>\n<p>Si ahora ejecutamos nuestros comandos <em>get pods y describe</em> veremos como tenemos otra versión ya desplegada.</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.18.58.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.18.58\"></p>\n<ol start=\"2\">\n<li>Es posible, al igual que hicimos antes, usar el comando edit, para modificar el estado actual que está en la api de kubernetes (en teoría funciona pero a mi me da problemas, aviso por si acaso)</li>\n</ol>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl edit deploy nginx-deploy\n</code></pre></div>\n<ol start=\"3\">\n<li>Tenemos la opción de modificar directamente la imagen de un deployment con la opción <em>set</em></li>\n</ol>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl <span class=\"token builtin class-name\">set</span> image deployments/nginx-deploy nginx-pod<span class=\"token operator\">=</span>nginx:latest\n</code></pre></div>\n<p>Este comando indicamos que modifique la imagen dentro del <strong>deployment/nginx-deploy</strong> de los pods con nombre <strong>nginx-pod</strong> usando la imagen <strong>nginx-latest</strong>. Si nos fijamos en nuestra definición de nuestro objeto <em>deployment</em> el nombre que yo le he puesto es el de <strong>nginx-pod</strong></p>\n<p>Si comprobaramos nuestros ya típicos <em>get pods y describe</em> otra vez veríamos lo siguiente</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.26.13.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.26.13\"></p>\n<p>Otra vez de nuevo nos ha cambiado la versión correctamente.</p>\n<p>Como tal tenemos otro comando para comprobar que la actualización ha ido correctamente que es el comando <em>rollout</em></p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl rollout status deployment nginx-deploy\n</code></pre></div>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.28.29.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.28.29\"></p>\n<p>Este comando nos confirma que la actualización ha ido correctamente.</p>\n<h4 id=\"haciendorollback\">Haciendo rollback</h4>\n<p>Ya hemos visto como actualizar pero... ¿y si todo se ha roto al actualizar y queremos volver atras? Pues en este caso es tan sencillo como...</p>\n<div class=\"kg-card kg-code-card gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">kubectl rollout undo deployment nginx-deploy\n</code></pre></div>\n<p>Et voilá</p>\n<p><img src=\"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.31.00.png\" alt=\"Captura-de-pantalla-2021-01-16-a-las-21.31.00\"></p>\n<p>Nuestra versión vuelve a ser la de <strong>nginx:stable</strong>. Creo que en este caso no tengo mucho más que decir, solo comentar que esto se podría automatizar con los <em>healthchecks</em> que vimos en post anteriores (revisar la doc oficial que yo no he entrado en tanto detalle de momento), esto han sido unos primeros pasos para ver como lo podemos hacer nosotros todo manualmente.</p>\n<p>No me enrollo más, espero que os haya gustado este post con el escalado y el rollback de nuestras aplicaciones, sin mucho más nos vemos en el siguienteeeee un abrazoooor</p>\n<!--kg-card-end: markdown-->","htmlAst":{"type":"root","children":[{"type":"comment","value":"kg-card-begin: markdown"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Hasta ahora en los post anteriores hemos estado trabajando con solo un POD o una instancia de cada POD. Pero para realmente tener ese escenario no necesitamos "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"kubernetes"}]},{"type":"text","value":" podiamos hacerlo igual con "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker"}]},{"type":"text","value":" o con "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"docker-conpose"}]},{"type":"text","value":". Veamos ahora realmente algo de lo que nos ofrece realmente kubernetes que es la posibilidad de gestionar multiples instancias de un mismo POD."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto lo hace kubernetes posible haciendo uso de los objetos con tipo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"Deployment"}]},{"type":"text","value":", es un objeto muy similar al POD con el que hemos estado trabajando solo que nos permite indicarle cuantas instancias queremos de un mismo pod. Para ver un ejemplo podemos hacer, como siempre, uso de un comando "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"dry-run"}]},{"type":"text","value":":"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl create deployment mario-deploy --image"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":"pengbai/docker-supermario:latest --port"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"8080"}]},{"type":"text","value":" --dry-run"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":"client -o yaml "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":">"}]},{"type":"text","value":" mario-deployment.yaml\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Esto nos creará un documento similar a este:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"apiVersion"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" apps/v1\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"kind"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Deployment\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" mario"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" mario"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"replicas"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"1"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"selector"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"matchLabels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" mario"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"strategy"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"template"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" mario"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"containers"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"image"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" pengbai/docker"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"supermario"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"latest\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" docker"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"supermario\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"containerPort"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"8080"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"resources"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"status"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Veamos un poco que es lo que tenemos aquí:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"metadata"}]},{"type":"text","value":": Ya hemos visto en el resto de posts que es esto. Contiene "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"labels"}]},{"type":"text","value":" identificativos que nos pueden ser utiles para hacer referencia desde otros objetos del cluster de kubernetes. Y también tiene su "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"name"}]},{"type":"text","value":" para identificar este objeto como tal."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Dentro del "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"spec"}]},{"type":"text","value":", que recordemos que es donde realmente se define la "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"magia"}]},{"type":"text","value":" tenemos nuevo:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"replicas"}]},{"type":"text","value":": Creo que es autoexplicativo. Podemos indicar la cantidad de instancias que queremos de un tipo de POD."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"selector"}]},{"type":"text","value":": Al igual que pasaba con el objeto de tipo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"service"}]},{"type":"text","value":", dentro de este selector indicamos los labels que tienen los pods que debe gestionar este objeto de tipo deployment."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"template"}]},{"type":"text","value":": Aquí es donde definimos el POD en cuestión que tiene que usar como plantilla, es decir, usando un objeto de tipo deployment ya no es necesario tener por otro lado un objeto de tipo POD, puede estar definido dentro de esta template (si los comparamos la definión debe ser muy similar)"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Una vez repasadas más o menos las diferencias veamos que pasa, "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"cambiemos la cantidad de replicas a 2"}]},{"type":"text","value":" y ejecutemos el típico create"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.05.28.png","alt":"Captura-de-pantalla-2021-01-16-a-las-20.05.28"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si comprobamos los pods que tenemos ahora veremos algo distinto"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.07.34.png","alt":"Captura-de-pantalla-2021-01-16-a-las-20.07.34"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Vemos como ahora ahora los pods se crean de otra manera, contienen el nombre del objeto deploy y además una conjunto alfanumerico, eso convierte el nombre del pod un identificador único. Además de comprobar los pods, ahora podemos usar otro comando específico para deployments"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl get deployments\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.10.29.png","alt":"Captura-de-pantalla-2021-01-16-a-las-20.10.29"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Que como podemos comprar nos muestra cuando pods tiene el deployment, cuandos estan activos y cuantos estan actualizados con la última versión (ya hablaremos de esto). Si ahora nosotros eliminamos uno de los pods, veremos como automáticamente nos crea uno nuevo."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.12.43.png","alt":"Captura-de-pantalla-2021-01-16-a-las-20.12.43"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Kubernetes, como siempre, en su ciclo de vida (llamada bucle de reconciliación) comprueba que el estado de los objetos que están en funcionamiento cumple con sus definiones/plantillas y si no, pues actua en consecuencia, en este caso con la creación de un nuevo POD usando la plantilla que tiene el objeto de tipo "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"deployment"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Pero claro ahora mismo hemos definido de antemano el número de replicas que queremos de un mismo POD dentro de la plantilla de deploy, pero... ¿y si tengo que "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"escalar"}]},{"type":"text","value":" mi aplicación ahora mismo entando en producción? pues para ello tenemos varias opciones:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Editar el estado actual de nuestro deploy en la API"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl edit deploy mario-deploy\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Nos aparecerá la definición que está actualmente en la API de kubernetes, lo que podemos hacer es editar el número de réplicas y esto nos debería escalar el número de instancias de pods"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Otra forma es usando el comando "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"scale"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl scale deployment mario-deploy --replicas"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"3"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"En cualquiera de los 2 casos si comprobamos los pods que tenemos deberían haber aumentado"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.28.51.png","alt":"Captura-de-pantalla-2021-01-16-a-las-20.28.51"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y lo mismo podemos hacer para reducirlos, si disminuimos el número de replicas nos eliminará el número de instancias activas"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl scale deployment mario-deploy --replicas"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"1"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Comprobamos que empieza con la eliminación de 2 de las instancias y al final nos deja solo con una"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-20.30.59.png","alt":"Captura-de-pantalla-2021-01-16-a-las-20.30.59"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"actualizandoversionesdenuestraaplicacinyrollback"},"children":[{"type":"text","value":"Actualizando versiones de nuestra aplicación y rollback"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Todo lo que hemos visto en este post está muy bien, pero normalmente ninguna aplicación se mantiene en la misma versión para siempre, como tal, es necesario actualizarla en algún momento, en esta sección veremos como cambiar la versión de nuestra imagen y también como hacer rollback en caso de que algo falle. Todo esto es realmente sencillo gracias a kubernetes."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Primero de todo vamos a seleccionar una imagen que tenga varias versiones, para no complicarnos creando nosotros un contenedor específico me he decantado por usar la imagen oficial de "},{"type":"element","tagName":"a","properties":{"href":"https://hub.docker.com/_/nginx"},"children":[{"type":"text","value":"NGINX"}]},{"type":"text","value":", como se puede ver entre los tags de versiones tenemos dos que son fáciles de diferencias "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"stable"}]},{"type":"text","value":" y "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"latest"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.06.18.png","alt":"Captura-de-pantalla-2021-01-16-a-las-21.06.18"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Primero creemos nuestro deployment con la imagen stable, para ello crearemos un fichero yaml con estos datos"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"apiVersion"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" apps/v1\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"kind"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Deployment\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"replicas"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"2"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"selector"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"matchLabels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"strategy"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"template"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"containers"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"image"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"stable \n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"pod \n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"containerPort"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"80"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"resources"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"status"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como véis tenemos 2 replicas de la versión "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"nginx:stable"}]},{"type":"text","value":", ejecutemos el comando crear y comprobemos que pasa (recomiendo eliminar todo lo que hicimos anteriormente, recordar con "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"kubectl delete ...."}]},{"type":"text","value":")"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl create -f nginx-deploy.yaml\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.10.17.png","alt":"Captura-de-pantalla-2021-01-16-a-las-21.10.17"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ya tenemos nuestros 2 pods que hemos definido en el deploy, ahora comprobemos la versión con la que se han creado con el comando describe"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl describe deploy nginx-deploy\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.11.36.png","alt":"Captura-de-pantalla-2021-01-16-a-las-21.11.36"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si nos fijamos tenemos dentro de "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"containers"}]},{"type":"text","value":" la imagen seleccionada que es "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"nginx:stable"}]},{"type":"text","value":", ahora veamos las opciones para actualizar de versión"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ol","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"Usar el comando apply"}]},{"type":"element","tagName":"br","properties":{},"children":[]},{"type":"text","value":"\nTenemos la posibilidad de actualizar la configuración actual de un objeto en kubernetes usando el comando "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"apply"}]},{"type":"text","value":", modificando nuestro fichero de deploy. Cambiemos la versión en nuestro fichero"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"yaml"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-yaml"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"apiVersion"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" apps/v1\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"kind"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" Deployment\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"replicas"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"2"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"selector"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"matchLabels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"strategy"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"template"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"metadata"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"creationTimestamp"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","null","important"]},"children":[{"type":"text","value":"null"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"labels"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"app"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"deploy\n    "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"spec"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"containers"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"image"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"latest \n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"name"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" nginx"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"pod \n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"ports"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"containerPort"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"80"}]},{"type":"text","value":"\n        "},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"resources"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","key","atrule"]},"children":[{"type":"text","value":"status"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"text","value":"\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Y ejecutemos el comando"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl apply -f nginx-deploy.yaml\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si ahora ejecutamos nuestros comandos "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"get pods y describe"}]},{"type":"text","value":" veremos como tenemos otra versión ya desplegada."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.18.58.png","alt":"Captura-de-pantalla-2021-01-16-a-las-21.18.58"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ol","properties":{"start":2},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Es posible, al igual que hicimos antes, usar el comando edit, para modificar el estado actual que está en la api de kubernetes (en teoría funciona pero a mi me da problemas, aviso por si acaso)"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl edit deploy nginx-deploy\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ol","properties":{"start":3},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"Tenemos la opción de modificar directamente la imagen de un deployment con la opción "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"set"}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl "},{"type":"element","tagName":"span","properties":{"className":["token","builtin","class-name"]},"children":[{"type":"text","value":"set"}]},{"type":"text","value":" image deployments/nginx-deploy nginx-pod"},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":"nginx:latest\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Este comando indicamos que modifique la imagen dentro del "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"deployment/nginx-deploy"}]},{"type":"text","value":" de los pods con nombre "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"nginx-pod"}]},{"type":"text","value":" usando la imagen "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"nginx-latest"}]},{"type":"text","value":". Si nos fijamos en nuestra definición de nuestro objeto "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"deployment"}]},{"type":"text","value":" el nombre que yo le he puesto es el de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"nginx-pod"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Si comprobaramos nuestros ya típicos "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"get pods y describe"}]},{"type":"text","value":" otra vez veríamos lo siguiente"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.26.13.png","alt":"Captura-de-pantalla-2021-01-16-a-las-21.26.13"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Otra vez de nuevo nos ha cambiado la versión correctamente."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Como tal tenemos otro comando para comprobar que la actualización ha ido correctamente que es el comando "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"rollout"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl rollout status deployment nginx-deploy\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.28.29.png","alt":"Captura-de-pantalla-2021-01-16-a-las-21.28.29"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Este comando nos confirma que la actualización ha ido correctamente."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h4","properties":{"id":"haciendorollback"},"children":[{"type":"text","value":"Haciendo rollback"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Ya hemos visto como actualizar pero... ¿y si todo se ha roto al actualizar y queremos volver atras? Pues en este caso es tan sencillo como..."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["kg-card","kg-code-card","gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"kubectl rollout undo deployment nginx-deploy\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Et voilá"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"img","properties":{"src":"https://jlgarcia.fulldev.ninja/assets/images/2021/01/Captura-de-pantalla-2021-01-16-a-las-21.31.00.png","alt":"Captura-de-pantalla-2021-01-16-a-las-21.31.00"},"children":[]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Nuestra versión vuelve a ser la de "},{"type":"element","tagName":"strong","properties":{},"children":[{"type":"text","value":"nginx:stable"}]},{"type":"text","value":". Creo que en este caso no tengo mucho más que decir, solo comentar que esto se podría automatizar con los "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"healthchecks"}]},{"type":"text","value":" que vimos en post anteriores (revisar la doc oficial que yo no he entrado en tanto detalle de momento), esto han sido unos primeros pasos para ver como lo podemos hacer nosotros todo manualmente."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"No me enrollo más, espero que os haya gustado este post con el escalado y el rollback de nuestras aplicaciones, sin mucho más nos vemos en el siguienteeeee un abrazoooor"}]},{"type":"text","value":"\n"},{"type":"comment","value":"kg-card-end: markdown"}],"data":{"quirksMode":false}},"tableOfContents":[{"id":"actualizandoversionesdenuestraaplicacinyrollback","heading":"Actualizando versiones de nuestra aplicación y rollback","items":[{"id":"haciendorollback","heading":"Haciendo rollback"}]}]},"featureImageSharp":{"base":"learning-docker-and-k8s-by-practice-3.jpg","publicURL":"/static/8f47faaada0e810968ad97255d3f8852/learning-docker-and-k8s-by-practice-3.jpg","imageMeta":{"width":480,"height":270},"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHXm8RRgf/EABkQAQACAwAAAAAAAAAAAAAAAAIBEQMQE//aAAgBAQABBQKWumR1Bmzv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhABAQEAAAAAAAAAAAAAAAAAAREg/9oACAEBAAY/AoBNf//EABsQAAICAwEAAAAAAAAAAAAAAAERACEgQWFx/9oACAEBAAE/IRg7EmEqQL9qLjT2sP/aAAwDAQACAAMAAAAQ8M//xAAWEQADAAAAAAAAAAAAAAAAAAAQEWH/2gAIAQMBAT8QVH//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEx/9oACAECAQE/EBpyP//EABoQAQEBAQADAAAAAAAAAAAAAAERADEQIUH/2gAIAQEAAT8QZBiMqOD0uIouvYaltghUH6eEHoYA4Tf/2Q==","aspectRatio":1.7857142857142858,"src":"/static/8f47faaada0e810968ad97255d3f8852/e3903/learning-docker-and-k8s-by-practice-3.jpg","srcSet":"/static/8f47faaada0e810968ad97255d3f8852/477ba/learning-docker-and-k8s-by-practice-3.jpg 175w,\n/static/8f47faaada0e810968ad97255d3f8852/06776/learning-docker-and-k8s-by-practice-3.jpg 350w,\n/static/8f47faaada0e810968ad97255d3f8852/e3903/learning-docker-and-k8s-by-practice-3.jpg 480w","srcWebp":"/static/8f47faaada0e810968ad97255d3f8852/61162/learning-docker-and-k8s-by-practice-3.webp","srcSetWebp":"/static/8f47faaada0e810968ad97255d3f8852/9fca7/learning-docker-and-k8s-by-practice-3.webp 175w,\n/static/8f47faaada0e810968ad97255d3f8852/37a4e/learning-docker-and-k8s-by-practice-3.webp 350w,\n/static/8f47faaada0e810968ad97255d3f8852/61162/learning-docker-and-k8s-by-practice-3.webp 480w","sizes":"(max-width: 480px) 100vw, 480px"}}}}}]}},"pageContext":{"slug":"docker-iii-mas-complicado","prev":"docker-iv-development-workflow","next":"docker-ii-creando-contenedores","tag":"docker","limit":3,"skip":0,"primaryTagCount":13,"collectionPaths":{}}},
    "staticQueryHashes": ["1272700106","1676991999","2138873178","2546165603","2681841279","2938721187","293880488","3052966952","4156497161"]}