<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>Fixed Buffer</title>
	<atom:link href="https://www.fixedbuffer.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.fixedbuffer.com/</link>
	<description>Blog de divulgación sobre tecnologías de Microsoft</description>
	<lastBuildDate>Sat, 11 Sep 2021 10:04:11 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.4.8</generator>

<image>
	<url>https://www.fixedbuffer.com/wp-content/uploads/2019/10/cropped-Logo-FixedBuffer-f-blanco-512x512-favicon-1-32x32.png</url>
	<title>Fixed Buffer</title>
	<link>https://www.fixedbuffer.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">151520615</site>	<item>
		<title>¡¡FixedBuffer ha cumplido su tercer año!!</title>
		<link>https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-tercer-ano/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=fixedbuffer-ha-cumplido-su-tercer-ano</link>
					<comments>https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-tercer-ano/#comments</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Sat, 11 Sep 2021 10:04:04 +0000</pubDate>
				<category><![CDATA[Varios]]></category>
		<category><![CDATA[Cumpleaños]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=3374</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 2</span> <span class="rt-label rt-postfix">minutos</span></span>Otro año más llega la vuelta de vacaciones y con ello FixedBuffer cumple un año más. Tres años hace desde que empece esta andadura donde voy publicando mis notas sobre ... </p>
<p class="read-more-container"><a title="¡¡FixedBuffer ha cumplido su tercer año!!" class="read-more button" href="https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-tercer-ano/#more-3374" aria-label="Más en ¡¡FixedBuffer ha cumplido su tercer año!!">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-tercer-ano/">¡¡FixedBuffer ha cumplido su tercer año!!</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 2</span> <span class="rt-label rt-postfix">minutos</span></span>
<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="800" height="800" src="https://www.fixedbuffer.com/wp-content/uploads/2021/09/vela-de-cumpleanos-3-azul-my-karamelli_7975_1.jpeg" alt="" class="wp-image-3375" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/09/vela-de-cumpleanos-3-azul-my-karamelli_7975_1.jpeg 800w, https://www.fixedbuffer.com/wp-content/uploads/2021/09/vela-de-cumpleanos-3-azul-my-karamelli_7975_1-300x300.jpeg 300w, https://www.fixedbuffer.com/wp-content/uploads/2021/09/vela-de-cumpleanos-3-azul-my-karamelli_7975_1-150x150.jpeg 150w, https://www.fixedbuffer.com/wp-content/uploads/2021/09/vela-de-cumpleanos-3-azul-my-karamelli_7975_1-768x768.jpeg 768w" sizes="(max-width: 800px) 100vw, 800px" /></figure>



<p>Otro año más llega la vuelta de vacaciones y con ello FixedBuffer cumple un año más. Tres años hace desde que empece esta andadura donde voy publicando mis notas sobre nuevas tecnologías y sobre cosas que en general me parecen interesantes.</p>



<p>Es cierto que este último año he estado sin escribir desde febrero, la muerte de mi padre, el COVID, cambios de trabajo y demás cosas me han alejado de escribir en el blog (principalmente por no tener ganas o simplemente no creer que tenía nada interesante para contar). La parte buena es que aunque no he escrito activamente si he seguido participando activamente en la comunidad así que al menos si alguien ha querido saber de mí (no alcanzo a imaginar por qué&#8230; xD), no se ha quedado sin la oportunidad.</p>



<p>También voy a aclarar que no todo ha sido malo, y que he cumplido con otras metas personales: he comprado mi casita, sigo siendo MVP y mi mujer todavía no se ha cansado de mí y de todo el tiempo que dedico a estas cosas (toda una santa he de añadir).</p>



<p>Dicho esto, ¿qué va a ser de FixedBuffer de aquí en adelante? Pues la verdad no tengo una idea clara&#8230; Me gustaría y voy a intentar escribir una entrada nueva cada 2-3 semanas, el verano me ha servido para recuperar fuerzas y llevo lo suficiente en mi nuevo trabajo como para haber descubierto varias tecnologías muy interesantes de las que escribir, o de cosas que si bien son básicas, no todo el mundo las conoce. Veremos en que se traduce y si no acabaré solo con las charlas (&#8216;traccionando&#8217; que diría alguno <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> )</p>



<p>Y ya como despedida, solo 2 cosas más. La primera es agradecer a la gente que seguís aquí pese a que no haya escrito demasiado últimamente, la verdad es que ver que el contenido sigue siendo útil es una de las principales razones para seguir tirando líneas <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br>La segunda, como ya empieza a ser tradición, es el top 5 de las entradas del último año:</p>



<ol id="block-519d6160-c1f2-46da-a838-d24dae7be6f0"><li><a href="https://www.fixedbuffer.com/index.php/entity-framework-core-2/">Haciendo fácil el acceso a datos con Entity Framework Core (Parte 2)</a></li><li><a href="https://www.fixedbuffer.com/inyeccion-de-dependencias-en-net-framework/" target="_blank" rel="noreferrer noopener">Inyección de Dependencias en .Net Framework</a></li><li><a href="https://www.fixedbuffer.com/worker-service-como-crear-un-servicio-net-core-3-multiplataforma/">Worker Service: Cómo crear un servicio .Net Core 3 multiplataforma</a></li><li><a href="https://www.fixedbuffer.com/como-medir-tiempos-en-c-y-net-con-precision/" target="_blank" rel="noreferrer noopener">Cómo medir tiempos en C# y .Net con precisión</a></li><li><a href="https://www.fixedbuffer.com/index.php/generacion-de-ficheros-excel-con-closedxml/">Generación de ficheros «Excel» (xlsx) con ClosedXML</a></li></ol>
<p>**La entrada <a href="https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-tercer-ano/">¡¡FixedBuffer ha cumplido su tercer año!!</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-tercer-ano/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3374</post-id>	</item>
		<item>
		<title>Generando y publicando imágenes Docker con GitHub Actions</title>
		<link>https://www.fixedbuffer.com/publicando-imagenes-docker-con-github-actions/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=publicando-imagenes-docker-con-github-actions</link>
					<comments>https://www.fixedbuffer.com/publicando-imagenes-docker-con-github-actions/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 07 Sep 2021 08:00:00 +0000</pubDate>
				<category><![CDATA[Despliegue Continuo]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Integración Continua]]></category>
		<category><![CDATA[Github Actions]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=3342</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 5</span> <span class="rt-label rt-postfix">minutos</span></span>¡Después de muchos meses sin escribir, por fin volvemos a la carga! Han pasado muchas cosas durante estos meses (de las que supongo que hablaré en la entrada de los ... </p>
<p class="read-more-container"><a title="Generando y publicando imágenes Docker con GitHub Actions" class="read-more button" href="https://www.fixedbuffer.com/publicando-imagenes-docker-con-github-actions/#more-3342" aria-label="Más en Generando y publicando imágenes Docker con GitHub Actions">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/publicando-imagenes-docker-con-github-actions/">Generando y publicando imágenes Docker con GitHub Actions</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 5</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="512" src="https://www.fixedbuffer.com/wp-content/uploads/2021/09/Github-docker-1024x512.png" alt="" class="wp-image-3353" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/09/Github-docker-1024x512.png 1024w, https://www.fixedbuffer.com/wp-content/uploads/2021/09/Github-docker-300x150.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2021/09/Github-docker-768x384.png 768w, https://www.fixedbuffer.com/wp-content/uploads/2021/09/Github-docker.png 1280w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>¡Después de muchos meses sin escribir, por fin volvemos a la carga! Han pasado muchas cosas durante estos meses (de las que supongo que hablaré en la entrada de los 3 años), pero entre ellas hay una que ha empujado el tema de esta entrada:<br>Docker está moviendo ficha en lo referente a sus políticas de precios.</p>



<p>Lo que nos lleva a una pregunta obvia, ¿en qué me afecta eso a mí? Pues seguramente en nada, pero tal vez seas una de esas personas que estaban utilizando utilidades como las «builds automáticas» de DockerHub. </p>



<p>Esta es precisamente la parte que más me ha impactado a mí, tengo algunas imágenes Docker que hasta hace poco generaba directamente usando DockerHub, pero ese tiempo se acabó, y ha tocado buscarse otra solución. Esta solución, en la mayoría de los casos va a pasar por utilizar los propios sistemas de CI/CD que ofrecen muchos proveedores, y ya que estamos en GitHub, ¿por qué no usar GitHub Actions para construir la imagen Docker?</p>



<h2 class="wp-block-heading" id="h-generando-una-imagen-docker-con-github">Generando una imagen Docker con GitHub</h2>



<p>No es la primera entrada en este blog sobre <a href="https://www.fixedbuffer.com/tag/github-actions/">qué son y cómo utilizar GitHub Actions</a>, así que podemos ir al turrón y centrarnos en como generar nuestras imágenes Docker. Esto en sí mismo ya es algo muy útil que podemos utilizar como parte de nuestro proceso de integración continua, para validar que no se está rompiendo nada con un cambio.</p>



<p>Para este pequeño ejemplo que estamos haciendo, vamos a utilizar un Dockerfile muy sencillo, ya que solo queremos ver como generar la imagen Docker con Github Actions, no recrearnos en Docker <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Algo como esto por ejemplo:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-Dockerfile">FROM alpine
ENTRYPOINT [&quot;echo&quot;, &quot;Hello World!&quot;]
</code></pre>
</div>



<p>Simplemente usando una imagen &#8216;alpine&#8217;, imprimimos por consola &#8216;Hello World!&#8217;.</p>



<p>El primer paso, como toda GitHub Action, es crear el yam correspondiente dentro de la carpata &#8216;.github/workflows&#8217;, en este caso, algo tan simple como esto:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">name: Docker GitHub

on: [push, pull_request,workflow_dispatch]

jobs:
  build-and-push-images:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

</code></pre>
</div>



<p>Sobre esta Action que de momento no hace nada, vamos a añadir el paso que generará la imagen. Para ello vamos a utilizar la acción <a href="https://github.com/docker/build-push-action" target="_blank" rel="noreferrer noopener">build-push-action</a>, que es su modo más simple, basta con añadir el paso a nuestro &#8216;yaml&#8217;:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">      - name: Build and push Docker image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          context: .  //Contexto para el comando Docker
          push: false //Indicamos si queremos hacer push de la imagen
</code></pre>
</div>



<p>Con esto ya tenemos todo listo, nuestra GitHub Action va a generar una imagen Docker automáticamente.</p>



<h2 class="wp-block-heading" id="h-publicando-una-imagen-docker-en-dockerhub-con-github">Publicando una imagen Docker en DockerHub con GitHub </h2>



<p>Muy bien, tenemos lista nuestra Action y ya genera la imagen Docker, pero esto aún no resuelve el problema inicial, de que no tengo las auto builds de DockerHub para mantener las imágenes actualizadas. Precisamente por eso, hay que ir un paso más allá y subir la imagen al registro.</p>



<p>Lo primero que vamos a hacer, es añadir una nueva GitHub Action más a nuestro &#8216;yaml&#8217; (antes de hacer el build &amp; push, obviamente), con la que haremos un login en DockerHub:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">      - name: Login to DockerHub
        if: github.ref == 'refs/heads/master'
        uses: docker/login-action@v1 
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

</code></pre>
</div>



<blockquote class="wp-block-quote"><p>Ten en cuenta que es necesario <a href="https://docs.docker.com/docker-hub/access-tokens/" target="_blank" rel="noreferrer noopener">crear un token en DockerHub</a> y añadirlo junto al usuario como secreto para el repositorio.</p></blockquote>



<p>Una vez hecho esto, basta con un par de cositas más, editar el paso donde hacemos el build, para indicarle que queremos también hacer un push por un lado, y por otro lado, darle un tag a la imagen:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">      - name: Build and push Docker image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          context: .
          push: true
          tags: 'fixedbuffer/hello'
</code></pre>
</div>



<p>Con esto, ya tenemos lista nuestra imagen Docker, y subida a DockerHub. Pero&#8230; ¿Y si quiero más de un tag? Es muy habitual que cuando creamos una nueva imagen, le demos un tag especifico y además &#8216;latest&#8217;.</p>



<h2 class="wp-block-heading" id="h-gestionando-m-ltiples-nombres-y-etiquetas">Gestionando múltiples nombres y etiquetas</h2>



<p>Vale, ya tenemos el proceso listo, pero como hemos comentado con el tema de los tags, es mejorable para simplificarnos la vida. Es por eso que mi propuesta es utilizar otra GitHub Action que nos va a generar diferentes tags según la configuremos. Esta Action es <a href="https://github.com/docker/metadata-action#tags-input" target="_blank" rel="noreferrer noopener">metadata-action</a>.</p>



<p>Simplemente vamos a tener que configurar la Action y editar un poco la Action con la que generamos la imagen Docker, de modo que utilice como entrada la salida de esta. Lo primero será configurar los metadatos, para lo que añadimos esto a nuestro &#8216;yaml&#8217;:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: |
            fixedbuffer/hello
          tags: |
            latest
            type=sha
</code></pre>
</div>



<p> Como resultado de esta Action, se van a generar las etiquetas &#8216;fixedbuffer/hello:latest&#8217; y &#8216;fixedbuffer/hello:sha-COMMIT-SHA&#8217;. Esta solo es una configuración muy simple para poder probar que funciona, pero realmente ofrece una gran cantidad de opciones, que puedes consultar en el mismo repositorio.</p>



<p>Ahora, vamos a usar esas etiquetas. La Action que genera y publica la imagen se verá así:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">      - name: Build and push Docker image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
</code></pre>
</div>



<p>Listo, ya tenemos solucionado el problema y se va a generar y subir la imagen con ambos tags.</p>



<h2 class="wp-block-heading" id="h-y-si-quiero-usar-otro-regristry-el-de-github-por-ejemplo">¿Y si quiero usar otro regristry? (El de GitHub por ejemplo)</h2>



<p>Buena pregunta, y la verdad es que tal cual hemos planteado nuestro &#8216;yaml&#8217;, es algo muy muy sencillo. Bastaría con hacer login en el regritry que queramos usar (ghcr, acr, ecs&#8230;) y añadir el nombre. Por ejemplo, con DockerHub Y el registry de GitHub nos quedaría una cosa así:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">name: Github Registry

on: [push, pull_request, workflow_dispatch]

env:
  IMAGE_NAME: fixedbuffer/hello

jobs:
  build-and-push-images:
    runs-on: ubuntu-latest
    # Asignamos los permisos sobre los recursos de GitHub
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: |
            ${{ env.IMAGE_NAME}}
            ghcr.io/${{ env.IMAGE_NAME}}
          tags: |
            latest
            type=sha

      - name: Login to DockerHub
        if: github.ref == 'refs/heads/master' # Solo master
        uses: docker/login-action@v1 
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GHCR
        if: github.ref == 'refs/heads/master' # Solo master
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          context: .
          push: github.ref == 'refs/heads/master' # Solo master
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
</code></pre>
</div>



<h2 class="wp-block-heading" id="h-conclusi-n">Conclusión</h2>



<p>La verdad es que en mi caso concreto, perder las auto builds de DockerHub fue un problema, ya que tuve que hacer un trabajo extra para añadir la generación de imágenes directamente desde GitHub. </p>



<p>De todos modos, como puedes comprobar es muy muy sencillo utilizar GitHub Actions para generar imágenes Docker. En este artículo hemos hecho una pequeña review de alto nivel, pero usando las dos Actions principales (docker/build-push-action y docker/metadata-action) podemos configurar incluso las plataformas de destino, usar buildx, &#8230;</p>



<p>Puedes verlo en marcha en <a href="https://github.com/FixedBuffer/docker-build" target="_blank" rel="noreferrer noopener">este repo de GitHub</a> (obviamente xD)</p>
<p>**La entrada <a href="https://www.fixedbuffer.com/publicando-imagenes-docker-con-github-actions/">Generando y publicando imágenes Docker con GitHub Actions</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/publicando-imagenes-docker-con-github-actions/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3342</post-id>	</item>
		<item>
		<title>Añadiendo aprobaciones  manuales en despliegues con Github Actions</title>
		<link>https://www.fixedbuffer.com/anadiendo-aprobaciones-manuales-en-nuestros-despliegues-con-github-actions/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=anadiendo-aprobaciones-manuales-en-nuestros-despliegues-con-github-actions</link>
					<comments>https://www.fixedbuffer.com/anadiendo-aprobaciones-manuales-en-nuestros-despliegues-con-github-actions/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 02 Feb 2021 09:00:00 +0000</pubDate>
				<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[Despliegue Continuo]]></category>
		<category><![CDATA[Integración Continua]]></category>
		<category><![CDATA[Github Actions]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=3158</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutos</span></span>Ha llegado la vuelta de las vacaciones y con ello la vuelta a las buenas costumbres. He de reconocer que después del parón de navidades me ha costado más de ... </p>
<p class="read-more-container"><a title="Añadiendo aprobaciones  manuales en despliegues con Github Actions" class="read-more button" href="https://www.fixedbuffer.com/anadiendo-aprobaciones-manuales-en-nuestros-despliegues-con-github-actions/#more-3158" aria-label="Más en Añadiendo aprobaciones  manuales en despliegues con Github Actions">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/anadiendo-aprobaciones-manuales-en-nuestros-despliegues-con-github-actions/">Añadiendo aprobaciones  manuales en despliegues con Github Actions</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img decoding="async" src="https://www.fixedbuffer.com/wp-content/uploads/2020/03/banner-1024x576.jpg" alt="Imagen ornamental con el logo de Github Actions para la entrada &quot;Añadiendo aprobaciones  manuales en despliegues con Github Actions&quot;" class="wp-image-2251" width="740" height="416" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/03/banner-1024x576.jpg 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/03/banner-300x169.jpg 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/03/banner-768x432.jpg 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/03/banner-624x351.jpg 624w, https://www.fixedbuffer.com/wp-content/uploads/2020/03/banner.jpg 800w" sizes="(max-width: 740px) 100vw, 740px" /></figure></div>



<p>Ha llegado la vuelta de las vacaciones y con ello la vuelta a las buenas costumbres. He de reconocer que después del parón de navidades me ha costado más de lo habitual volver a escribir. Muchos frentes abiertos durante las fiestas y no acababa de encontrar ningún tema del que me apeteciese hablar&#8230; Pero eso por fin ha cambiado y he encontrado algo que personalmente me parece realmente interesante, <strong>Github ha añadido soporte para entornos y aprobaciones manuales.</strong> </p>



<p>He de aclarar aquí, que el soporte a diferentes entornos es anterior a los aprobadores manuales  aunque yo los nombre a la vez.</p>



<h2 class="wp-block-heading" id="h-de-d-nde-venimos">De dónde venimos</h2>



<p>A la gente que me conoce no le sorprende el hecho de que siempre este con un ojo puesto en como avanzan los procesos de CI/CD en diferentes proveedores. Soy firme defensor de los procesos automatizados tanto para la integración como para el despliegue. Muestra de ello son <a href="https://www.fixedbuffer.com/category/ci-cd/">las entradas sobre el tema</a> durante estos años. </p>



<p>Es por eso que cuando aparecieron las Github Actions fue una grata sorpresa, Microsoft hacia una fuerte apuesta por llevar sus herramientas a Github. De hecho, hay una <a href="https://www.fixedbuffer.com/herramientas-de-desarollo-github-actions-como-ci-para-net-core/">entrada específica hablando sobre Github Actions</a>, y tuve la suerte de charlar un rato con <a href="https://twitter.com/gimenete" target="_blank" rel="noreferrer noopener">Alberto Gimeno <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/2615.png" alt="☕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> (@gimenete)</a> en <a href="https://www.youtube.com/watch?v=fCw-ApG9lc0" target="_blank" rel="noreferrer noopener">Github Actions: ¿Hasta dónde podemos llegar?</a>.</p>



<p>El titular de ese momento es que Github Actions estaba todavía muy por detrás de otras herramientas como Azure Pipelines, ya que las posibilidades de CI/CD que teníamos a nuestra disposición se limitaban prácticamente a hacer integraciones y algún despliegue sencillo. Esto por suerte a cambiado como vamos a ver.</p>



<h2 class="wp-block-heading" id="h-por-qu-son-tan-importantes-los-aprobadores-manuales-en-github-actions">¿Por qué son tan importantes los aprobadores manuales en Github Actions?</h2>



<p>Pues aquí no hay mucho donde rascar, los despliegues a producción tienen que ser controlados, cualquier código no puede llegar a producción. De acuerdo que trabajamos con metodologías agiles y desplegamos a producción en ciclos cortos, pero eso no es carta blanca para que los despliegues a producción sean «la jungla».</p>



<p>Aquí vamos a aclarar que desde luego una aprobación manual no tiene por qué ser ni el requisito elegido, ni el único. Existen otros tipos de aprobaciones que no son manuales, pero esto es el mínimo que nos permite tener despliegues controlados.</p>



<h2 class="wp-block-heading" id="h-y-eso-de-los-entornos">¿Y eso de los entornos?</h2>



<p>Más allá de opiniones personales (posiblemente con muchos sesgos), vamos a lo que no tiene discusión, que es el proceso para poder tener aprobadores. El concepto de aprobador manual en Github Actions (y otros muchos proveedores) no es otra cosa más que una acción que debe hacer una persona para poder iniciar los pasos contra cierto entorno.</p>



<p>Esto nos lleva a otro punto interesante entre las novedades de Github, los entornos. Es muy habitual que diferentes etapas del desarrollo tengan diferentes requisitos, por poner un ejemplo, si trabajamos con kubernetes en producción, posiblemente tengamos un cluster potente, autoescalable y bien monitorizado y supervisado con alertas. En cambio, un cluster  de desarrollo puede ser algo sencillito, con apenas configuraciones de seguridad y sin monitorización y/o alertas.</p>



<p>Esto se traduce en una &#8216;entidad&#8217; llamada entorno (&#8216;environment&#8217;) que contendrá valores específicos para ciertas variables de los despliegues como el tamaño del cluster del ejemplo anterior. Este entorno es además sobre el que añadiremos a los distintos aprobadores que deben dar su autorización para que el proceso pueda seguir en él.</p>



<p>Para poder comprobar que efectivamente tenemos diferentes valores en las mismas variables, vamos a utilizar un programa de consola al que vamos a llamar desde los diferentes entornos:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-csharp">class Program
{
    static void Main(string[] args)
    {
        var env = Environment.GetEnvironmentVariable(&quot;MYVAL&quot;);
        int.TryParse(env, out var id);
        var message = id switch
        {
            1 =&gt; &quot;Development&quot;,
            2 =&gt; &quot;Production&quot;,
            _ =&gt; &quot;MYVAL is not defined&quot;
        };
        Console.WriteLine(message);
    }
}
</code></pre>
</div>



<p>Con ese programa listo, simplemente vamos a crear los entornos y a registrar un secreto llamado &#8216;MYVAL&#8217; en ellos, cuyo valor sea 1 y 2 en desarrollo y producción respectivamente.</p>



<p>Para esto, basta con ir a la pestaña de &#8216;Settings&#8217;, dentro de ella al menú &#8216;Environments&#8217; y después pulsar sobre &#8216;New environment&#8217;:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="502" src="https://www.fixedbuffer.com/wp-content/uploads/2021/02/new-env-1024x502.png" alt="La imagen señala la pestaña 'Settings', dentro de ella el botón 'Environments' y dentro de este panel el botón 'New environment'" class="wp-image-3168" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/02/new-env-1024x502.png 1024w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/new-env-300x147.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/new-env-768x377.png 768w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/new-env.png 1337w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>Esto nos permitirá darle un nombre al entorno. Tras esto, pasamos directamente a configurarlo. Es desde esta sección precisamente donde vamos a añadir nuestro secreto de ejemplo &#8216;MYVAL&#8217; con su valor concreto:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="865" height="526" src="https://www.fixedbuffer.com/wp-content/uploads/2021/02/add-secret.png" alt="La imagen señala el botón 'Add Secret' dentro del panel de configuración del entorno" class="wp-image-3169" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/02/add-secret.png 865w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/add-secret-300x182.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/add-secret-768x467.png 768w" sizes="(max-width: 865px) 100vw, 865px" /></figure></div>



<p>Para poder probar que nuestros entornos funcionan correctamente, vamos a crear nuestro workflow para Github Actions donde vamos a compilar nuestro programa de ejemplo y lo vamos a ejecutar tanto en desarrollo como en producción:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">name: CI/CD

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

env:
  PACKAGE_PATH: ./package
  PACKAGE_NAME: console_sample
  CONFIGURATION: Release
  DOTNET_CORE_VERSION: 5.0.x
  DOTNET_CLI_TELEMETRY_OPTOUT: 1
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: ${{ env.DOTNET_CORE_VERSION}}
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore --configuration ${{ env.CONFIGURATION }}
    - name: Publish
      run: dotnet publish --no-build --configuration ${{ env.CONFIGURATION }} --output &quot;${{ env.PACKAGE_PATH }}&quot;
    - name: Publish Artifacts
      uses: actions/upload-artifact@v1.0.0
      with:
        name: ${{ env.PACKAGE_NAME }}
        path: ${{ env.PACKAGE_PATH }}

  development:
    # Avoid to continue if the trigger is a PR
    if: github.event_name == 'push'
    needs: build
    name: Deploy Development Environment
    runs-on: ubuntu-latest
    environment:
      name: Development
    env:
      MYVAL: ${{ secrets.MYVAL }}
    steps:
    # Download artifacts
    - name: Download artifacts
      uses: actions/download-artifact@v2
      with:
        name: ${{ env.PACKAGE_NAME }}
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: ${{ env.DOTNET_CORE_VERSION}}
    - name: Execute 
      run: dotnet ManualApproval.dll    

  # This stage doesn't need to use if because the previous stage applies it
  production:
    needs: development
    name: Deploy Production Environment
    runs-on: ubuntu-latest
    environment:
      name: Production
    env:
      MYVAL: ${{ secrets.MYVAL }}
    steps:
    # Download artifacts
    - name: Download artifacts
      uses: actions/download-artifact@v2
      with:
        name: ${{ env.PACKAGE_NAME }}
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: ${{ env.DOTNET_CORE_VERSION}}
    - name: Execute 
      run: dotnet ManualApproval.dll    

</code></pre>
</div>



<p>Nada raro en el fichero, simplemente es un workflow con 3 jobs, uno donde vamos a generar el artefacto, y 2 para nuestra ejecución (uno por entorno). La novedad aquí es que dentro del job, vamos a indicar el entorno al que pertenece utilizando:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">environment:
  name: Nombre_Entorno
</code></pre>
</div>



<p>Además, para probar que cambia en función del valor que hemos asignado al entorno, creamos una variable que utiliza ese secreto:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-yaml">env:
  MYVAL: ${{ secrets.MYVAL }}
</code></pre>
</div>



<p>Basta con hacer un push al repositorio para que automáticamente se ejecute, y si vamos a la sección de acciones, vamos a poder encontrarnos con que el paso &#8216;Execute&#8217; de cada uno de los jobs, muestra el valor correcto:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="685" height="504" src="https://www.fixedbuffer.com/wp-content/uploads/2021/02/execute.png" alt="La imagen muestra la sección de Actions y el resultado de la ejecución del paso 'Execute' del job Development donde se lee 'Development' en la entrada &quot;Añadiendo aprobaciones  manuales en despliegues con Github Actions&quot;" class="wp-image-3170" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/02/execute.png 685w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/execute-300x221.png 300w" sizes="(max-width: 685px) 100vw, 685px" /></figure></div>



<h2 class="wp-block-heading" id="h-a-adiendo-aprobadores-manuales-a-nuestra-github-action">Añadiendo aprobadores manuales a nuestra Github Action</h2>



<p>Con esto que hemos hecho hasta ahora, ya tenemos 2 entornos diferenciados. Con esto hecho, ya nos queda lo más fácil, añadir aprobaciones manuales a nuestras Github Actions.</p>



<p>Para esto, es suficiente con que vayamos al entorno en concreto que queremos que tenga una aprobación manual, y marcar el check &#8216;Required reviewers&#8217;. Esto nos mostrará un campo de entrada donde vamos a poder buscar a integrantes del repositorio a los que añadir. Aquí hay que tener en cuenta que ahora mismo el número máximo de aprobadores es de 6. Una vez añadidos, simplemente pulsamos el botón &#8216;Save protection rules&#8217;.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="451" height="435" src="https://www.fixedbuffer.com/wp-content/uploads/2021/02/approval.png" alt="La imagen señala como añadir aprobación manual a Github Actions. Señala el check 'Required reviewers', el campo de entrada y el botón 'Save protection rules'" class="wp-image-3177" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/02/approval.png 451w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/approval-300x289.png 300w" sizes="(max-width: 451px) 100vw, 451px" /></figure></div>



<p>Una vez que hemos guardado los cambios, las nuevas ejecuciones de nuestra acción se pararán cuando lleguen al entorno de producción:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="433" src="https://www.fixedbuffer.com/wp-content/uploads/2021/02/view-1024x433.png" alt="La imagen muestra una etiqueta amarilla donde dice: 'JorTurFer requested your review to deploy to Production'" class="wp-image-3178" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/02/view-1024x433.png 1024w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/view-300x127.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/view-768x324.png 768w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/view.png 1160w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>En este punto, se habrá enviado un email a las personas con capacidad de aprobar el proceso, y una vez se tenga la aprobación, el proceso continuará con normalidad.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="582" height="384" src="https://www.fixedbuffer.com/wp-content/uploads/2021/02/confirm.png" alt="La imagen muestra los botónes de aprobación y de rechazo de la acción" class="wp-image-3179" srcset="https://www.fixedbuffer.com/wp-content/uploads/2021/02/confirm.png 582w, https://www.fixedbuffer.com/wp-content/uploads/2021/02/confirm-300x198.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></figure></div>



<h2 class="wp-block-heading" id="h-conclusi-n">Conclusión</h2>



<p>Aunque Github Actions sigue estando un paso por detrás de su hermano mayor Azure Pipelines, está dando unos saltos agigantados para alcanzarlo y seguramente superarlo. Es todo un placer ver como esta herramienta evoluciona y va añadiendo nuevas características.</p>



<p>No olvidemos que es una herramienta gratuita en una gran cantidad de casos, pero que aporta mucho valor en nuestros proyectos. Quizás todavía no esté preparada para grandes proyectos con despliegues extremadamente complejos, pero sí que tiene una madurez suficiente para proyectos pequeños y medianos. En este ejemplo nos hemos limitado a ejecutar una aplicación de consola por sencillez, pero lo habitual sería hacer despliegues directamente.</p>



<p>Por último, aunque la aprobación manual es insuficiente para automatizar totalmente los procesos, abre la puerta a otro tipo de aprobaciones más sofisticadas en el futuro. Y eso sin hablar de que acciones manuales es más que suficiente para una cantidad de casos enorme.</p>



<p>Si quieres, puedes echarle un ojo en detalle al <a href="https://github.com/FixedBuffer/ManualApprovals">repositorio en Github</a> que es como mejor se aprende <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/anadiendo-aprobaciones-manuales-en-nuestros-despliegues-con-github-actions/">Añadiendo aprobaciones  manuales en despliegues con Github Actions</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/anadiendo-aprobaciones-manuales-en-nuestros-despliegues-con-github-actions/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3158</post-id>	</item>
		<item>
		<title>Cómo crear un módulo IoT Edge que use las GPIOs de Raspberry Pi 4</title>
		<link>https://www.fixedbuffer.com/como-crear-un-modulo-io-para-iot-edge-con-raspberry-pi-4/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=como-crear-un-modulo-io-para-iot-edge-con-raspberry-pi-4</link>
					<comments>https://www.fixedbuffer.com/como-crear-un-modulo-io-para-iot-edge-con-raspberry-pi-4/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 15 Dec 2020 09:00:00 +0000</pubDate>
				<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Azure Infrastructure]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Entorno de Trabajo]]></category>
		<category><![CDATA[Lenguajes]]></category>
		<category><![CDATA[Azure IoT Edge]]></category>
		<category><![CDATA[Azure IoT Hub]]></category>
		<category><![CDATA[GPIO]]></category>
		<category><![CDATA[IoT]]></category>
		<category><![CDATA[IoT Hub]]></category>
		<category><![CDATA[Raspberry Pi 4]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=3060</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutos</span></span>Se acercan las navidades y este año en mi carta he pedido una Raspberry Pi 4 con la que poder jugar en mis ratos libres. No soy una persona a ... </p>
<p class="read-more-container"><a title="Cómo crear un módulo IoT Edge que use las GPIOs de Raspberry Pi 4" class="read-more button" href="https://www.fixedbuffer.com/como-crear-un-modulo-io-para-iot-edge-con-raspberry-pi-4/#more-3060" aria-label="Más en Cómo crear un módulo IoT Edge que use las GPIOs de Raspberry Pi 4">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/como-crear-un-modulo-io-para-iot-edge-con-raspberry-pi-4/">Cómo crear un módulo IoT Edge que use las GPIOs de Raspberry Pi 4</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="900" height="450" src="https://www.fixedbuffer.com/wp-content/uploads/2020/12/raspberry-pi-logo.jpg" alt="Imagen ornamental para la entrada: Cómo crear un módulo IO para IoT Edge usando Raspberry Pi 4" class="wp-image-3063" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/12/raspberry-pi-logo.jpg 900w, https://www.fixedbuffer.com/wp-content/uploads/2020/12/raspberry-pi-logo-300x150.jpg 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/12/raspberry-pi-logo-768x384.jpg 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure></div>



<p>Se acercan las navidades y este año en mi carta he pedido una <a href="https://www.raspberrypi.org/products/raspberry-pi-4-model-b/?resellerType=home" target="_blank" rel="noreferrer noopener nofollow">Raspberry Pi 4</a> con la que poder jugar en mis ratos libres. No soy una persona a la que le guste esperar, y no he podido aguantar las ganas de ponerme a probarla con varios proyectos que tenía pendientes. De ahí, que me haya parecido muy interesante dedicar la última entrada del año a uno de esos proyectos que tenía pendiente <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Del título queda bastante claro cuál va a ser el tema de esta entrada, cómo poder gestionar el GPIO (general-purpose input/output) de una Raspberry Pi con un módulo de IoT Edge, y con ella pretendo dar fin a la temática de estas últimas entradas dedicadas al mundo IoT relacionado con Azure y Docker. Por si tienes interés, son todas estas:</p>



<ul><li><a href="https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/"></a><a href="https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/">Cómo crear una imagen docker multiarquitectura desde cero (fixedbuffer.com)</a></li><li><a href="https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/">IoT Hub DPS: Creando dispositivos bajo demanda (fixedbuffer.com)</a></li><li><a href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/">Creando módulos especializados para Azure IoT Edge (fixedbuffer.com)</a></li><li><a href="https://www.plainconcepts.com/es/iot-edge-cargas-trabajo-iot-hub/" target="_blank" rel="noreferrer noopener">IoT Edge: Desplegando cargas de trabajo «on-prem» desde IoT Hub (plainconcepts.com)</a></li><li><a href="https://www.plainconcepts.com/es/iot-hub-enrutando-mensajes-hacia-diferentes-destinos/" target="_blank" rel="noreferrer noopener">IoT Hub: Enrutando mensajes hacia diferentes destinos </a><a href="https://www.plainconcepts.com/es/iot-edge-cargas-trabajo-iot-hub/" target="_blank" rel="noreferrer noopener">(plainconcepts.com)</a></li></ul>



<h2 class="wp-block-heading" id="h-por-qu-una-raspberry-pi">¿Por qué una Raspberry Pi?</h2>



<p>La verdad es que cuando hablamos de IoT, normalmente una pensamos en placas hechas a medida, que trabajan con muy pocos recursos y tienen una función específica muy concreta. Aunque Raspberry Pi es una placa que en su última versión no es precisamente un hardware simplón (hay que tener en cuenta que soporta el 4K, más que muchos ordenadores actuales), sí que es un hardware muy asequible. Además, el número de entradas que nos ofrece no es nada desdeñable:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="588" src="https://www.fixedbuffer.com/wp-content/uploads/2020/12/GPIO-Pinout-Diagram-2-1024x588.png" alt="Diagrama de pines GPIO de la Raspberry Pi 4 Model B" class="wp-image-3082" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/12/GPIO-Pinout-Diagram-2-1024x588.png 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/12/GPIO-Pinout-Diagram-2-300x172.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/12/GPIO-Pinout-Diagram-2-768x441.png 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/12/GPIO-Pinout-Diagram-2-1536x882.png 1536w, https://www.fixedbuffer.com/wp-content/uploads/2020/12/GPIO-Pinout-Diagram-2-2048x1176.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>Precisamente del hecho de que sea un PC sencillo y de muy bajo consumo (11W y eso que tengo un overclock de un 40%), junto a un precio muy razonable, es lo que ha motivado el que esta entrada se haga en base a ella. Realmente, todo lo que vamos a ver aquí es susceptible de aplicar en cualquier otro hardware sobre el que estemos desplegando módulos de IoT Edge y no solo en una Raspberry Pi, incluso el uso de sus GPIO.</p>



<p>Llegados a este punto, voy a hacer una aclaración importante: <strong>Todo lo dicho en esta entrada aplica solo cuando estemos utilizando IoT Edge y no Docker en general</strong>. </p>



<p>Esto es porque hoy en día, el runtime de IoT Edge no nos permite levantar contenedores con cualquier configuración. Esta limitación nos va a obligar a dar más permisos de los que se consideran deseables puesto que nuestro módulo va a necesitar correr con permisos privilegiados como veremos más adelante.</p>



<p>Si por el contrario, queremos acceder al GPIO de Raspberry Pi desde Docker directamente, yo utilizaría un modelo en el que asigne un usuario especifico con acceso al GPIO.</p>



<h2 class="wp-block-heading" id="h-probando-las-gpios-de-una-raspberry-pi">Probando las GPIOs de una Raspberry Pi</h2>



<p>Llegados a este punto, si sigues leyendo esto es porque asumes que quieres utilizar IoT Edge y que por tanto vas a crear un módulo que se despliegue en una Raspberry Pi y que opere su GPIOs.</p>



<p>Como es importante aprender a andar antes de aprender a correr, lo primero que he hecho es crear un pequeño programa que sea capaz de leer una entrada y escribir una salida de las GPIO de Raspberry Pi directamente, sin IoT Edge de por medio.</p>



<p>Para esto, hay que decir que .Net Core tiene soporte directo para este tipo de GPIO a través del paquete &#8216;<a href="https://www.nuget.org/packages/System.Device.Gpio" target="_blank" rel="noreferrer noopener nofollow">System.Device.Gpio</a>&#8216;, con lo que el proceso se simplifica bastante.</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-csharp">using System;
using System.Device.Gpio;

namespace Sample
{
    class Program
    {
        static void Main(string[] args)
        {
            // GPIO 6 which is physical pin 31
            int outPin = 6;
            // GPIO 12 is physical pin 32
            int inPin = 12;

            var controller = new GpioController();

            // Sets the LED pin to output mode so we can switch something on
            controller.OpenPin(outPin, PinMode.Output);

            // Sets the button pin to input mode so we can read a value
            controller.OpenPin(inPin, PinMode.Input);

            // Read pin value
            Console.WriteLine(controller.Read(inPin));

            // Set pin value
            controller.Write(outPin, PinValue.High);

            // Reset pin value
            controller.Write(outPin, PinValue.Low);
        }
    }
}

</code></pre>
</div>



<p>Básicamente, este programa lo que hace es abrir los pines 31 y 32 en modo salida y entrada respectivamente, lee la entrada y escribe la salida. Es un programa muy sencillo pero valida que tenemos nuestro sistema funciona bien si vamos directos a sobre la Raspberry Pi. </p>



<p>Una cosa que me gusta de este paquete NuGet es que podemos crear un callback para los cambios sobre un pin en concreto, por lo que no necesitamos leer todo el tiempo ese pin, sino que se ejecutará el callback siempre que haya cambios.</p>



<p>Por otro lado, en caso de que nuestras entradas no estén soportadas por ese paquete, siempre podemos añadir &#8216;<a href="https://www.nuget.org/packages/Iot.Device.Bindings" target="_blank" rel="noreferrer noopener">Iot.Device.Bindings</a>&#8216; para ampliar la gama de GPIOs soportados.</p>



<h2 class="wp-block-heading" id="h-creando-un-m-dulo-de-iot-edge-que-opere-el-gpio-de-una-raspberry-pi">Creando un módulo de IoT Edge que opere el GPIO de una Raspberry Pi</h2>



<p>Hemos conseguido leer y escribir las GPIO sin problemas, por lo que vamos a llevarnos el código a un módulo de IoT Edge. Esto no tiene mucho misterio si has leído las entradas anteriores relacionadas con el tema, pero por si acaso, he dejado el código <a href="https://github.com/FixedBuffer/Raspi_IoTEdge_IO" target="_blank" rel="noreferrer noopener nofollow">completo en GitHub</a>. </p>



<p>Para no complicar mucho el proceso de manera innecesaria, simplemente estamos haciendo 3 cosas:</p>



<ul><li>Inicializar tanto el GPIO como el módulo.</li><li>Escribir una salida cada 300 milisegundos.</li><li>Leer una entrada cada segundo y enviando el valor a la cola llamada &#8216;output&#8217;.</li></ul>



<p>Una cosa a tener en cuenta, el código no es importante en esta entrada, con lo que hemos visto antes podría ser suficiente. Simplemente lo estamos &#8216;integrando&#8217; un poco más el GPIO de Raspberry Pi en el módulo de IoT Edge.</p>



<p>Digo que el código no es importante, porque precisamente el reto aquí es conseguir que un módulo que corre como un contenedor Docker, gestionado totalmente por el runtime de IoT Edge, sea capaz de acceder al hardware subyacente para poder operar las salidas.</p>



<h2 class="wp-block-heading" id="h-adaptando-el-m-dulo-creado-para-poder-operar-las-gpios">Adaptando el módulo creado para poder operar las GPIOs</h2>



<p>Al principio de la entrada hacia un disclaimer sobre que esta no es la mejor manera si estamos trabajando con Docker directamente en lugar de IoT Edge, y es el momento de explicarlo.</p>



<div class="wp-block-jetpack-markdown"><p>Si ejecutamos el comando <code>ls -l</code> en el directorio <code>/sys/class</code> (que es donde se encuentran los puntos de montaje de parte del hardware de la Raspberry Pi), vamos a encontrarnos con algo como esto:</p>
<pre><code class="language-bash">pi@raspberrypi:/sys/class $ ls -l
total 0
...
drwxrwx--- 2 root gpio 0 Dec 13 16:57 gpio
...
</code></pre>
<p>Esto nos está indicando que, aunque montásemos la ruta directamente en el módulo, esto no va a ser suficiente debido a los permisos del sistema de archivos.</p>
<p>Para que esto funcione, necesitamos que nuestro contenedor se ejecute con el usuario <code>root</code>, o con algún usuario que pertenezca el grupo <code>gpio</code>. Esto con Docker sería un problema muy sencillo de resolver simplemente indicando el modificador <code>-u</code> junto al usuario y grupo con el que queramos ejecutar el contenedor.</p>
</div>



<p>Es precisamente por esto, que para que nuestro módulo funcione vamos a necesitar hacer 2 cosas:</p>



<ul><li>Ejecutar nuestro contenedor como root</li><li>Montar el directorio dentro de nuestro módulo</li></ul>



<p>Aunque sinceramente, el tener que ejecutar el contenedor con privilegios de root me parece algo que se deba evitar siempre que sea posible. El problema aquí es que IoT Edge nos permite definir dentro de su plantilla algunas <a href="https://docs.docker.com/engine/api/v1.32/#operation/ContainerCreate" target="_blank" rel="noreferrer noopener">opciones propias de la creación de contenedores</a>, pero a día de hoy, no se soportan todas (o al menos yo he sido incapaz de hacerlas funcionar, estaré encantado si alguien lo ha conseguido que me deje un comentario).</p>



<div class="wp-block-jetpack-markdown"><p>De hecho, las 2 únicas que he visto que se soportan son <code>image</code> y <code>createOptions</code>, por lo que no podemos indicar el usuario con el que queremos ejecutar el proceso.</p>
<p>Dicho esto, vamos a ver cómo es gracias a <code>createOptions</code> que vamos a conseguir tener al menos la versión menos segura.</p>
<p>Dentro de las opciones que nos ofrece <code>createOptions</code>, encontramos una llamada <code>HostConfig</code>, en cuyo interior podemos definir diferentes <a href="https://docs.docker.com/storage/bind-mounts/">bind mounts</a>. Por otro lado, ya que no podemos elegir el usuario que ejecuta el contenedor, sí que podemos decirle al menos que se ejecute con permisos elevados (<code>root</code>) siempre que el contenedor no defina y utilice otro usuario.</p>
<p>Nuestro módulo teniendo estas dos cosas en cuenta, quedaría algo como esto:</p>
<pre><code class="language-json">&quot;modules&quot;: {
  &quot;IO&quot;: {
    &quot;version&quot;: &quot;1.0&quot;,
    &quot;type&quot;: &quot;docker&quot;,
    &quot;status&quot;: &quot;running&quot;,
    &quot;restartPolicy&quot;: &quot;always&quot;,
    &quot;settings&quot;: {
      &quot;image&quot;: &quot;${MODULES.IO}&quot;,
      &quot;createOptions&quot;: {
        &quot;Privileged&quot;: true,
        &quot;HostConfig&quot;: {
          &quot;Binds&quot;: [
            &quot;/sys:/sys&quot;
          ]
        }
      }
    }
  }
}
</code></pre>
<p>Pero es importante señalar, que eso solo funcionará si no se ha definido otro usuario con el cual ejecutar el contenedor (cosa que es recomendable). Si vamos a los diferentes Dockerfiles que se han generado, podemos ver que en los que no son <code>.debug</code> sí que se está definiendo un usuario específico para el módulo, por lo que debemos comentar esas líneas de los Dockerfile:</p>
<pre><code class="language-Dockerfile">RUN useradd -ms /bin/bash moduleuser
USER moduleuser
</code></pre>
</div>



<h2 class="wp-block-heading" id="h-probando-nuestro-m-dulo">Probando nuestro módulo</h2>



<div class="wp-block-jetpack-markdown"><p>Una vez que hemos terminado con todos estos cambios, ya estamos listos para probar nuestro módulo. Simplemente generamos la imagen del método que prefiramos, hardware dedicado o <code>docker buildx</code> (si te interesa este punto, hablamos de ello en <a href="https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/">Cómo crear una imagen docker multiarquitectura desde cero</a>) y la desplegamos sobre nuestra Raspberry Pi. Previamente le hemos tenido que <a href="https://www.plainconcepts.com/es/iot-edge-cargas-trabajo-iot-hub/">instalar el runtime de IoT Edge</a>.</p>
<p>Una vez que el módulo se haya desplegado, vamos a recibir mensajes desde el dispositivo que se pueden parecer a estos:</p>
<pre><code class="language-bash">[IoTHubMonitor] [12:05:44 AM] Message received from [Raspi/IO]:
&quot;High&quot;
[IoTHubMonitor] [12:05:45 AM] Message received from [Raspi/IO]:
&quot;High&quot;
[IoTHubMonitor] [12:05:46 AM] Message received from [Raspi/IO]:
&quot;Low&quot;
[IoTHubMonitor] [12:05:47 AM] Message received from [Raspi/IO]:
&quot;High&quot;
[IoTHubMonitor] [12:05:48 AM] Message received from [Raspi/IO]:
&quot;Low&quot;
</code></pre>
</div>



<p>Llegados a este punto y si vas siguiendo paso a paso, seguramente en tu caso no esté cambiando el mensaje que recibes. ¿Y dónde está la magia entonces? Pues en algo que no se puede ver, un cable que une físicamente esos dos pines (31 y 32, indicados en el programa):</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img decoding="async" src="https://www.fixedbuffer.com/wp-content/uploads/2020/12/RaspberryPi-Cable-edited-1020x1024.jpeg" alt="La imagen muestra la Raspberry Pi con un cable que uno dos de sus GPIOs" class="wp-image-3087" width="400" height="400" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/12/RaspberryPi-Cable-edited-300x300.jpeg 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/12/RaspberryPi-Cable-edited-150x150.jpeg 150w" sizes="(max-width: 400px) 100vw, 400px" /></figure></div>



<p>Con este simple cable, estamos consiguiendo que la entrada que leemos y enviamos a la cola &#8216;output&#8217; este constantemente cambiando de valor. Con esto tan simple, estamos comprobando que de verdad estamos accediendo a las GPIOs de las Raspberry Pi desde el módulo IoT Edge, ya que estamos cambiando el mensaje en función del valor de la entrada.</p>



<h2 class="wp-block-heading" id="h-conclusi-n">Conclusión</h2>



<p>Aunque hay que hacer algunas cosas que podríamos considerar &#8216;feas&#8217; desde el punto de vista de las buenas prácticas de Docker, deberíamos tener en cuenta que realmente nuestro objetivo es IoT Edge y no Docker. Aclaro esto porque podemos encontrarnos con otras situaciones donde tampoco podamos cumplir al 100% las buenas prácticas y no nos queda otra que esperar a que el soporte vaya siendo más completo.</p>



<p>Dicho esto, personalmente pienso que es una buenísima manera de conseguir mejorar el ciclo de vida de una aplicación que opere entradas y salidas. Estamos aprovechando toda la potencia que nos ofrece IoT Edge (y de la que ya hemos hablado largo y tendido) con el añadido de que podemos operar sensórica real que esté conectada a la Raspberry Pi. Las GPIO integradas nos ofrecen tanto entradas como salidas digitales y <a href="https://es.wikipedia.org/wiki/Modulaci%C3%B3n_por_ancho_de_pulsos" target="_blank" rel="noreferrer noopener nofollow">PWM</a> de serie, con posibilidad de ampliar con entradas y salidas analógicas.</p>
<p>**La entrada <a href="https://www.fixedbuffer.com/como-crear-un-modulo-io-para-iot-edge-con-raspberry-pi-4/">Cómo crear un módulo IoT Edge que use las GPIOs de Raspberry Pi 4</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/como-crear-un-modulo-io-para-iot-edge-con-raspberry-pi-4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3060</post-id>	</item>
		<item>
		<title>Cómo crear una imagen docker multiarquitectura desde cero</title>
		<link>https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=como-crear-imagenes-docker-multiarquitectura</link>
					<comments>https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 24 Nov 2020 09:00:00 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[Docker Desktop]]></category>
		<category><![CDATA[Docker Multi Arch]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=2998</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutos</span></span>Estas últimas semanas han sido apasionantes, Kubecon, DotNetConf,&#8230; Un montón de novedades y nuevas versiones de diferentes softwares y herramientas de las que hablaremos en las próximas semanas. Hoy vengo ... </p>
<p class="read-more-container"><a title="Cómo crear una imagen docker multiarquitectura desde cero" class="read-more button" href="https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/#more-2998" aria-label="Más en Cómo crear una imagen docker multiarquitectura desde cero">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/">Cómo crear una imagen docker multiarquitectura desde cero</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1000" height="600" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker.png" alt="Imagen ornamental de docker" class="wp-image-3005" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker.png 1000w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker-300x180.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker-768x461.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></figure></div>



<p>Estas últimas semanas han sido apasionantes, Kubecon, DotNetConf,&#8230; Un montón de novedades y nuevas versiones de diferentes softwares y herramientas de las que hablaremos en las próximas semanas. Hoy vengo a hablar de algo más &#8216;mundano&#8217;, como generar imágenes Docker. Solo que esta vez vamos a hacer que nuestra imagen sea multiarquitectura.</p>



<blockquote class="wp-block-quote is-style-default"><p>DISCLAIMER: La idea de esta entrada es hablar de cómo crear imágenes Docker multiarquitectura, por lo que no se entra en los detalles sobre crear imágenes Docker en si mismo, sino que se pasa por encima para dar el contexto.</p></blockquote>



<h2 class="wp-block-heading" id="h-qu-es-y-c-mo-funciona-docker">¿Qué es y cómo funciona Docker?</h2>



<p>Antes de empezar a hablar sobre imágenes multiarquitectura, conviene empezar aclarando que es Docker y como funciona. Docker es un sistema de empaquetado de contenedores que nos permite añadir aplicaciones y dependencias en un mismo &#8216;paquete&#8217;, de modo que corra donde corra, siempre funcionará igual. Podríamos decir que es como si cogiéramos una máquina virtual y la distribuyésemos. De este modo, podemos preparar el sistema operativo, sus dependencias y nuestra aplicación, generar un &#8216;paquete&#8217; y ponerlo en marcha allá donde queramos.</p>



<p>Con esto mente, hay que aclarar que docker <strong>es como correr una maquina virtual, pero no es correr una máquina virtual</strong>. Esto quiere decir que, a diferencia de una máquina virtual, aquí no movemos todo el sistema operativo, sino simplemente las &#8216;particularidades&#8217; que tiene, dejando la base común intacta ya que esta base común se comparte con el sistema operativo anfitrión.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="908" height="435" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker-vm.png" alt="La imagen muestra la composición de las aplicaciones en una VM y en Docker, donde en la VM el sistema huésped corre sobre el sistema anfitrión y sobre este huésped las aplicaciones mientras que en Docker las aplicaciones corren sobre el contenedor y este sobre el sistema anfitrión " class="wp-image-3011" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker-vm.png 908w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker-vm-300x144.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/docker-vm-768x368.png 768w" sizes="(max-width: 908px) 100vw, 908px" /></figure></div>



<p>A esta tecnología se la conoce como contenedores y no es algo nuevo, si no que lleva existiendo varios años ya. Su funcionamiento (simplificado) es que vamos a crear <strong>imágenes</strong> que con las dependencias y aplicaciones, y estas son las que vamos a distribuir. Precisamente del hecho de que la parte común se comparta con el sistema operativo anfitrión, es por lo que tenemos que hablar de imágenes Docker para una arquitectura concreta. Por poner un símil, diferentes distribuciones de Linux comparten la misma base común que es el kernel de Linux, diferentes imágenes comparten la misma arquitectura.</p>



<h2 class="wp-block-heading" id="h-por-qu-me-puede-interesar-crear-im-genes-docker-multiarquitectura">¿Por qué me puede interesar crear imágenes Docker multiarquitectura?</h2>



<p>En las últimas entradas estuvimos hablando sobre <a href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/">módulos para IoT Edge</a> y sobre <a href="https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/">aprovisionamiento de dispositivos con IoT Hub</a>. Pues bien, en ese momento no entramos en detalle, aunque hicimos referencia a ello:</p>



<blockquote class="wp-block-quote"><p><em>Actualmente, Docker soporta de forma&nbsp;</em><a href="https://docs.docker.com/engine/reference/commandline/manifest/" target="_blank" rel="noreferrer noopener">experimental el crear manifiestos de imágenes para múltiples arquitecturas</a><em>&nbsp;simultáneamente, siendo el runtime de Docker el que se descarga la imagen de su arquitectura especifica.</em></p><cite><a href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/">Creando módulos especializados para Azure IoT Edge</a></cite></blockquote>



<p>Pues efectivamente, este es un caso muy claro donde seguramente nos interese crear imágenes Docker multiarquitectura. Esto es porque es un escenario común que queramos crear módulos de IoT Edge que trabajen por ejemplo de igual manera si los desplegamos en un microprocesador amd64, arm32, arm64, &#8230;</p>



<p>Estos escenarios solían requerir de diferentes imágenes con una imagen base diferente, y cuyo uso requería de mantener un tag diferente para identificarlos. De este modo no es nada raro encontrarnos con que muchos repositorios de <a href="https://hub.docker.com/" target="_blank" rel="noreferrer noopener">Docker Hub </a>tienen soporte especifico mediante tags para diferentes arquitecturas.</p>



<p>Esto tiene un problema evidente y es que te obliga a especificar un tag concreto en el que indicamos la arquitectura, y dificulta mucho el generalizar en situaciones de despliegue masivo. ¿Realmente necesito saber la arquitectura del dispositivo IoT Edge para ejecutar el un módulo? ¿Realmente es algo que quiera tener en cuenta?</p>



<p>En Python sin ir más lejos, su imagen soporta hasta 10 arquitecturas distintas:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="147" height="233" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/image.png" alt="" class="wp-image-3017"/></figure></div>



<p>En cambio, todas ellas se sirven desde el mismo tag, es decir, están agrupadas y es el propio cliente de Docker el que se baja la imagen concreta que necesita para el caso concreto.</p>



<h2 class="wp-block-heading" id="h-creando-nuestra-primera-imagen-docker-multiarquitectura-docker-manifest">Creando nuestra primera imagen Docker multiarquitectura (Docker Manifest)</h2>



<p>Vamos a partir plantear un ejemplo muy sencillo. Partimos de un escenario donde todas las arquitecturas que queremos soportar ya están soportadas por la imagen base que utilicemos.</p>



<p>Lo primero que vamos a necesitar, es habilitar el soporte para &#8216;<em>Docker Manifest</em>&#8216;. Esta es una característica experimental de Docker por lo que para habilitarlas debemos (extraído y traducido de la documentación oficial):</p>



<blockquote class="wp-block-quote"><p>Para habilitar las características experimentales en el Docker CLI, edite el archivo config.json y ponga experimental a habilitado.<br>Para habilitar las características experimentales en el menú del escritorio de la base Docker, haz clic en Configuración (Preferencias en macOS) &gt; Línea de comandos y luego activa la opción Habilitar características experimentales. Haz clic en Aplicar y reiniciar.</p></blockquote>



<p>Una vez teniendo eso listo, vamos a crear las diferentes imágenes que queremos que se sirvan desde nuestra &#8216;meta imagen&#8217; multiarquitectura. Por ejemplo, podríamos tener un par de Dockerfiles como estos (en el <a href="https://github.com/FixedBuffer/MultiArchDocker" target="_blank" rel="noreferrer noopener nofollow">repositorio de Github</a> puedes encontrar más arquitecturas):</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-Dockerfile"># Dockerfile.amd64
FROM amd64/alpine
CMD echo &quot;Hello world desde amd64/alpine&quot;

# Dockerfile.arm32v6
FROM arm32v6/alpine
CMD echo &quot;Hello world desde arm32v6/alpine&quot;
</code></pre>
</div>



<p>Una vez que tenemos estos dos Dockerfile, vamos a generar sus respectivas imágenes y subirlas a Docker Hub con los comandos:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-bash"># Generamos las imágenes
docker build -f .\Dockerfile.amd64 -t fixedbuffer/multi-arch:amd64  .
docker build -f .\Dockerfile.arm32v6 -t fixedbuffer/multi-arch:arm32v6  .

# Pusheamos las imágenes a Docker Hub
docker push fixedbuffer/multi-arch:amd64
docker push fixedbuffer/multi-arch:arm32v6
</code></pre>
</div>



<p>Una vez que hemos subido las imágenes, vamos a crear nuestro manifiesto que genere esa anhelada imagen Docker multiarquitectura. Para ello simplemente vamos a ejecutar el comando &#8216;<em>docker manifest</em> <em>create</em>&#8216; indicandole el nombre del manifiesto y su tag (que será el nombre de esa &#8216;meta imagen&#8217;) y las imágenes que queramos que la compongan:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-bash">docker manifest create fixedbuffer/multi-arch:latest fixedbuffer/multi-arch:amd64 fixedbuffer/multi-arch:arm32v6
</code></pre>
</div>



<p>En caso de que queramos añadir nuevas imágenes, basta con ejecutar de nuevo el comando con el modificador &#8216;<em>&#8211;amend</em>&#8216;. Una vez que hemos terminado de crear nuestro manifiesto, basta con subirlo a Docker Hub ejecutando:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-bash">docker manifest push docker.io/fixedbuffer/multi-arch:latest
</code></pre>
</div>



<p>Si ahora vamos a Docker Hub, vamos a encontrarnos con que nuestro repositorio tiene una nueva imagen multiarquitectura son la misma etiqueta que nuestro manifiesto. Esta nueva imagen soporta varias arquitecturas:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="107" height="133" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/image-1.png" alt="" class="wp-image-3029"/></figure></div>



<p>Si ahora ejecutamos la imagen en diferentes equipos, vamos a poder comprobar como dependiendo del sistema, se utiliza de manera transparente una u otra imagen:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="543" height="61" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/image-2.png" alt="La imagen muestra el comando docker run desde una raspberry donde la salida es Hello world desde arm32v6/alpine" class="wp-image-3030" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/11/image-2.png 543w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/image-2-300x34.png 300w" sizes="(max-width: 543px) 100vw, 543px" /></figure></div>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="575" height="125" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/image-3.png" alt="La imagen muestra el comando docker run desde un equipo Windows con Docker for Descktop donde la salida es Hello world desde amd64/alpine" class="wp-image-3031" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/11/image-3.png 575w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/image-3-300x65.png 300w" sizes="(max-width: 575px) 100vw, 575px" /></figure></div>



<h2 class="wp-block-heading" id="h-creando-nuestra-segunda-imagen-docker-multiarquitectura-docker-buildx">Creando nuestra segunda imagen Docker multiarquitectura (Docker Buildx)</h2>



<p>Si bien es cierto que esto funciona y funciona bien, se queda un poco a medias en el sentido de que es necesario generar las diferentes imágenes previamente a hacer el manifiesto. Esto significa que vamos a necesitar tener hardware que pueda ejecutar los Dockerfiles y subirlos a Docker Hub.</p>



<p>Otra opción, que personalmente me gusta más por ser muy sencilla de automatizar es <em><code>'<a href="https://docs.docker.com/buildx/working-with-buildx/" target="_blank" rel="noreferrer noopener">docker buildx</a></code>&#8216;</em>. Este comando nos va a permitir definir las diferentes arquitecturas que queremos que soporte nuestra imagen multiarquitectura, y será suficiente con que recibamos ese argumento en nuestro Dockerfile. Por ejemplo:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-Dockerfile">ARG ARCH=
FROM ${ARCH}debian:buster-slim

CMD echo &quot;Hello world desde ${ARCH}debian:buster-slim&quot;
</code></pre>
</div>



<p>Este Dockerfile simplemente recibe como parámetro el tipo de arquitectura que tiene que utilizar, el cual obtendrá desde la propia ejecución del comando:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-bash">docker buildx build --push --tag fixedbuffer/multi-arch:action --platform linux/amd64,linux/arm/v7,linux/arm64 -f Dockerfile.multi .
</code></pre>
</div>



<p>El comando <em>&#8216;docker buildx build</em>&#8216; soporta los mismos parámetros que soporta &#8216;<em>docker build</em>&#8216; de manera normal, pero además nos va a permitir utilizar algunos adicionales como &#8211;push para subir la imagen al terminar, o &#8211;platform para indicarle las diferentes plataformas que queremos soportar.</p>



<p>Una de las grandes ventajas que le veo respecto a Docker Manifest, es que es muy fácil de automatizar, como se puede comprobar en la entrada del propio blog de Docker (en inglés): <a href="https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/">Multi-arch build and images, the simple way &#8211; Docker Blog</a></p>



<h2 class="wp-block-heading" id="h-conclusi-n">Conclusión</h2>



<p>En esta entrada hemos planteado un par de soluciones para poder generar imágenes Docker multiarquitectura, de modo que podamos utilizar la misma &#8216;meta imagen&#8217; en todos nuestros dispositivos, sin tener que preocuparnos de cuál sea la arquitectura de dicho dispositivo.</p>



<p>Si bien es cierto que no es un escenario muy habitual, ya que normalmente las aplicaciones tienen una plataforma objetivo, no está de más conocer este tipo de posibilidades ya que como a mí, pueden sacarte de un apuro cuando generalizas las imágenes Docker.</p>
<p>**La entrada <a href="https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/">Cómo crear una imagen docker multiarquitectura desde cero</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2998</post-id>	</item>
		<item>
		<title>IoT Hub DPS: Creando dispositivos bajo demanda</title>
		<link>https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=iot-hub-dps-creando-dispositivos-bajo-demanda</link>
					<comments>https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 03 Nov 2020 09:00:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Azure Infrastructure]]></category>
		<category><![CDATA[Azure IoT Edge]]></category>
		<category><![CDATA[Azure IoT Hub]]></category>
		<category><![CDATA[IoT]]></category>
		<category><![CDATA[IoT Hub]]></category>
		<category><![CDATA[IoT Hub Device Provisioning Service]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=2950</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutos</span></span>¡Otra semana más! parece que fue ayer cuando estábamos de vacaciones todavía, pero nada más lejos de la realidad. Arrancan nuevos proyectos en mi trabajo y por suerte siempre estoy ... </p>
<p class="read-more-container"><a title="IoT Hub DPS: Creando dispositivos bajo demanda" class="read-more button" href="https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/#more-2950" aria-label="Más en IoT Hub DPS: Creando dispositivos bajo demanda">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/">IoT Hub DPS: Creando dispositivos bajo demanda</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="317" height="159" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/iothubdps.png" alt="Imagen ornamental para la entrada &quot;IoT Hub DPS: Creando dispositivos bajo demanda&quot;" class="wp-image-2952" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/11/iothubdps.png 317w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/iothubdps-300x150.png 300w" sizes="(max-width: 317px) 100vw, 317px" /></figure></div>



<p>¡Otra semana más! parece que fue ayer cuando estábamos de vacaciones todavía, pero nada más lejos de la realidad. Arrancan nuevos proyectos en mi trabajo y por suerte siempre estoy ahí moviéndome en lo que me gusta, Kubernetes y IoT. <br>Es por eso qué hoy toca seguir hablando de IoT en Azure <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>En la última entrada planteamos <a href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/">cómo crear módulos especializados para desplegar sobre un dispositivo IoT Edge</a> y hoy toca hablar de otra situación muy habitual al hablar de IoT: cómo aprovisionar los dispositivos.</p>



<h2 class="wp-block-heading" id="h-en-qu-consiste-aprovisionar-dispositivos">¿En qué consiste aprovisionar dispositivos?</h2>



<p>Hasta ahora, siempre que hemos trabajado con IoT Hub, hemos creado individualmente cada dispositivo, sea de IoT Hub o de IoT Edge, y hemos recuperado su cadena de conexión. </p>



<p>Existe otro modo en el que podemos registrar esos diferentes dispositivos que consiste en utilizar un servicio de Azure llamado <a href="https://docs.microsoft.com/es-es/azure/iot-dps/" target="_blank" rel="noreferrer noopener">Azure IoT Hub Device Provisioning Service (DPS)</a>. Este servicio nos va a permitir asociar diferentes IoT Hub, sobre los cuales se van a crear los dispositivos de manera automática.</p>



<p>Este gráfico (sacado de la <a href="https://docs.microsoft.com/es-es/azure/iot-dps/about-iot-dps">documentación oficial</a>) describe muy claramente cuál es el flujo que sigue IoT Hub DPS para crear un dispositivo nuevo:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="436" src="https://www.fixedbuffer.com/wp-content/uploads/2020/11/dps-provisioning-flow-1024x436.png" alt="1- El fabricante del dispositivo agrega la información de registro del dispositivo a la lista de inscripción en Azure Portal.
2- El dispositivo contacta con el punto de conexión de DPS configurado de fábrica. 
3- DPS valida la identidad del dispositivo.
4- DPS registra el dispositivo con un IoT Hub.
5- El centro de IoT devuelve la información del identificador de dispositivo a DPS.
6- DPS devuelve la información de conexión del centro de IoT al dispositivo. El dispositivo ya puede empezar a enviar datos directamente a la instancia de IoT Hub.
7- El dispositivo se conecta a IoT Hub.
8- Obtiene el estado deseado del dispositivo gemelo en la instancia de IoT Hub." class="wp-image-2959" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/11/dps-provisioning-flow-1024x436.png 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/dps-provisioning-flow-300x128.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/dps-provisioning-flow-768x327.png 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/11/dps-provisioning-flow.png 1068w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<ol><li>El fabricante del dispositivo agrega la información de registro del dispositivo a la lista de inscripción en Azure Portal.</li><li>El dispositivo contacta con el punto de conexión de DPS configurado de fábrica. </li><li>DPS valida la identidad del dispositivo.</li><li>DPS registra el dispositivo con un IoT Hub.</li><li>El centro de IoT devuelve la información del identificador de dispositivo a DPS.</li><li>DPS devuelve la información de conexión del centro de IoT al dispositivo. El dispositivo ya puede empezar a enviar datos directamente a la instancia de IoT Hub.</li><li>El dispositivo se conecta a IoT Hub.</li><li>Obtiene el estado deseado del dispositivo gemelo en la instancia de IoT Hub.</li></ol>



<h2 class="wp-block-heading" id="h-por-qu-aprovisionar-dispositivos-con-iot-hub-dps">¿Por qué aprovisionar dispositivos con IoT Hub DPS?</h2>



<p>Crear manualmente los dispositivos está bien siempre que el número de dispositivos que queremos registrar no sea excesivamente grande, o siempre que cada dispositivo que creamos físicamente tenga algún proceso de fabricación individual. </p>



<p>Por ejemplo, si cada dispositivo es arrancado por un técnico para hacer algún proceso propio de la fabricación, no es descabellado que durante ese proceso se cree el dispositivo y se registre.</p>



<p>En cambio, imagina un escenario en el que no existe ese proceso manual y se fabrican en serie cientos de dispositivos iguales, que pueden ir a parar a cualquier lugar del mundo y caer en cualquier mano. En caso de estar en un escenario como el que acabo de describir, seguramente nos interese tener diferentes IoT Hub distintos desplegados en diferentes regiones y que los dispositivos se creen y conecten en el Hub más cercano pare reducir la latencia (geolocalización). Eso en el caso más exigente, pero otro caso quizás más común es aquél donde el usuario final no tiene los conocimientos o interés en configurar el dispositivo.</p>



<p>En este tipo de escenarios masivos en los que no hay intervención humana especializada durante el proceso de fabricación, es vital tener un sistema de aprovisionamiento automático.</p>



<h2 class="wp-block-heading" id="h-creando-nuestro-iot-hub-dps">Creando nuestro IoT Hub DPS</h2>



<p>Doy por hecho que si estas leyendo esto, es porque conoces IoT Hub y como crearlo, por lo que voy a partir del punto en el que conoces como crear un IoT Hub y como funciona, así como en que consiste IoT Edge. En caso de que no sea así, te dejo unas entradas sobre el tema:</p>



<ul><li><a href="https://docs.microsoft.com/es-es/azure/iot-hub/iot-hub-create-through-portal" target="_blank" rel="noreferrer noopener nofollow">Creación de una instancia de IoT Hub mediante Azure Portal</a></li><li><a href="https://www.plainconcepts.com/es/iot-edge-cargas-trabajo-iot-hub/" target="_blank" rel="noreferrer noopener">IoT Edge: Desplegando cargas de trabajo &#8216;on-prem&#8217; desde IoT Hub</a></li><li><a href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/">Creando módulos especializados para Azure IoT Edge</a></li></ul>



<p>Aunque existen varias maneras de crear un IoT Hub DPS, a fin de simplificar al máximo vamos a utilizar <a href="https://docs.microsoft.com/es-es/cli/azure/install-azure-cli" rel="noreferrer noopener nofollow" target="_blank">Az Cli</a> con su extension &#8216;<a href="https://github.com/Azure/azure-iot-cli-extension#installation" target="_blank" rel="noreferrer noopener">iot</a>&#8216;.</p>



<p>Lo primero va a ser crear el servicio, para ello, basta con ejecutar:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-powershell"> az iot dps create --name $dpsName --resource-group $rg --location $region
</code></pre>
</div>



<p>Esto nos va a devolver entre otras cosas, dos datos que necesitamos guardar para utilizar más adelante (aunque también los podemos recuperar más adelante desde Az Cli o desde el portal). Estos datos son &#8216;<em>idScope</em>&#8216; y &#8216;<em>serviceOperationsHostName</em>&#8216; y los vamos a necesitar cuando estemos configurando los dispositivos para auto aprovisionarse con IoT Hub DPS.</p>



<p>Lo siguiente que vamos a hacer una vez que se ha creado el servicio, es asociarlo con las instancias de IoT Hub donde queremos que sea posible aprovisionar dispositivos. Para esto vamos a necesitar tener una cadena de conexión con permisos de administración sobre cada uno de los IoT Hub que queremos conectar. Podemos conseguirla desde el portal o simplemente ejecutando:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-powershell">$hubConnectionString=$(az iot hub show-connection-string --name $iotHubName --key primary --query connectionString -o tsv)
</code></pre>
</div>



<p>Y una vez que la tengamos, asociamos IoT Hub DPS con la instancia de IoT Hub ejecutando:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-powershell">az iot dps linked-hub create --dps-name $dpsName --resource-group $rg --connection-string $hubConnectionString --location $region
</code></pre>
</div>



<p>Adicionalmente, podemos añadir el parámetro &#8216;<em>&#8211;allocation-weight</em>&#8216;, que no es más que un valor de prioridad que podemos utilizar en el siguiente paso para decidir en que IoT Hub se van aprovisionar los dispositivos en caso de que exista más de uno asociado a la instancia de IoT Hub DPS.</p>



<h2 class="wp-block-heading" id="h-manejando-las-inscripciones">Manejando las inscripciones</h2>



<p>Vamos a pasar revista de lo que tenemos hasta el momento:</p>



<ul><li>Tenemos un IoT Hub o varios.</li><li>Tenemos un IoT Hub DPS junto a su <em>idScope</em> y <em>serviceOperationsHostName</em>.</li><li>Hemos asociado nuestro/s IoT Hub con el DPS.</li></ul>



<p>Ya casi estamos listos para poder empezar a utilizar nuestro IoT Hub DPS, solo nos falta registrar dentro del propio servicio el método por el que vamos a identificar a los dispositivos. Para esto tenemos 2 modelos:</p>



<ul><li>Registrar dispositivos individualmente.</li><li>Crear grupos de registro.</li></ul>



<p>Estos dos modelos nos van a permitir gestionar como queremos que se inscriban los diferentes dispositivos. Llegados a este punto, puede parecer un poco absurdo el hecho de registrar dispositivos individualmente, ¿no? Pues la verdad es que tiene todo el sentido del mundo ya que el hecho de utilizar IoT Hub DPS para un dispositivo individual nos garantiza que en caso de que borremos el dispositivo de IoT Hub por cualquier razón, se volverá a crear automáticamente sin necesidad de interacción humana. Por ejemplo, puede darse el caso de que una instancia de IoT Hub este sobrecargada y no la podamos escalar por &#8216;x&#8217; razón, gracias a este aprovisionamiento individual, podríamos cambiar dispositivos concretos de un Hub a otro con un mínimo esfuerzo.<br>Por su parte en cambio, la inscripción de grupos nos va a permitir registrar múltiples dispositivos con un mismo sistema.</p>



<p>Independientemente del sistema de inscripción que elijamos, vamos a tener 2 mecanismos para que un dispositivo se identifique ante IoT Hub DPS y un tercero en caso de dispositivos individuales:</p>



<ul><li><a href="https://es.wikipedia.org/wiki/X.509" target="_blank" rel="noreferrer noopener nofollow">X.509</a></li><li>Clave Simétrica</li><li><a href="https://es.wikipedia.org/wiki/M%C3%B3dulo_de_plataforma_de_confianza" rel="noreferrer noopener nofollow" target="_blank">TPM</a> (dispositivos individuales)</li></ul>



<p>Teniendo en cuenta estos dos tipos de registros y sus diferentes modos de identificación, vamos a crear una inscripción de grupo a modo de ejemplo (aunque una individual sería prácticamente igual). Para este proceso, vamos a necesitar tomar algunas decisiones: ¿Qué criterio vamos a usar para elegir el IoT Hub? Las opciones disponibles son:</p>



<ul><li>Latencia mínima.</li><li>Peso de los IoT Hub.</li><li>Siempre al mismo.</li><li>Utilizar una Azure Function.</li></ul>



<p>¿Qué instancias de IoT Hub queremos que sean elegibles para registrar el dispositivo? ¿Qué propiedades y/o tags queremos que tengan automáticamente los dispositivos? Estas últimas por ejemplo son muy útiles cuando queremos que un <a href="https://docs.microsoft.com/es-es/azure/iot-edge/how-to-deploy-at-scale" target="_blank" rel="noreferrer noopener">deployment de módulos se aplique directamente</a> a los dispositivos IoT Edge.</p>



<div class="wp-block-jetpack-markdown"><p>En este caso para probar, vamos a crear dos grupos de inscripción, uno que sirva para dispositivos IoT Edge y añada el tag <code>{&quot;IoTEdge&quot; : &quot;FixedBuffer&quot;}</code> y otro que sirva para IoT Hub y tenga el tag <code>{&quot;IoTHub&quot; : &quot;FixedBuffer&quot;}</code>.</p>
</div>



<p>Para eso, vamos a ejecutar:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-powershell"># Grupo para IoT Edge
az iot dps enrollment-group create -g $rg --dps-name $dpsName --enrollment-id &quot;IoTEdge&quot; --primary-key $primaryKey --secondary-key $secondayKey --initial-twin-tags &quot;{'IoTEdge' : 'FixedBuffer'}&quot; --edge-enabled=true
# Grupo para IoT Hub
az iot dps enrollment-group create -g $rg --dps-name $dpsName --enrollment-id &quot;IoTHub&quot; --primary-key $primaryKey --secondary-key $secondayKey --initial-twin-tags &quot;{'IoTHub' : 'FixedBuffer'}&quot; --edge-enabled=false
# Verificamos que se han creado
az iot dps enrollment-group list --dps-name $dpsName --resource-group $rg
</code></pre>
</div>



<h2 class="wp-block-heading" id="h-probando-iot-hub-dps">Probando IoT Hub DPS</h2>



<p>Con esto que hemos hecho ya estamos preparados para poder empezar a aprovisionar nuestros dispositivos. Bueno, en realidad no del todo&#8230; He elegido aprovisionar grupos ya que tiene un paso más respecto a aprovisionar dispositivos individuales. </p>



<p>No es una buena idea el distribuir de manera masiva una clave que permite registrar tantos dispositivos quieras de manera automática. Es por esto que IoT Hub DPS trabaja con una clave derivada cuando se trata de inscripción de grupos. Esta clave derivada se puede calcular utilizando HMACSHA256, para lo cual la <a href="https://docs.microsoft.com/es-es/azure/iot-dps/how-to-legacy-device-symm-key#derive-a-device-key" target="_blank" rel="noreferrer noopener">propia documentación nos ofrece un código de ejemplo</a>.</p>



<p>Probar el aprovisionamiento de un dispositivo IoT Hub es muy sencillo ya que basta con que nos bajemos el <a href="https://github.com/Azure-Samples/azure-iot-samples-csharp" target="_blank" rel="noreferrer noopener nofollow">repositorio de ejemplos de IoT</a>, y ejecutemos el ejemplo &#8216;<em>ProvisioningDeviceSamples</em>&#8216; seleccionando el proyecto de inicio en &#8216;<em>SymmetricKeySample</em>&#8216;. El propio fichero Program.cs nos guía mediante comentarios sobre que tenemos que rellenar y donde para poder validar el funcionamiento. </p>



<div class="wp-block-jetpack-markdown"><p>En mi caso, una vez ejecutado se ha registrado un dispositivo llamado &#8216;sample-iot-hub&#8217;, al que al consultar sus tags con <code>az iot hub device-identity list --ee false -n $iotHubName --query=&quot;[*].tags&quot;</code> me devuelve esto:</p>
</div>



<div class="wp-block-jetpack-markdown"><pre><code class="language-json">{
  &quot;IoTHub&quot;: &quot;FixedBuffer&quot;
}
</code></pre>
</div>



<p>De igual modo, vamos a probar que se registra correctamente si el dispositivo es de tipo Edge. Esto requiere que configuremos su fichero config.yaml y le indiquemos los datos que necesita para poder conectarse (que son los mismos que en el caso anterior). Para poder probar que todo esto funciona de manera más sencilla, he creado una imagen Docker que a la que indicándole las variables de entorno &#8216;<em>SCOPE_ID</em>&#8216; y &#8216;<em>SYMMETRIC_KEY</em>&#8216;, va a registrar un dispositivo IoT Edge con el nombre del contenedor utilizando IoT Hub DPS. No es una imagen 100% funcional, pero para la prueba que vamos a hacer nosotros es suficiente. Puedes ver el <a href="https://github.com/FixedBuffer/IoTEdgeDPS" target="_blank" rel="noreferrer noopener nofollow">código de la imagen en GitHub</a> si tienes interés <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-powershell">docker run --privileged=true -e SCOPE_ID=$SCOPE_ID -e SYMMETRIC_KEY=$primaryKey jorturfer/iot-edge-dps-post
</code></pre>
</div>



<div class="wp-block-jetpack-markdown"><p>Una vez se ejecute y se conecte, puedo comprobar que se ha aprovisionado correctamente ejecutando <code>az iot hub device-identity list --ee true -n $iotHubName --query=&quot;[*].tags&quot;</code>, que esta vez me devolverá:</p>
<pre><code class="language-json">{
  &quot;IoTEdge&quot;: &quot;FixedBuffer&quot;
}
</code></pre>
</div>



<h2 class="wp-block-heading" id="h-conclusi-n">Conclusión</h2>



<p>Una vez más, me dejo muchas cosas chulas en el tintero, pero la verdad es que si las revisásemos todas en detalle, ni yo mismo leería mi entrada por el tamaño final&#8230;</p>



<p>Hemos hecho una pequeña aproximación al aprovisionamiento de dispositivos utilizando IoT Hub DPS ya que es una herramienta que personalmente me parece muy potente y útil. Respecto a su coste hay que decir también que, aunque sí tiene coste, es ridículo a cambio del trabajo que ahorra, nada más y nada menos que €0,085 por cada 1.000 operaciones.</p>



<p>En futuras entradas, me gustaría unificar todos y cada uno de estos conceptos que hemos ido planteando tanto aquí como en las entradas que escribí para <a href="https://www.plainconcepts.com/es/" target="_blank" rel="noreferrer noopener">Plain Concepts</a> en una entrada con una ejemplo funcional E2E. ¿Qué opinas? ¿Crees que sería interesante? ¿Conocías previamente IoT Hub Device Provisioning Service?</p>
<p>**La entrada <a href="https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/">IoT Hub DPS: Creando dispositivos bajo demanda</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/iot-hub-dps-creando-dispositivos-bajo-demanda/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2950</post-id>	</item>
		<item>
		<title>Creando módulos especializados para Azure IoT Edge</title>
		<link>https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=creando-modulos-especializados-para-azure-iot-edge</link>
					<comments>https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 20 Oct 2020 08:00:00 +0000</pubDate>
				<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Azure Infrastructure]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Azure IoT Edge]]></category>
		<category><![CDATA[Azure IoT Hub]]></category>
		<category><![CDATA[IoT]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=2901</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 12</span> <span class="rt-label rt-postfix">minutos</span></span>Después de unas semanas muy liado con unas complicaciones personales (solo he tenido tiempo de publicar la review de NDepend), ya estamos de vuelta y además con una entrada que ... </p>
<p class="read-more-container"><a title="Creando módulos especializados para Azure IoT Edge" class="read-more button" href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/#more-2901" aria-label="Más en Creando módulos especializados para Azure IoT Edge">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/">Creando módulos especializados para Azure IoT Edge</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 12</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="576" src="https://www.fixedbuffer.com/wp-content/uploads/2020/10/BadgeIoTEdge-1024x576.jpg" alt="Imagen ornamental para la entrada &quot;Creando módulos especializados para Azure IoT Edge&quot;" class="wp-image-2946" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/10/BadgeIoTEdge-1024x576.jpg 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/10/BadgeIoTEdge-300x169.jpg 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/10/BadgeIoTEdge-768x432.jpg 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/10/BadgeIoTEdge.jpg 1200w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>Después de unas semanas muy liado con unas complicaciones personales (solo he tenido tiempo de publicar la <a href="https://www.fixedbuffer.com/mejorando-la-calidad-del-codigo-con-ndepend/">review de NDepend</a>), ya estamos de vuelta y además con una entrada que toca muchos palos. Vamos a hablar de Azure, vamos a hablar de C# y vamos a hablar de Docker.</p>



<p>Por el título de la entrada, no es ningún secreto el tema del que vamos a hablar de Azure IoT Edge y sus módulos. La idea de escribir esto es porque no hace mucho escribí en el blog de Plain Concepts una entrada sobre <a href="https://www.plainconcepts.com/es/iot-edge-cargas-trabajo-iot-hub/" target="_blank" rel="noreferrer noopener">cómo desplegar cargas de trabajo &#8216;on-prem&#8217; utilizando IoT Hub</a>. </p>



<p>Aunque lo que se plantea en la entrada es totalmente cierto y el runtime de IoT Edge permite correr imágenes Docker sin más, la verdad es que nos permite ir un paso más allá. IoT Edge nos va a permitir crear módulos especializados con los que vamos a poder utilizar las funcionalidades propias de IoT Hub y todo ello empaquetado en una imagen Docker. Con esto vamos a conseguir que nuestro IoT Edge deje de ser un &#8216;nodo&#8217; de Docker al que podemos mandar cosas y se convierta en un verdadero punto de <a href="https://www.redhat.com/es/topics/edge-computing/what-is-edge-computing" target="_blank" rel="noreferrer noopener nofollow">computación de borde</a>.</p>



<h2 class="wp-block-heading" id="h-por-qu-deber-a-usar-m-dulos-de-iot-hub-en-lugar-de-im-genes-docker-normales">¿Por qué debería usar módulos de IoT Hub en lugar de imágenes Docker normales?</h2>



<p>Es cierto que con una imagen Docker las cosas soy mucho más simples comunes a la hora de desarrollar. Tienes un fichero Dockerfile, expones unos puertos, creas tus APIs, y si necesitas comunicarte con IoT Hub para enviar mensajes, puedes utilizar directamente el cliente de IoT Hub para el lenguaje con el que estés trabajando. ¿Por qué debería entonces complicarme la vida?</p>



<p>Pues, aunque los módulos implican añadir ciertos cambios a la hora de programar (más bien de desplegar), no dejan de ser una aplicación de consola sin más, al que se le ha dotado de funcionalidad para interactuar con el runtime sobre el que está corriendo. Esto se traduce en 2 grandes ventajas:</p>



<ul><li>Delegamos en el runtime el enrutado de los mensajes entre los diferentes módulos.</li><li>Tenemos a nuestra disposición toda la potencia de IoT Hub en cada módulo (envío de métricas/mensajes, dispositivo gemelo, mensajes desde la nube al dispositivo, etc&#8230;), utilizando la conexión del runtime de IoT Edge.</li></ul>



<h2 class="wp-block-heading" id="h-las-ventajas-son-realmente-ventajas">¿Las ventajas son realmente ventajas?</h2>



<p>Vale, he planteado las 2 cosas que a mi parecer son las principales mejoras, ¿pero esto realmente son mejoras? Analicemos la primera de ellas, el enrutado de mensajes.</p>



<p>El planteamiento de IoT Edge es tener pequeños programas que vayan añadiendo procesado sobre los datos de entrada. Imaginemos un caso donde tenemos una cámara capturando imágenes de una autopista, las imágenes que se obtienen se procesan y por último se etiquetan. Es cierto que esto se podría hacer de manera monolítica todo en el mismo proceso, pero eso dificulta el reutilizar código. Si separamos la adquisición en una imagen, el procesado en otra y el etiquetado en otra, vamos a poder cambiar una de las piezas por otra y reutilizar al máximo. </p>



<p>Por poner un caso, podríamos tener otro escenario en el que solo queramos capturar imágenes y mandarlas a un almacenamiento, el hecho de que la adquisición este <strong>modularizada</strong>, nos va a permitir reaprovechar ese módulo y añadir solo la parte nueva de persistencia.</p>



<p>Hecha esta aclaración sobre la modularización y reutilización, podríamos extraer varios módulos del conjunto de los dos escenarios:</p>



<ul><li>Adquisición de imágenes.</li><li>Procesado de las imágenes.</li><li>Etiquetado de las imágenes.</li><li>Persistencia de las imágenes.</li></ul>



<p>Y su representación sería algo así:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="547" height="192" src="https://www.fixedbuffer.com/wp-content/uploads/2020/10/image.png" alt="La imagen muestra un diagrama de bloques en que que hay 2 zonas. La primera zona dice &quot;escenario 1&quot; y tienes 3 bloques unidos por flechas. Desde bloque Captura sale una flecha hacia Procesado, y desde ahí sale una flecha hacia Etiquetado.
La segunda zona es &quot;escenario 2&quot; y tiene una flecha que sale desde el bloque Captura y llega a Persistencia" class="wp-image-2912" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/10/image.png 547w, https://www.fixedbuffer.com/wp-content/uploads/2020/10/image-300x105.png 300w" sizes="(max-width: 547px) 100vw, 547px" /></figure></div>



<p>En una situación multi contenedor corriendo sobre Docker, simplemente podríamos decirle a un contenedor, por ejemplo, Captura, que una vez que tenga una imagen la mande al siguiente contenedor siguiendo un modelo push (es el propio contenedor el que se encarga de empujar los datos al siguiente). Incluso podríamos cambiar el planteamiento y utilizar un modelo pull donde cada paso obtenga los datos del paso previo.</p>



<p>Esto tiene problemas a la hora de reutilizar los módulos, ya que cada módulo necesita tener consciencia o desde donde obtiene los datos o a donde los tiene que enviar. Tenemos acoplamiento entre los módulos. Si por ejemplo quisiéramos añadir al escenario 1 la persistencia entre la captura y el procesado (o en cualquier otra posición), tendríamos que modificar el código del módulo para que se adapte al nuevo escenario, no nos vale solo con modificar las configuraciones.</p>



<blockquote class="wp-block-quote"><p>En este punto, voy a aclarar que en programación casi todo es posible, e incluso esto se puede solucionar añadiendo código o creando nosotros mismos sistemas complejos que lo solucionen. En algunos casos puede que sea necesario, pero generalmente es tiempo y esfuerzo en balde.</p></blockquote>



<p>Para evitar esto, podríamos añadir algún sistema de colas donde cada módulo publique los resultados a una cola y lea las entradas desde otra cola. De este modo cada módulo no necesita saber nada al respecto del anterior ni del siguiente, simplemente lee los datos desde una cola y los deja en otra cola. El problema de este planteamiento es que somos nosotros los que vamos a tener que mantener la infraestructura de colas&#8230; y aquí es donde coge importancia el delegar en el runtime el enrutado de mensajes.</p>



<p>IoT Edge nos proporciona de serie un sistema de enrutado que podemos utilizar desde los módulos especializados de IoT Edge. Nosotros simplemente vamos a definir una o más entradas para nuestro módulo de IoT Edge, y de igual manera vamos a escribir una serie de salidas del módulo. Después, desde la propia configuración de IoT Edge vamos a definir mediante lenguaje de consulta el coger las salidas un el módulo A y enviarlas a la cola de entrada del módulo B. Es importante aquí el punto de lenguaje de consulta, ya que vamos a poder aplicar condiciones para que la ruta se cumpla.</p>



<p>Esto desacopla totalmente los módulos entre si, ya que estamos usando un sistema de colas, pero en este caso es el propio <a href="https://docs.microsoft.com/es-es/azure/iot-edge/module-composition" target="_blank" rel="noreferrer noopener nofollow">runtime quien lo gestiona</a>.</p>



<p>Por otro lado, comentaba que la segunda gran ventaja era que el propio módulo de IoT Edge era capaz de ser un dispositivo IoT en si mismo, aprovechando la conexión del runtime. Esto significa que por ejemplo que podemos utilizar el sistema de rutas para enviar métricas a IoT Hub, es decir, es posible especificarle a IoT Edge que coja la cola de salida del módulo A y que directamente la envíe como una métrica a IoT Hub.</p>



<h2 class="wp-block-heading" id="h-creando-la-soluci-n-iot-edge">Creando la solución IoT Edge</h2>



<p>Como hablar siempre es muy fácil, vamos a crear un módulo IoT Edge desde 0, que enrute los mensajes generados desde el simulador de pruebas que ofrece Microsoft. <a href="https://docs.microsoft.com/es-es/azure/iot-edge/tutorial-develop-for-linux" target="_blank" rel="noreferrer noopener nofollow">Lo primero de todo es preparar el entorno</a>, el resumen ejecutivo de los elementos que necesitamos es .Net Core y una extensión para <a href="https://marketplace.visualstudio.com/items?itemName=vsc-iot.vs16iotedgetools" target="_blank" rel="noreferrer noopener nofollow">Visual Studio</a> o <a href="https://marketplace.visualstudio.com/items?itemName=vsciot-vscode.azure-iot-tools" target="_blank" rel="noreferrer noopener nofollow">Visual Studio Code</a> que nos de soporte para desarrollar módulos IoT Edge. Una vez instalada la extensión, seguiremos la documentación específica para configurar la extensión con nuestro IoT Hub.</p>



<p>En este caso por versatilidad, yo estoy utilizando Visual Studio Code, por lo que, una vez instalada y configurada la extensión, basta con escribir «<strong>Azure&nbsp;IoT&nbsp;Edge: New IoT Edge solution</strong>» en la paleta de comandos.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="715" height="135" src="https://www.fixedbuffer.com/wp-content/uploads/2020/10/image-1.png" alt="La imagen muestra la paleta de Visual Studio Code con el comando &quot;Azure IoT Edge: New IoT Edge solution&quot; escrito" class="wp-image-2919" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/10/image-1.png 715w, https://www.fixedbuffer.com/wp-content/uploads/2020/10/image-1-300x57.png 300w" sizes="(max-width: 715px) 100vw, 715px" /></figure></div>



<p>Esto iniciará un pequeño asistente en el que nos va a pedir una ruta, un nombre para la solución, y nos va a ofrecer una plantilla de módulo IoT Edge en varios lenguajes diferentes. Una vez seleccionado el nombre del módulo, nos va a pedir el último paso que es decirle cual es el registro de Docker en el que se tiene que subir la imagen con el módulo. Este registro puede ser público o privado. </p>



<p>Una vez hecho esto, ya tenemos listo el andamiaje del proyecto, y ya podemos editar y generar el módulo, desplegarlo, depurarlo,&#8230; Este andamiaje está compuesto de varios ficheros que vamos a analizar.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="724" height="630" src="https://www.fixedbuffer.com/wp-content/uploads/2020/10/image-2.png" alt="La imagen muestra la plantilla de IoT Edge end Visual Studio Code" class="wp-image-2920" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/10/image-2.png 724w, https://www.fixedbuffer.com/wp-content/uploads/2020/10/image-2-300x261.png 300w" sizes="(max-width: 724px) 100vw, 724px" /></figure></div>



<p>El <strong>fichero</strong> <strong>.env</strong> contendrá variables que se reemplazan durante el proceso de generación. Principalmente valores sobre el registro de Docker.</p>



<p>Dentro de la <strong>carpeta</strong> .<strong>vscode</strong>, se ha creado el código necesario para ejecutar en local el código (que no deja de ser una aplicación de consola), o para depurarlo dentro de un contenedor Docker.</p>



<p>La <strong>carpeta config</strong> va a contener los ficheros de configuración que generamos como artefacto de salida del proceso. Estos ficheros son muy útiles ya que son el json que podemos utilizar para desplegar sobre un dispositivo IoT Edge el conjunto de módulos que definamos. Esto es así porque realmente hemos creado una solución completa de IoT Edge que contiene módulos, algunos pueden ser de terceros y algunos nuestros (como es el caso).</p>



<p>En la <strong>carpeta modules</strong> es donde encontramos la magia. Aquí es donde vamos a encontrar los diferentes módulos que estemos creando. Ahora entraremos en detalle.</p>



<p>Por último, nos encontramos los ficheros <strong>deployment.template.json</strong> y <strong>deployment.debug.template.json</strong>. Estos ficheros son los que van a servir de entrada para que durante el proceso de generación del módulo se cree el artefacto de salida en la carpeta config. La principal diferencia entre ellos es que el que contiene debug nos va a permitir depurar el código del contenedor.</p>



<h2 class="wp-block-heading" id="h-el-m-dulo-iot-edge">El módulo IoT Edge</h2>



<p>Para acabar de revisar que tenemos, cuando entramos en la carpeta modules nos encontramos con un buen número de ficheros Dockerfile, un fichero module.json y un program.cs.</p>



<p>Si comprobamos que contiene el fichero program.cs, nos encontramos con que no es más que una aplicación de consola. Hay que reconocer que la plantilla no es todo lo óptimo que debería ser en cuanto a buenas prácticas, pero podríamos reducirlo a algo así:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-csharp">using System;
using System.Runtime.Loader;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Client.Transport.Mqtt;

namespace SampleModule
{
    class Program
    {
        static int counter;

        static async Task Main(string[] args)
        {
            await InitAsync();

            var cts = new CancellationTokenSource();
            AssemblyLoadContext.Default.Unloading += (ctx) =&gt; cts.Cancel();
            Console.CancelKeyPress += (sender, cpe) =&gt; cts.Cancel();
            await WhenCancelledAsync(cts.Token);
        }

        public static Task WhenCancelledAsync(CancellationToken cancellationToken)
        {
            var tcs = new TaskCompletionSource&lt;bool&gt;();
            cancellationToken.Register(s =&gt; ((TaskCompletionSource&lt;bool&gt;)s).SetResult(true), tcs);
            return tcs.Task;
        }

        static async Task InitAsync()
        {
            MqttTransportSettings mqttSetting = new MqttTransportSettings(TransportType.Mqtt_Tcp_Only);
            ITransportSettings[] settings = { mqttSetting };

            ModuleClient ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(settings);
            await ioTHubModuleClient.OpenAsync();

            await ioTHubModuleClient.SetInputMessageHandlerAsync(&quot;input1&quot;, PipeMessage, ioTHubModuleClient);
        }

        static async Task&lt;MessageResponse&gt; PipeMessage(Message message, object userContext)
        {
            int counterValue = Interlocked.Increment(ref counter);

            var moduleClient = userContext as ModuleClient;
            if (moduleClient == null)
            {
                throw new InvalidOperationException(&quot;UserContext doesn't contain &quot; + &quot;expected values&quot;);
            }           
            
            //Encolamos el mensaje como salida
            await moduleClient.SendEventAsync(&quot;output1&quot;, message);
            return MessageResponse.Completed;
        }
    }
}
</code></pre>
</div>



<p>Si analizamos el código (he cambiado ligeramente para usar await en vez de .Wait() respecto a la plantilla y borrado código que no aporta), podemos comprobar que tenemos un Main que inicializa el dispositivo llamando a InitAsync (Init en la plantilla original). Dentro de este método, se inicializa el módulo IoT Edge a través del tipo ModuleClient.</p>



<p>Sobre este módulo registramos un manejador para cada vez que llegue un mensaje por la cola «input1» con la línea:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-csharp">await ioTHubModuleClient.SetInputMessageHandlerAsync(&quot;input1&quot;, PipeMessage, ioTHubModuleClient);
</code></pre>
</div>



<p>Añadir nuevas entradas es tan simple como registrar más manejadores para otras colas de entrada.</p>



<p>Para escribir los mensajes a una cola de salida, se utiliza la línea:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-csharp">await moduleClient.SendEventAsync(&quot;output1&quot;, message);
</code></pre>
</div>



<p>En este caso, simplemente estamos utilizando el mensaje de la cola de entrada «input1» del módulo IoT Edge y lo estamos colocando en la cola de salida «output1».</p>



<p>Adicionalmente a lo que nos ofrece la plantilla, el módulo nos ofrece también varios métodos con los que vamos a poder ampliar la utilidad del módulo mediante configuración de dispositivo gemelo, recepción de mensajes CloudToDevice, o llamadas a métodos directos. Esto se consigue simplemente añadiendo a la inicialización las líneas:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-csharp">await ioTHubModuleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertiesUpdateAsync, ioTHubModuleClient);
await ioTHubModuleClient.SetMessageHandlerAsync(HandleMessageAsync,ioTHubModuleClient);
await ioTHubModuleClient.SetMethodHandlerAsync(&quot;hello&quot;,HelloMethodHandlerAsync,ioTHubModuleClient);
</code></pre>
</div>



<blockquote class="wp-block-quote"><p>A este código hay que añadirle los callback de los manejadores, para no alargar una entrada ya de por si larga, puedes consultar el código <a href="https://github.com/FixedBuffer/Post_IoT_Edge_Module" target="_blank" rel="noreferrer noopener">completo en el repositorio de Github</a>.</p></blockquote>



<p>Hay que tener en cuenta que tanto las colas de entrada y salida, como el resto de las funcionalidades como métodos directos o dispositivo gemelo están ligados al módulo y estarán disponibles en cualquier runtime de IoT Edge donde despleguemos el módulo y dependerá de si queremos usarlos o no.</p>



<p>Una vez visto el código, ¿por qué tantos Dockerfile?. La idea de que haya tantos es que estamos creando un módulo que queremos que se pueda desplegar en cualquier sitio que pueda correr IoT Edge, y esto son muchas arquitecturas de microprocesador diferente. Gracias a tener todos esos Dockerfile ya en la plantilla, nos abre la puerta a poder hacer que nuestro módulo de IoT Edge corra en Linux, Windows, en microprocesadores AMD, ARM,&#8230; </p>



<p>Vamos a poder configurar la arquitectura de destino simplemente ejecutando en la paleta de comandos «<strong>Azure IoT Edge: Set Default Target Platform for Edge Solution</strong>«. Esto nos va a dar varias opciones disponibles, que son las que están definidas en el fichero <strong>module.json</strong>, donde vamos a relacionar las arquitecturas con los Dockerfile concretos entre otras cosas como especificar la versión del módulo.</p>



<blockquote class="wp-block-quote"><p>Actualmente, Docker soporta de forma <a rel="noreferrer noopener nofollow" href="https://docs.docker.com/engine/reference/commandline/manifest/" target="_blank">experimental el crear manifiestos de imágenes para múltiples arquitecturas</a> simultáneamente, siendo el runtime de Docker el que se descarga la imagen de su arquitectura especifica. Si tienes interés en el tema, hablo de ello en la entrada <a href="https://www.fixedbuffer.com/como-crear-imagenes-docker-multiarquitectura/">Cómo crear una imagen docker multiarquitectura desde cero</a>.</p></blockquote>



<p>Una vez que hemos terminado de configurar y programar nuestro módulo, vamos a poder generar la imagen o imágenes y subirlas al repositorio haciendo click derecho sobre el json del módulo o de la solución y pulsando sobre «<strong>Build&nbsp;and&nbsp;Push&nbsp;IoT&nbsp;Edge&nbsp;Module&nbsp;Image</strong>» o «<strong>Build&nbsp;and&nbsp;Push&nbsp;IoT&nbsp;Edge&nbsp;Solution</strong>» respectivamente.</p>



<p>En caso de que hayamos elegido la segunda opción, además de generar las imágenes y subirlas, va a generar el fichero de deployment en la carpeta config de la solución.</p>



<h2 class="wp-block-heading" id="h-configurando-las-rutas-y-desplegando-la-soluci-n">Configurando las rutas y desplegando la solución</h2>



<p>Ya casi estamos listos para desplegar la solución con nuestro módulo en un dispositivo IoT Edge. Tenemos el módulo listo, pero para poder probar el sistema de módulos de manera sencilla, vamos a necesitar desplegar varios módulos juntos.</p>



<p>Por suerte la plantilla de la solución de IoT que hemos usado, ya nos ha creado los ficheros deployment con un módulo extra de simulación, que va a sacar por su cola de salida «<strong>temperatureOutput</strong>«. En la sección de rutas (routes) del json de despliegue <strong>deployment.template.json</strong> vamos a definir los diferentes enrutados internos y externos de los mensajes como parte de $edgeHub. En este caso la plantilla ya nos ofrece 2 rutas que son desde la salida del simulador a la entrada del módulo, y desde la salida del módulo a $upstream. Es importante aclarar $upstream significa que el mensaje será enviado directamente a IoT Hub como una métrica del dispositivo:</p>



<div class="wp-block-jetpack-markdown"><pre><code class="language-csharp">&quot;routes&quot;: {
  &quot;SampleModuleToIoTHub&quot;: &quot;FROM /messages/modules/SampleModule/outputs/* INTO $upstream&quot;,
  &quot;sensorToSampleModule&quot;: &quot;FROM /messages/modules/SimulatedTemperatureSensor/outputs/temperatureOutput INTO BrokeredEndpoint(\&quot;/modules/SampleModule/inputs/input1\&quot;)&quot;
}
</code></pre>
</div>



<p>Cada vez que actualicemos la plantilla de despliegue, será necesario generar de nuevo el manifiesto de despliegue para la arquitectura concreta. Para esto vale con hacer click derecho sobre el json de la plantilla y seleccionar «<strong>Generate&nbsp;IoT&nbsp;Edge&nbsp;Deployment&nbsp;Manifest</strong>«.</p>



<p>Por último, ya solo queda desplegar la solución. Para esto es suficiente con hacer click derecho sobre el json del manifiesto en la carpeta de config y seleccionar «<strong>Create Deployment for Single Device</strong>«. Con esto vamos a poder elegir el dispositivo sobre el que queremos desplegar y «<em>et voilà</em>«. Ya hemos desplegado nuestro propio módulo IoT Edge y hemos configurado sus mensajes.</p>



<h2 class="wp-block-heading" id="h-conclusi-n">Conclusión</h2>



<p>Soy consciente de que se han quedado muchas cosas en el tintero, no he entrado a como depurar el código, usar el simulador, configuraciones complejas,&#8230; La idea detrás de todo esto era dar una visión de alto nivel sobre cómo es posible crear y desplegar módulos IoT Edge sin sufrir mucho por el camino. </p>



<p>Siempre que he usado IoT Edge he intentado usar directamente imágenes stand-alone porque pensaba que el sistema de módulos era complejo y que no valía la pena entrar en él, pero evidentemente me equivocaba. </p>



<p>Una cosa importante que no he dicho pero que también es importante, es que IoT Edge no tiene coste adicional, sobre el coste de mensajes que tengas en IoT Hub y además funciona con el tier gratuito, por lo que es posible utilizar IoT Edge gratis siempre que no mandemos más de 8 mil mensajes al día, momento en el que se cortará la comunicación con IoT Hub hasta el día siguiente.</p>



<p>De momento, te dejo el <a href="https://docs.microsoft.com/es-es/azure/iot-edge/" target="_blank" rel="noreferrer noopener">enlace a la documentación oficial de IoT Edge</a>, donde vas a poder encontrar no solo como desarrollar módulos, sino como hacer ajustes finos en muchas partes del sistema.</p>



<p>¿Y tú qué opinas? ¿Conocías los módulos para IoT Edge? Déjame en los comentarios si quieres que haga otra entrada más en detalle sobre cómo desarrollar módulos, depurarlos y hacer configuraciones más complejas.</p>
<p>**La entrada <a href="https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/">Creando módulos especializados para Azure IoT Edge</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/creando-modulos-especializados-para-azure-iot-edge/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2901</post-id>	</item>
		<item>
		<title>Mejorando la calidad del código con NDepend (con sorteo de licencia!)</title>
		<link>https://www.fixedbuffer.com/mejorando-la-calidad-del-codigo-con-ndepend/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mejorando-la-calidad-del-codigo-con-ndepend</link>
					<comments>https://www.fixedbuffer.com/mejorando-la-calidad-del-codigo-con-ndepend/#comments</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 29 Sep 2020 08:00:00 +0000</pubDate>
				<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[.Net Framework]]></category>
		<category><![CDATA[.Net Standard]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Calidad de código]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=2842</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutos</span></span>Hace ya unos meses, me escribió Patrick Smacchia, autor y lead del equipo de desarrollo de NDepend para ofrecerme probar su herramienta y hacer una review sobre ella. Me ha ... </p>
<p class="read-more-container"><a title="Mejorando la calidad del código con NDepend (con sorteo de licencia!)" class="read-more button" href="https://www.fixedbuffer.com/mejorando-la-calidad-del-codigo-con-ndepend/#more-2842" aria-label="Más en Mejorando la calidad del código con NDepend (con sorteo de licencia!)">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/mejorando-la-calidad-del-codigo-con-ndepend/">Mejorando la calidad del código con NDepend (con sorteo de licencia!)</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image is-style-default"><figure class="aligncenter size-large"><img decoding="async" width="310" height="163" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/logo.png" alt="Imagen ornamental con el logo de NDpend" class="wp-image-2844" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/09/logo.png 310w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/logo-300x158.png 300w" sizes="(max-width: 310px) 100vw, 310px" /></figure></div>



<p>Hace ya unos meses, me escribió Patrick Smacchia, autor y lead del equipo de desarrollo de NDepend para ofrecerme probar su herramienta y hacer una review sobre ella.</p>



<p>Me ha costado sacar tiempo para trastear la herramienta (lo siento Patrick <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f641.png" alt="🙁" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ), pero finalmente lo he podido hacer y ahí vamos con ello. </p>



<blockquote class="wp-block-quote"><p>Disclaimer: Nada de lo que digo aquí está condicionado, Patrick me ofreció una licencia para probar el software y hacer una review objetiva sobre lo que ofrece si me parecía interesante, pero en ningún caso ha revisado ni opinado sobre lo que aquí estoy escribiendo</p></blockquote>



<h2 class="wp-block-heading" id="h-qu-es-ndepend">¿Qué es NDepend?</h2>



<p>NDepend es un analizador de código con el que vamos a poder generar informes interactivos con métricas sobre la calidad del código analizado. Cada métrica está documentada y es monitorizable.</p>



<p>Se trata por tanto de una herramienta, enfocada a desarrolladores y arquitectos software, que realiza análisis estático de proyectos .NET y .NET Core.</p>



<p>Además, puede utilizarse tanto en nuestro propio equipo durante el proceso de desarrollo (ejecutable standalone e integraciones con Visual Studio), como en varios sistemas de integración continua como pueden ser Azure DevOps, TFS, Teamcity, Jenkins y algunas más.</p>



<h2 class="wp-block-heading" id="h-para-qu-me-puede-servir-ndepend">¿Para qué me puede servir NDepend?</h2>



<p>Detectar errores y demás está muy bien, pero eso ya lo consigo con FxCop Analyzers, ¿no?. La verdad es que NDepend llega bastante más alla de los que puede ser análisis de código estático simplemente. Una de las ventajas que nos aporta es la detección automática de puntos donde algo raro está pasando (code smells), así como la detección de buenas prácticas (o ausencia de ellas). Obviamente, también tenemos métricas sobre cantidad de lineas, complejidad ciclomática, etc&#8230;</p>



<p>En resumen (de brocha gorda):</p>



<ul><li>Detección de código sospechoso (code smells)</li><li>Detección de zonas donde el código es complejo (complejidad ciclomática)</li><li>Detección de referencias (que partes de nuestro código son las más utilizadas)</li><li>Detección de acoplamiento</li><li>Cálculo de deuda técnica (sí, aunque no lo reconozcas, no todo tu código es perfecto)</li><li>Tendencias y gráficos de evolución sobre la calidad del código</li><li>Estimación del tiempo invertido</li></ul>



<h2 class="wp-block-heading" id="h-un-caso-pr-ctico">Un caso práctico</h2>



<p>Como siempre, hablar es fácil y por eso quiero poner un caso práctico del análisis de una aplicación. En mi caso concreto yo he probado NDepend con un cliente en el que tenemos nada más y nada menos que 36 proyectos dentro de la misma solución (pequeñito el sistema&#8230; xD). Como evidentemente no puedo dar datos sobre los clientes de mi trabajo, el caso práctico lo vamos a plantear sobre un proyecto público que a más de uno os puede resultar familiar como es <a href="https://github.com/StackExchange/Dapper" target="_blank" rel="noreferrer noopener">Dapper</a>, un micro-ORM desarrollado por la gente de <a href="https://stackoverflow.com/" target="_blank" rel="noreferrer noopener">Stack Exchange</a> (casi nada&#8230;)</p>



<p>Lo primero de todo, es que yo he optado por utilizar la versión standalone directamente en lugar de la integración con Visual Studio, ya que en muchos proyectos utilizo Visual Studio Code. No obstante, en una prueba rápida con su integración en VS he podido comprobar que ofrece prácticamente lo mismo.</p>



<p>Para esto simplemente he tenido que ejecutar la aplicación que me he descargado sin necesidad de hacer ninguna instalación extra. Una vez dentro de Visual NDepend, basta con ir al menú de archivo y decirle que queremos analizar un proyecto o solución de VS. </p>



<p>Tras unos instantes analizando el proyecto (que dependerá del tamaño del proyecto que estemos analizando), podremos acceder a los diferentes paneles con la información.</p>



<h2 class="wp-block-heading" id="h-panel-general-dashboard">Panel general (dashboard)</h2>



<p>Este es el cuadro de mando general con las métricas del análisis, por ejemplo, en el proyecto de Dapper:</p>



<ul><li><strong>13059</strong>&nbsp;líneas de código</li><li><strong>129 </strong>archivos de código fuente</li><li><strong>11 </strong>ensamblados</li><li><strong>40 </strong>espacios de nombres</li><li><strong>6385 </strong>líneas de comentarios</li><li>Estimación del esfuerzo para realizar la aplicación:&nbsp;<strong>345 días</strong></li><li><strong>Deuda técnica:&nbsp;40 días</strong></li><li>5 días y 6 horas de esfuerzo para reducir la&nbsp;<strong>deuda técnica de C</strong>&nbsp;a B</li><li><strong>2 </strong>alertas de fallos primordiales a revisar</li><li><strong>8 </strong>alertas de reglas críticas no superadas</li></ul>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="761" height="623" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image.png" alt="La imagen muestra el panel de Visual NDepend donde se leen los datos arriba descritos" class="wp-image-2855" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image.png 761w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-300x246.png 300w" sizes="(max-width: 761px) 100vw, 761px" /></figure></div>



<h2 class="wp-block-heading" id="h-vista-de-m-tricas">Vista de métricas</h2>



<p>Este es otro panel que aporta a mi modo de ver una información importante de manera muy visual. Gracias a sus tres selectores vamos a poder configurar el nivel de agrupación (cantidad de métodos, campos, espacios de nombres,&#8230;), dentro de la agrupación, vamos a poder seleccionar en base a que métrica queremos que tenga el tamaño, y por último vamos a poder seleccionar en base a que métrica queremos que tenga el color. </p>



<p>De este modo vamos a poder hacer una revisión visual muy rápida, por ejemplo, de la complejidad ciclomática de cada método, haciendo que se represente más grande cuantas más referencias tiene:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="407" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-1-1024x407.png" alt="La imagen muestra un recuadro por cada método del proyecto en un color del verde al rojo en funcion de la complejidad ciclomática del método" class="wp-image-2856" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-1-1024x407.png 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-1-300x119.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-1-768x306.png 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-1-1536x611.png 1536w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-1.png 1566w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<h2 class="wp-block-heading" id="h-gr-fico-y-matriz-de-dependencias">Gráfico y matriz de dependencias</h2>



<p>Otros de los paneles que ofrece y que me parecen extremadamente interesantes son los referentes a las dependencias dentro de la solución. Estos paneles de dependencias nos van a mostrar, valga la redundancia, las dependencias. </p>



<p>De manera gráfica:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="407" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-2-1024x407.png" alt="La imagen muestra de manera visual las dependencias  entre partes del proyecto" class="wp-image-2859" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-2-1024x407.png 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-2-300x119.png 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-2-768x306.png 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-2-1536x611.png 1536w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-2.png 1566w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>Y para proyectos más grandes, en forma de matriz:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="714" height="623" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-3.png" alt="" class="wp-image-2861" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-3.png 714w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/image-3-300x262.png 300w" sizes="(max-width: 714px) 100vw, 714px" /></figure></div>



<h2 class="wp-block-heading" id="h-conclusi-n">Conclusión</h2>



<p>Aunque he tenido poco trato con NDepend en batallas del día a día, la verdad es que creo que es una herramienta con mucho potencial. La posibilidad de crear reglas de análisis propias le otorga un grado extra de flexibilidad que creo que puede conseguir que se adapte a más escenarios. </p>



<p>El soporte que ofrece para diferentes entornos de integración y despliegue continuo la verdad que me parece una maravilla también, ya que simplifica mucho el proceso de utilización de la herramienta.</p>



<p>Más allá del coste de licenciamiento, que puede ser o no una barrera para adoptar NDepend (como cualquier otro software), si que creo importante resaltar que es una herramienta muy focalizada en .Net. </p>



<p>También he de añadir que <a rel="noreferrer noopener" href="https://www.ndepend.com/docs/sonarqube-integration-ndepend" target="_blank">NDepend se integra con SonarQube</a> y puede hacer su trabajo también junto a este si necesitas encajarlo con esta herramienta.</p>



<p>Si con todo esto que he comentado, crees que puede encajarte esta herramienta, te recomiendo echarle un ojo tanto a sus <a rel="noreferrer noopener" href="https://www.ndepend.com/purchase" target="_blank">costes</a> como a su <a rel="noreferrer noopener" href="https://www.ndepend.com/download" target="_blank">versión de prueba</a>.</p>



<h2 class="wp-block-heading" id="h-participa-en-el-sorteo">¡Participa en el sorteo!</h2>



<p>Como comentaba en el titulo, NDepend ha cedido muy amablemente&nbsp;<strong>una licencia de desarrollador, valorada en 399 Euros</strong>, para sortear entre los lectores de FixedBuffer.</p>



<p>Para participar,&nbsp;<strong>basta con escribir un comentario en este post</strong> (OJO, en la sección de correo electrónico hay que poner uno válido o no podré contactar), podéis aprovechar también para contarnos qué os parece NDepend o en qué pensáis que os podría ayudar, todo lo que os gusta FixedBuffer, o si hace frío o calor. La cosa es comentar algo para participar <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>El sorteo se realizará&nbsp;<strong>el próximo domingo día 10</strong> <strong>octubre de 2020</strong> y consistirá en elegir al azar entre uno de los comentarios. Tras ello, me pondré en contacto con el autor para detallarle los pasos a dar para reclamar su licencia gratuita.</p>



<p>¡No dejéis de participar! ¡Suerte!</p>



<blockquote class="wp-block-quote"><p>Edición 10-10-2020:<br>Felicidades Lorenzo, tu comentario ha sido el ganador!!!</p></blockquote>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="361" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/Sorteo-1024x361.jpg" alt="La imagen muestra el resultado del sorteo donde se ve que Lorenzo es el ganador" class="wp-image-2887" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/09/Sorteo-1024x361.jpg 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/Sorteo-300x106.jpg 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/Sorteo-768x271.jpg 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/Sorteo.jpg 1462w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
<p>**La entrada <a href="https://www.fixedbuffer.com/mejorando-la-calidad-del-codigo-con-ndepend/">Mejorando la calidad del código con NDepend (con sorteo de licencia!)</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/mejorando-la-calidad-del-codigo-con-ndepend/feed/</wfw:commentRss>
			<slash:comments>16</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2842</post-id>	</item>
		<item>
		<title>¡¡FixedBuffer ha cumplido su segundo año!!</title>
		<link>https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-segundo-ano/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=fixedbuffer-ha-cumplido-su-segundo-ano</link>
					<comments>https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-segundo-ano/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Fri, 11 Sep 2020 08:00:00 +0000</pubDate>
				<category><![CDATA[Varios]]></category>
		<category><![CDATA[Cumpleaños]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=2806</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 3</span> <span class="rt-label rt-postfix">minutos</span></span>Fin del verano y vuelta a la rutina. Parece mentira que hoy hace 2 años empezase la andadura de este blog. Han sido dos años de descubrimiento personal y la ... </p>
<p class="read-more-container"><a title="¡¡FixedBuffer ha cumplido su segundo año!!" class="read-more button" href="https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-segundo-ano/#more-2806" aria-label="Más en ¡¡FixedBuffer ha cumplido su segundo año!!">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-segundo-ano/">¡¡FixedBuffer ha cumplido su segundo año!!</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> 3</span> <span class="rt-label rt-postfix">minutos</span></span>
<div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large"><img decoding="async" width="512" height="457" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/2nd.jpg" alt="Imagen ornamental. Pastel de cumpleaños con una vela en forma de número 2" class="wp-image-2809" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/09/2nd.jpg 512w, https://www.fixedbuffer.com/wp-content/uploads/2020/09/2nd-300x268.jpg 300w" sizes="(max-width: 512px) 100vw, 512px" /></figure></div>



<p>Fin del verano y vuelta a la rutina. Parece mentira que hoy hace 2 años empezase la andadura de este blog. Han sido dos años de descubrimiento personal y la verdad no pensaría que llegaría hasta aquí <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Con esta entrada ya se convierte en tradición (2 de 2 veces) el día del aniversario echar la vista hacia atrás y hacer un resumen (breve, que no quiero aburrir) de los principales hitos del año anterior.</p>



<p>En primer lugar, el hecho más evidente, FixedBuffer sigue dando guerra y para celebrarlo, el blog ha tenido un lavado de cara para darle un aire más moderno y minimalista, pero sobre todo mejor optimizado para ordenador y móvil. El número de lectores desde móvil y tablet, aunque es mucho menor que el de ordenador, va creciendo poco a poco y todos os merecéis la mejor experiencia posible el tiempo que pasáis aquí.</p>



<div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large"><img decoding="async" width="480" height="270" src="https://www.fixedbuffer.com/wp-content/uploads/2020/09/heart.gif" alt="Imagen animada de una persona haciendo un corazón con las manos" class="wp-image-2812"/></figure></div>



<blockquote class="wp-block-quote"><p>Estaré encantado de leer en los comentarios opiniones sobre el nuevo estilo <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p></blockquote>



<p>En segundo lugar, he sido renovado de nuevo como MVP en la categoría de &#8216;Tecnologías de desarrollo&#8217; y ya llevo 2 anillos. Este año he recibido el premio en la mejor compañía como os <a href="https://www.fixedbuffer.com/renovado-como-mvp-2020/">contaba en su momento</a>.</p>



<p>Tercero y no por eso menos importante, me he convertido en tutor de mi propio curso en CampusMVP: <a href="https://www.campusmvp.es/catalogo/Product-Testing-de-aplicaciones-.NET-y-.NET-Core-con-xUnit-y-Moq_244.aspx" target="_blank" rel="noreferrer noopener">Testing de aplicaciones .NET y .NET Core con xUnit y Moq</a>. Han sido muchos meses haciendo un gran esfuerzo para sacar el curso adelante, pero todo ha ido genial y la gente está muy contenta con él (cosa que me hace tremendamente feliz).</p>



<blockquote class="wp-block-quote is-style-default"><p>Por cierto, con motivo del aniversario la gente de CampusMVP hace un descuento del 10% utilizando el código <strong>FIXED2Y</strong> antes del 20 de septiembre de 2020. Si estabas pensando en hacer el curso, es la oportunidad perfecta.</p></blockquote>



<p>Además, he podido coincidir con algunos de vosotros en las charlas que me han dejado dar por diferentes comunidades (esas cosas que hacíamos antes del COVID-19 como excusa para juntarnos y beber cerveza). Eso es algo que siempre gusta y es de agradecer porque echamos muy buenos ratos <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Otra cosa que me gustaría decir es que por fin he puesto al día la sección de <a href="https://www.fixedbuffer.com/colaboraciones/">colaboraciones</a>, donde si os habéis quedado con ganas de leer más cosillas de las que he escrito, podéis ir y consultar ya que hay cerca de 20 artículos más publicados en otros blogs.</p>



<p>Se avecina un año muy interesante por delante. De antemano os quiero dar las gracias a todos ya que, si no fuese por vosotros, FixedBuffer no estaría cumpliendo su segundo año online, y es que como he dicho ya muchas veces, un blog sin lectores no es un blog. Dicho esto, vamos a ver el top 5 de entradas del último año:</p>



<ol id="block-519d6160-c1f2-46da-a838-d24dae7be6f0"><li><a href="https://www.fixedbuffer.com/index.php/entity-framework-core-2/">Haciendo fácil el acceso a datos con Entity Framework Core (Parte 2)</a></li><li><a href="https://www.fixedbuffer.com/worker-service-como-crear-un-servicio-net-core-3-multiplataforma/">Worker Service: Cómo crear un servicio .Net Core 3 multiplataforma</a></li><li><a href="https://www.fixedbuffer.com/index.php/generacion-de-ficheros-excel-con-closedxml/">Generación de ficheros «Excel» (xlsx) con ClosedXML</a></li><li><a href="https://www.fixedbuffer.com/index.php/closedxml-dar-formato-a-nuestros-xlsx/">ClosedXML, una manera fácil de dar formato a nuestros .xlsx</a></li><li><a href="https://www.fixedbuffer.com/index.php/como-crear-un-servicio-net-core-multiplataforma/"><a href="https://www.fixedbuffer.com/index.php/acceso-a-datos-con-entity-framework-core/">Haciendo fácil el acceso a datos con Entity Framework Core</a></a></li></ol>



<blockquote class="wp-block-quote"><p>Y seguido muy de cerca se ha quedado <a href="https://www.fixedbuffer.com/escribiendo-codigo-de-alto-rendimiento-en-net/">Escribiendo código de alto rendimiento en .Net Core</a>. Una entrada muy especial por conseguir la friolera de <strong>473 visitas en un solo día</strong>, un record nada despreciable para un pequeño blog como este <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p></blockquote>



<p>¡¡Muchas gracias a todos los seguidores de FixedBuffer, que sois los que hacéis que esto valga la pena!!</p>
<p>**La entrada <a href="https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-segundo-ano/">¡¡FixedBuffer ha cumplido su segundo año!!</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/fixedbuffer-ha-cumplido-su-segundo-ano/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2806</post-id>	</item>
		<item>
		<title>¡A descansar!</title>
		<link>https://www.fixedbuffer.com/a-descansar/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=a-descansar</link>
					<comments>https://www.fixedbuffer.com/a-descansar/#respond</comments>
		
		<dc:creator><![CDATA[JorTurFer]]></dc:creator>
		<pubDate>Tue, 14 Jul 2020 18:04:58 +0000</pubDate>
				<category><![CDATA[Varios]]></category>
		<guid isPermaLink="false">https://www.fixedbuffer.com/?p=2731</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> &#60; 1</span> <span class="rt-label rt-postfix">minuto</span></span>EDITADO: Por fin he actualizado la imagen y así es como se ve la caseta 🙂 Un año más llega el veranito y con él las vacaciones. Es momento de ... </p>
<p class="read-more-container"><a title="¡A descansar!" class="read-more button" href="https://www.fixedbuffer.com/a-descansar/#more-2731" aria-label="Más en ¡A descansar!">Leer más</a></p>
<p>**La entrada <a href="https://www.fixedbuffer.com/a-descansar/">¡A descansar!</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Tiempo de lectura:</span> <span class="rt-time"> &lt; 1</span> <span class="rt-label rt-postfix">minuto</span></span>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" width="1024" height="768" src="https://www.fixedbuffer.com/wp-content/uploads/2020/07/caseta-1024x768.jpeg" alt="En la imagen se ve una caseta hecha de madera y paja" class="wp-image-2822" srcset="https://www.fixedbuffer.com/wp-content/uploads/2020/07/caseta-1024x768.jpeg 1024w, https://www.fixedbuffer.com/wp-content/uploads/2020/07/caseta-300x225.jpeg 300w, https://www.fixedbuffer.com/wp-content/uploads/2020/07/caseta-768x576.jpeg 768w, https://www.fixedbuffer.com/wp-content/uploads/2020/07/caseta-1536x1152.jpeg 1536w, https://www.fixedbuffer.com/wp-content/uploads/2020/07/caseta.jpeg 2000w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>EDITADO: Por fin he actualizado la imagen y así es como se ve la caseta <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Un año más llega el veranito y con él las vacaciones. Es momento de coger aire, descansar y recuperar fuerzas para seguir adelante. Este año debido a los problemas de movilidad por esta pandemia mundial me ha pillado el toro y repito foto :(.</p>



<p>He de decir que la casa está terminada (recordemos que a eso me dediqué el verano pasado) y a la vuelta de vacaciones prometo actualizar la foto por la de la casita terminada <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Dicho esto, es hora de decir adiós hasta septiembre, momento en el que volveremos a la carga con unas ideas a las que estoy dando forma y que espero poner en marcha antes del segundo aniversario de FixedBuffer. Además de eso, ya me conocéis, no puedo parar, así que estoy preparando contenido para algunos otros blogs con los que colaboro así que estad atentos a la <a href="https://www.fixedbuffer.com/colaboraciones/">sección de colaboraciones</a> <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Muchas gracias a todos los que hacéis que este proyecto siga adelante y nos vemos en septiembre</p>
<p>**La entrada <a href="https://www.fixedbuffer.com/a-descansar/">¡A descansar!</a> se publicó primero en <a href="https://www.fixedbuffer.com">Fixed Buffer</a>.**</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fixedbuffer.com/a-descansar/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2731</post-id>	</item>
	</channel>
</rss>
