<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jonathan</title>
    <description>The latest articles on DEV Community by Jonathan (@jevillanueva).</description>
    <link>https://dev.to/jevillanueva</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F335212%2F55677ba5-c268-4c9a-a250-66e9710ff8ed.jpg</url>
      <title>DEV Community: Jonathan</title>
      <link>https://dev.to/jevillanueva</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jevillanueva"/>
    <language>en</language>
    <item>
      <title>Construir imágenes de Docker Multi-arch utilizando Manifest, Buildx y GitHub Actions</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sun, 14 Aug 2022 04:59:00 +0000</pubDate>
      <link>https://dev.to/jevillanueva/construir-imagenes-de-docker-multi-arch-utilizando-manifest-buildx-y-github-actions-pbo</link>
      <guid>https://dev.to/jevillanueva/construir-imagenes-de-docker-multi-arch-utilizando-manifest-buildx-y-github-actions-pbo</guid>
      <description>&lt;p&gt;Cuando trabajas en entornos de arquitectura uniforme es bastante fácil lidiar con la construcción de imágenes de docker, todos trabajando bajo la misma arquitectura “amd64” por ejemplo.&lt;br&gt;
Pero qué sucede cuando trabajas con una Macbook Air con un chip M1 que es “arm64”,  y tiene que generar imágenes para “amd64”, o a la inversa, trabajas con un equipo “amd64” y tienes que levantar contenedores en un cluster de Raspberry PI o servidores ARM, que están ganando cada vez más popularidad entre los proveedores de cloud.&lt;/p&gt;

&lt;p&gt;El objetivo de hoy es enseñar desde mi perspectiva y en español a generar imágenes docker multiarquitectura o multi-arch y publicarlas en el Docker Hub.&lt;/p&gt;

&lt;p&gt;Lo primero es tener una aplicación en mi caso utilizaré un archivo simple de Python que nos obtenga información acerca del actual sistema. Para lo cual utilizaremos la librería &lt;a href="https://docs.python.org/3/library/platform.html"&gt;platform&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#main.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;platform&lt;/span&gt;
&lt;span class="n"&gt;platform_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'Machine:&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;platform_var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'System:&lt;/span&gt;&lt;span class="se"&gt;\t\t&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;platform_var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'Node:&lt;/span&gt;&lt;span class="se"&gt;\t\t&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;platform_var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'Release:&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;platform_var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'Version:&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;platform_var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'Processor:&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;platform_var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo siguiente es armar nuestro Dockerfile normalmente de la siguiente manera, esta es la versión inicial, más adelante le haremos pequeñas modificaciones para poder visualizar en qué arquitectura estamos construyendo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:alpine3.10&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "python", "main.py" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hasta este punto no tenemos nada del otro mundo podemos generar la imagen y hacerla correr.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; jevillanueva/multiarch:latest
docker run jevillanueva/multiarch:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Donde tenemos una salida similar a esta:&lt;/p&gt;




&lt;p&gt;Machine:            x86_64&lt;br&gt;
System:             Linux&lt;br&gt;
Node:       8ca0b41e5372&lt;br&gt;
Release:    4.19.128-microsoft-standard&lt;br&gt;
Version:            #1 SMP Tue Jun 23 12:58:10 UTC 2020&lt;br&gt;
Processor:&lt;/p&gt;



&lt;p&gt;Hasta aquí ya sabemos que funciona lo siguiente es modificar el Dockerfile donde añadiremos un par de cosas para poder visualizar lo que pasa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt;  --platform=$TARGETPLATFORM python:alpine3.10&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETPLATFORM&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"construyendo para &lt;/span&gt;&lt;span class="nv"&gt;$TARGETPLATFORM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /log
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "python", "main.py" ]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Añadimos la opción platform  y el argumento “TARGETPLATFORM”, en este caso&lt;/p&gt;

&lt;p&gt;Existen dos caminos para realizar el proceso multi-arch, la primera la más antigua xD, la describiremos a continuación nos servirá para entender cómo funciona.&lt;/p&gt;

&lt;h2&gt;
  
  
  DOCKER MANIFEST
&lt;/h2&gt;

&lt;p&gt;Todas las imágenes de docker cuentan con un manifest, un manifest contiene información acerca de la imagen, sus layers o capas,  el tamaño y su digest que es un id inmutable para cada imagen, para nuestro caso de multi-arch esto es vital ya que en el manifiesto se encuentra detallado para imágenes con funcionalidades idénticas pero para diferentes sistemas operativos y arquitecturas. Puede ver mas de &lt;a href="https://docs.docker.com/engine/reference/commandline/manifest/"&gt;manifest&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos a inspeccionar el manifiesto de la imagen base que utilizamos que es “python:alpine3.10”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker manifest inspect python:alpine3.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tendremos una salida similar a esta que nos muestra el tamaño, su digest, las plataformas, el sistema operativo y sus arquitecturas&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.list.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"manifests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1368&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:655edcda221823fcdb79b61095dd77e6c767bf1543505dcf078f6945497c7fcf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1368&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:16779e4847cac747c0496fc56d0daa7c2090ea6cb2e4c9aa455672d6818d7179"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v6"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1368&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:3dee5de82f12477d1f0a8ed0836181064863ba84cd8098b9c62c65f8347c656b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v7"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1368&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:001faf6c4b5e87a3a4b251e72bb9a9dd1a5ef93083fc3a42d54e8734dc76e1f5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arm64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v8"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1368&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:42829bfc005ccdd282d82da1328c9a2511e20f09439c702311d30627c91c70ca"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"386"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1368&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:52e6ffac79478822ea62492d73c7aac3eca9a1cf41e8531899744e8e4958c236"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ppc64le"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1368&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:90f421378ca9b94b071a20e7c4b2df746d9b4bc26dcbd85ea79f0db9322d330f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s390x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hasta aquí ya comprendemos a grandes rasgos qué papel importante juega manifest, ahora nos utilizaremos para crear las imágenes multi-arch así que manos a la obra, realizaremos para dos arquitecturas amd64 y arm64.&lt;/p&gt;

&lt;h2&gt;
  
  
  Imagenes usando manifest
&lt;/h2&gt;

&lt;p&gt;Lo primero es construir las imágenes para cada arquitectura&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# AMD64&lt;/span&gt;
docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; jevillanueva/multiarch:manifest-amd64 &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;TARGETPLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amd64
docker push jevillanueva/multiarch:manifest-amd64

&lt;span class="c"&gt;# ARM64&lt;/span&gt;
docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; jevillanueva/multiarch:manifest-arm64 &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;TARGETPLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64
docker push jevillanueva/multiarch:manifest-arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ya tenemos construidas nuestras dos imágenes para diferentes arquitecturas y publicadas en el Docker Hub puede verse algo así &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dc22yFtd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbwt2dksvy671tijgpi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dc22yFtd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbwt2dksvy671tijgpi6.png" alt="Docker Hub Con Manifest" width="880" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora creemos el manifiesto que listará estas dos imágenes con diferentes tags en una sola unidad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker manifest create jevillanueva/multiarch:manifest-latest &lt;span class="nt"&gt;--amend&lt;/span&gt; jevillanueva/multiarch:manifest-amd64 &lt;span class="nt"&gt;--amend&lt;/span&gt; jevillanueva/multiarch:manifest-arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y Posterior a este realizamos el publicado del nuevo manifiesto&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker manifest push jevillanueva/multiarch:manifest-latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como se puede visualizar ahora en nuestro registry se encuentra un nuevo tag que hace referencia a las dos imágenes y sus respectivos sistemas operativos y arquitecturas&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SsFUqznQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5cpde052at07u0ug7xu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SsFUqznQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5cpde052at07u0ug7xu3.png" alt="hub Nuevo Manifiesto" width="880" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos probar las imágenes basadas en manifest desde diferentes instancias para obtener los resultados de la arquitectura mediante python&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run jevillanueva/multiarch:manifest-latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;He aquí el resultado desde mi máquina personal de amd64 y desde una máquina arm64&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ViwekZPa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bx1sp17ynw9nufe2kgoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ViwekZPa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bx1sp17ynw9nufe2kgoi.png" alt="Ejecuciones" width="880" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hasta aquí es la forma clásica y detallada para comprender el porqué de las cosas xD ahora algo mucho más rápido actualmente existen una nueva herramienta desarrollada por Docker que podemos utilizar para estos escenarios llamada “Buildx” &lt;/p&gt;

&lt;h2&gt;
  
  
  DOCKER BUILDX
&lt;/h2&gt;

&lt;p&gt;Docker buildx es una herramienta que nos permite acceder a las características de la herramienta de construcción &lt;a href="https://github.com/moby/buildkit"&gt;moby buildkit&lt;/a&gt;, nos permite soportar multiples instancias asi que procedemos a crear nuestras imágenes para diferentes plataformas, creamos la instancia para buildx llamada “mybuild” y usamos esa instancia&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker buildx create &lt;span class="nt"&gt;--name&lt;/span&gt; mybuild
docker buildx use mybuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker buildx build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--push&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; jevillanueva/multiarch:buildx-latest &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64,linux/arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Después de un proceso de descarga y compilación utilizando moby/buildkit  se publica la imagen en el docker hub y se tiene un resultado similar que usando manifest&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vCqABdZw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6n17fbvbl3kfxsxrnmbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vCqABdZw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6n17fbvbl3kfxsxrnmbo.png" alt="Construyendo con buildx" width="880" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Del mismo modo procedemos a probar desde diferentes dispositivos con diferentes arquitecturas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Oysxvrq1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ndo5e9ccdt6iecomscv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Oysxvrq1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ndo5e9ccdt6iecomscv.png" alt="prueba con buildx" width="880" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como se puede ver existen varias maneras de realizar esto. Y para finalizar como un extra les dejo un modelo de para github actions para generar imágenes multiarch utilizando qemu para virtualizar las arquitecturas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Actions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker Image CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.repository }}&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; 
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up QEMU&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-qemu-action@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v2&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker Login&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DOCKER_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.DOCKER_USER}}&lt;/span&gt;
        &lt;span class="na"&gt;DOCKER_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.DOCKER_PASSWORD}}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;docker login -u $DOCKER_USER -p $DOCKER_PASSWORD &lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
        &lt;span class="na"&gt;platforms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/amd64,linux/arm64&lt;/span&gt;
        &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.IMAGE_NAME }}:${{env.IMAGE_TAG}},${{ env.IMAGE_NAME }}:latest&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí encuentran el &lt;a href="https://github.com/jevillanueva/multiarch"&gt;repositorio del proyecto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enlaces utilizados:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/registry/spec/manifest-v2-2/"&gt;https://docs.docker.com/registry/spec/manifest-v2-2/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://docs.docker.com/engine/reference/commandline/manifest/"&gt;https://docs.docker.com/engine/reference/commandline/manifest/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://docs.docker.com/build/buildx/multiplatform-images/"&gt;https://docs.docker.com/build/buildx/multiplatform-images/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/docker/setup-buildx-action#with-qemu"&gt;https://github.com/docker/setup-buildx-action#with-qemu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahí les dejo mi página personal &lt;a href="https://jevillanueva.dev"&gt;jevillanueva.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>tutorial</category>
      <category>spanish</category>
      <category>python</category>
    </item>
    <item>
      <title>Crear una función en Python para desplegar en OpenFaas (2/2)</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sat, 06 Aug 2022 00:35:00 +0000</pubDate>
      <link>https://dev.to/jevillanueva/crear-una-funcion-en-python-para-desplegar-en-openfaas-22-2k9e</link>
      <guid>https://dev.to/jevillanueva/crear-una-funcion-en-python-para-desplegar-en-openfaas-22-2k9e</guid>
      <description>&lt;p&gt;Lo primero que necesitamos es el cliente de Open Faas “faas-cli” para lo cual podemos escoger desde su página los diferentes binarios &lt;a href="https://docs.openfaas.com/cli/install/"&gt;CLI Install&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En mi caso y para este ejemplo lo realizaré en Windows, para descargar el cliente lo primero es obtener la versión última del cli:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Invoke-WebRequest &lt;span class="s2"&gt;"https://api.github.com/repos/openfaas/faas-cli/releases/latest"&lt;/span&gt; | ConvertFrom-Json&lt;span class="o"&gt;)[&lt;/span&gt;0].tag_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y ahora descargamos el ejecutable de acuerdo a la última versión:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;New-Object System.Net.WebClient&lt;span class="o"&gt;)&lt;/span&gt;.DownloadFile&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/openfaas/faas-cli/releases/download/&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="s2"&gt;/faas-cli.exe"&lt;/span&gt;, &lt;span class="s2"&gt;"faas-cli.exe"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podríamos agregar como variable de entorno en la ruta PATH para poder acceder a él sin necesidad de invocar toda la ruta o sino utilizarlo mediante la ruta absoluta del ejecutable.&lt;/p&gt;

&lt;p&gt;En el blog anterior hable sobre la instalación de OpenFaas en el cual al finalizar se obtiene el password del servidor para poder ingresar&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/jevillanueva/openfaas-con-k3s-en-un-servidor-arm64-2ib2"&gt;https://dev.to/jevillanueva/openfaas-con-k3s-en-un-servidor-arm64-2ib2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ese password guardaremos en un archivo para poder iniciar sesión  llamado “password.txt” esta es una forma de realizar el login, posteriormente conectamos utilizando el archivo “password.txt” apuntando al gateway de openfaas&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .&lt;span class="se"&gt;\p&lt;/span&gt;assword.txt | faas-cli.exe login &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--gateway&lt;/span&gt; https://faas.jevillanueva.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nos generará las credenciales para nuestro usuario almacenado en la carpeta de “usuarios/.openfaas”.&lt;/p&gt;

&lt;p&gt;Fass cli, nos permite gestionar las imágenes de docker para ser cargadas a openfaas, utilizando build, push, deploy y up.&lt;/p&gt;

&lt;p&gt;Existe muchas opciones para deployar cuenta con una variedad de &lt;a href="https://docs.openfaas.com/cli/templates/"&gt;templates&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para esta guía utilizaremos python&lt;/p&gt;

&lt;p&gt;faas-cli new echo --lang python3&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WFaYFlHl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2mveudx6xcfc5xgxrcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WFaYFlHl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2mveudx6xcfc5xgxrcw.png" alt="Crear Funcion" width="516" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Genera una estructura de proyecto similar a esta&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KhHGpcxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/co9zabhuil2pvkmohwdt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KhHGpcxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/co9zabhuil2pvkmohwdt.png" alt="Estructura Proyecto" width="341" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el archivo handler.py se encuentra el request que se recibirá cuando esté en línea&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""handle a request to the function
    Args:
        req (str): request body
    """&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modificaremos el archivo “echo.yml” con las características de nuestro servidor de openfaas&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#echo.yml&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;
&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openfaas&lt;/span&gt;
  &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://faas.jevillanueva.dev/&lt;/span&gt; &lt;span class="c1"&gt;#Dirección del servidor openfaas&lt;/span&gt;
&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;echo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./echo&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jevillanueva/echo-openfaas:latest&lt;/span&gt; &lt;span class="c1"&gt;#nombre de la imagen de docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para estos ejemplos se esta utilizando imágenes públicas para que sea de fácil consumo y deploy utilizando Docker Hub como Container Registry&lt;/p&gt;

&lt;p&gt;Realizamos el build&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;faas-cli build &lt;span class="nt"&gt;-f&lt;/span&gt; ./echo.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subimos al docker hub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;faas-cli push &lt;span class="nt"&gt;-f&lt;/span&gt; ./echo.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez compilada y publicada la imagen en el docker hub ya podemos levantar la función en nuestro servidor openfaas&lt;/p&gt;

&lt;p&gt;Levantamos la función utilizando&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;faas-cli deploy &lt;span class="nt"&gt;-f&lt;/span&gt; ./echo.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos visualizar desde la página de administración la función e invocar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yifXsPLY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmddu897oek71mh8qdgv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yifXsPLY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmddu897oek71mh8qdgv.png" alt="Funcion desplegada" width="880" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos probar nuestro endpoint utilizando postman o un curl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="s1"&gt;'https://faas.jevillanueva.dev/function/echo'&lt;/span&gt; &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{"message": "Hola Mundo"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d_FH6PnN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3l9uivb4tfxonlw19go.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d_FH6PnN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3l9uivb4tfxonlw19go.png" alt="Desde Postman" width="880" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  EXTRA
&lt;/h2&gt;

&lt;p&gt;Como estamos trabajando sobre Windows en un equipo con Arquitectura de 64 Bits, y el servidor de OpenFaas está sobre ARM64 necesitaremos compilar las imágenes de docker de manera compatible para ARM utilizaremos para esto &lt;a href="https://docs.docker.com/build/buildx/"&gt;Buildx&lt;/a&gt;, donde nos permitirá otra forma de generar las imagenes y &lt;a href="https://www.qemu.org/"&gt;QEMU&lt;/a&gt; para virtualizar un entorno ARM. Para lo cual primero debemos descargar las herramientas de QEMU&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--privileged&lt;/span&gt;  multiarch/qemu-user-static &lt;span class="nt"&gt;--reset&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez descargadas las herramientas se puede realizar el publish multiarch en docker hub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;faas-cli publish &lt;span class="nt"&gt;-f&lt;/span&gt; ./echo.yml &lt;span class="nt"&gt;--platforms&lt;/span&gt; linux/arm64,linux/amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El proyecto implementado se encuentra en: &lt;br&gt;
&lt;a href="https://github.com/jevillanueva/echo-openfaas"&gt;https://github.com/jevillanueva/echo-openfaas&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://hub.docker.com/r/jevillanueva/echo-openfaas"&gt;https://hub.docker.com/r/jevillanueva/echo-openfaas&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Recursos Utilizados:&lt;br&gt;
&lt;a href="https://docs.openfaas.com/cli/install/"&gt;https://docs.openfaas.com/cli/install/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.openfaas.com/cli/templates/"&gt;https://docs.openfaas.com/cli/templates/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://docs.docker.com/build/buildx/"&gt;https://docs.docker.com/build/buildx/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.qemu.org/"&gt;https://www.qemu.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para ver el post anterior de levantamiento de servidor de Open Faas pueden ver el post anterior &lt;br&gt;
&lt;a href="https://dev.to/jevillanueva/openfaas-con-k3s-en-un-servidor-arm64-2ib2"&gt;"OpenFaaS con K3S en un servidor ARM64 (1/2)"&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahí les dejo mi página personal &lt;a href="https://jevillanueva.dev"&gt;jevillanueva.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>openfaas</category>
      <category>spanish</category>
    </item>
    <item>
      <title>OpenFaaS con K3S en un servidor ARM64 (1/2)</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Mon, 11 Jul 2022 23:09:00 +0000</pubDate>
      <link>https://dev.to/jevillanueva/openfaas-con-k3s-en-un-servidor-arm64-2ib2</link>
      <guid>https://dev.to/jevillanueva/openfaas-con-k3s-en-un-servidor-arm64-2ib2</guid>
      <description>&lt;p&gt;En esta ocasión escribo acerca de mi experiencia levantando un servicio propio de funciones utilizando &lt;a href="https://www.openfaas.com/"&gt;https://www.openfaas.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Antes que nada primero veremos los requisitos para esta tarea.&lt;/p&gt;

&lt;p&gt;Primero fue levantado en un servidor Ampere A1 con 2 nucleos y 12 de Memoria y 100 GB de almacenamiento, se encuentra alojado en &lt;a href="https://cloud.oracle.com/"&gt;Oracle Cloud&lt;/a&gt;, cuenta con una IP pública.&lt;/p&gt;

&lt;p&gt;Después para los certificados de seguridad y asegurado del sitio mediante SSL se necesita un dominio  con el cual se pueda hacer pruebas y configurar el DNS. La forma más económica de obtener un dominio para mi fue comprarlo en &lt;a href="http://namecheap.com/"&gt;namecheap&lt;/a&gt;, desde el cual voy a crear un subdominio para acceder al servicio de funciones.&lt;/p&gt;

&lt;p&gt;Con estos requisitos podemos iniciar, lo primero que hice fue instalar &lt;a href="https://k3s.io/"&gt;K3S&lt;/a&gt;, nos permite instalar una versión “Lightweight Kubernetes”, una versión ligera y rápida para desarrollar y poner en producción un cluster de Kubernetes.&lt;/p&gt;

&lt;p&gt;Para instalar el cluster  usamos, en cual instalaremos el cluster de pruebas y su acceso a través del usuario actual que tenemos en el servidor, se podría implementar en un cluster con más nodos, en mi caso solo utilizaré uno.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; https://get.k3s.io | sh &lt;span class="nt"&gt;-s&lt;/span&gt; - &lt;span class="nt"&gt;--write-kubeconfig-mode&lt;/span&gt; 644
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl config view &lt;span class="nt"&gt;--raw&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.kube/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos verificar el estado con&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Donde obtendremos una salida similar a esta:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mmGTMQmP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/989ssornnnbj795t5ibn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mmGTMQmP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/989ssornnnbj795t5ibn.png" alt="Get Nodes" width="880" height="38"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La idea es siempre hacerlo simple entender que está sucediendo ya que son un conjunto de cosas que funcionan entre sí, para lo cual la solución pensada es similar a algo así:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BYPhK7oh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhvyl5m6pnfxpzhtqa5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BYPhK7oh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhvyl5m6pnfxpzhtqa5u.png" alt="Arquitecture" width="501" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Asi que bueno:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Divide y vencerás”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Antes de iniciar con la instalación de los diferentes componentes utilizaremos dos gestores de paquetes para Kubernetes&lt;/p&gt;

&lt;p&gt;Helm donde podemos instalarlo siguiendo la documentación &lt;a href="https://helm.sh/docs/intro/install/"&gt;Helm Install&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;700 get_helm.sh
&lt;span class="nv"&gt;$ &lt;/span&gt;./get_helm.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos verificar la instalación utilizando&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El segundo gestor de paquetes que utilizaremos es &lt;a href="https://github.com/alexellis/arkade"&gt;Arkade&lt;/a&gt;  un gestor de paquetes que nos permite instalar charts con un solo comando de  manera rápida&lt;br&gt;
Podemos instalarla y verificar utilizando los siguientes comandos&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sLS&lt;/span&gt; https://get.arkade.dev | &lt;span class="nb"&gt;sudo &lt;/span&gt;sh
&lt;span class="nv"&gt;$ &lt;/span&gt;arkade &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora que ya están instalados podemos continuar con los despliegues.&lt;/p&gt;

&lt;p&gt;Primero el Ingress, encargarnos del Ingress para lo cual primero nos asignaremos un subdominio a nuestra ip pública,  algo así “openfaas.example.com” con la cual nos aseguraremos que todo funcione incluso la emisión de certificados SSL antes de instalar open faas, en caso de utilizar Namecheap para el dominio puede seguir el siguiente enlace que explica como crear un subdominio y apuntar a la IP que vayamos a utilizar, cuando se realiza el registro puede llevar un tiempo en reflejarse me ha pasado que a veces tarda unos 10 minutos en detectar el subdominio el DNS hasta un 1 dia entero asi a tener paciencia xD &lt;a href="https://www.namecheap.com/support/knowledgebase/article.aspx/9776/2237/how-to-create-a-subdomain-for-my-domain/"&gt;Crear Subdominio en Namecheap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el ejemplo utilizamos dos subdominios uno de prueba para una aplicación de demostración y otra que nos permita acceder a openfaas.&lt;/p&gt;

&lt;p&gt;Como vamos a utilizar Nginx-ingress, necesitamos también asegurar nuestros sitios para lo cual utilizaremos cert-manager. Puede leer más al respecto del &lt;a href="https://cert-manager.io/docs/tutorials/acme/nginx-ingress/"&gt;cert-manager&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Nginx Ingress
&lt;/h2&gt;

&lt;p&gt;Lo primero vamos a instalar utilizando helm nuestro Nginx Ingress usando sus repositorios&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo update
&lt;span class="nv"&gt;$ &lt;/span&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;nginxingress ingress-nginx/ingress-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos comprobar el estado del servicio usando&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get svc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Donde tendremos una salida similar a la siguiente image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5lJDsrMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2j3wa5n99arn4rmwunwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5lJDsrMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2j3wa5n99arn4rmwunwh.png" alt="get svc" width="880" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Donde en principio se encontrara  pendiente hasta que se asocie a la IP pública, en caso de suceder puede realizarlo manualmente editando el servicio usando Kubernetes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl edit svc nginxingress-ingress-nginx-controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Donde usaremos VIM para editar nuestro servicio añadiendo al nivel del type: LoadBalancer nuestra IP externa&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;…
  &lt;span class="nb"&gt;type&lt;/span&gt;: LoadBalancer
  externalIPs:
  - XXX.XXX.XXX.XXX
…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para probar nuestro Ingress y Cert Manager utilizaremos un deploy de demostración llamado &lt;a href="https://github.com/kubernetes-up-and-running/kuard"&gt;KUARD&lt;/a&gt;. Para lo cual crearemos los dos archivos del deploy y del ingress&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# kuard-ingress.yaml&lt;/span&gt;
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kuard
spec:
  selector:
    matchLabels:
      app: kuard
  replicas: 1
  template:
    metadata:
      labels:
        app: kuard
    spec:
      containers:
      - image: gcr.io/kuar-demo/kuard-arm64:1
        imagePullPolicy: Always
        name: kuard
        ports:
        - containerPort: 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Levantamos el deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-deploy.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luego configuramos nuestro ingress para Kuard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#kuard-ingress.yaml&lt;/span&gt;
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  annotations:
    kubernetes.io/ingress.class: &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
    &lt;span class="c"&gt;# cert-manager.io/issuer: "letsencrypt-prod"&lt;/span&gt;

spec:
  tls:
  - hosts:
    - kuard.example.com &lt;span class="c"&gt;# Dominio de prueba&lt;/span&gt;
    secretName: quickstart-example-tls &lt;span class="c"&gt;# Nombre de nuestro Certificado&lt;/span&gt;
  rules:
  - host: kuard.example.com &lt;span class="c"&gt;# Dominio de prueba&lt;/span&gt;
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kuard
            port:
              number: 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Levantamos el ingress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos verificar el estado de nuestro deploy e ingres utilizando&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get ingress,deploy &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;span class="c"&gt;# probar el acceso a nuestro dominio de prueba usando su IP&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-kivL&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Host: kuard.example.com'&lt;/span&gt; &lt;span class="s1"&gt;'http://10.0.0.210'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez levantado el deploy y el Ingress tenemos que configurar nuestro cert manager que se encargará de gestionar los certificados para nuestro sitio, para lo cual nos ayudará &lt;a href="https://cert-manager.io/docs/installation/helm/#installing-with-helm"&gt;helm&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo add jetstack https://charts.jetstack.io
&lt;span class="nv"&gt;$ &lt;/span&gt;helm update
&lt;span class="nv"&gt;$ &lt;/span&gt; kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/cert-manager/cert-manager/releases/download/v1.8.2/cert-manager.crds.yaml 
&lt;span class="nv"&gt;$ &lt;/span&gt;helm &lt;span class="nb"&gt;install   &lt;/span&gt;cert-manager jetstack/cert-manager   &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager   &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;   &lt;span class="nt"&gt;--version&lt;/span&gt; v1.8.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez instalado procedemos a crear nuestros issuers, que se encargaran de generar los certificados en nuestro caso usaremos letsencrypt creando los dos siguientes issuers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# lets-staging.yaml&lt;/span&gt;
   apiVersion: cert-manager.io/v1
   kind: Issuer
   metadata:
     name: letsencrypt-staging
   spec:
     acme:
       &lt;span class="c"&gt;# The ACME server URL&lt;/span&gt;
       server: https://acme-staging-v02.api.letsencrypt.org/directory
       &lt;span class="c"&gt;# Email address used for ACME registration&lt;/span&gt;
       email: me@example.com
       &lt;span class="c"&gt;# Name of a secret used to store the ACME account private key&lt;/span&gt;
       privateKeySecretRef:
         name: letsencrypt-staging
       &lt;span class="c"&gt;# Enable the HTTP-01 challenge provider&lt;/span&gt;
       solvers:
       - http01:
           ingress:
             class:  nginx

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y el archivo para generar certificados en prod&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# lets-prod.yaml&lt;/span&gt;
   apiVersion: cert-manager.io/v1
   kind: Issuer
   metadata:
     name: letsencrypt-prod
   spec:
     acme:
       &lt;span class="c"&gt;# The ACME server URL&lt;/span&gt;
       server: https://acme-v02.api.letsencrypt.org/directory
       &lt;span class="c"&gt;# Email address used for ACME registration&lt;/span&gt;
       email: me@example.com
       &lt;span class="c"&gt;# Name of a secret used to store the ACME account private key&lt;/span&gt;
       privateKeySecretRef:
         name: letsencrypt-prod
       &lt;span class="c"&gt;# Enable the HTTP-01 challenge provider&lt;/span&gt;
       solvers:
       - http01:
           ingress:
             class: nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creamos ambos issuers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; lets-staging.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; lets-prod.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get Issuers
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl describe issuer letsencrypt-staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hasta este punto ya tenemos el cert-manager con nuestros Issuers, ahora toca configurar nuestro Ingress que estamos probando, editamos nuestro kuard-ingress.yaml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#kuard-ingress.yaml&lt;/span&gt;
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  annotations:
    kubernetes.io/ingress.class: &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
    cert-manager.io/issuer: &lt;span class="s2"&gt;"letsencrypt-staging"&lt;/span&gt;

spec:
  tls:
  - hosts:
    - kuard.example.com &lt;span class="c"&gt;# Dominio de prueba&lt;/span&gt;
    secretName: quickstart-example-tls &lt;span class="c"&gt;# Nombre de nuestro Certificado&lt;/span&gt;
  rules:
  - host: kuard.example.com &lt;span class="c"&gt;# Dominio de prueba&lt;/span&gt;
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kuard
            port:
              number: 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y  volvemos a levantar nuestro Ingress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O sino eliminamos el Ingress y levantamos nuevamente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este punto se creará el punto de ingreso y el certificado.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get certificate
NAME                     READY   SECRET                   AGE
quickstart-example-tls   True    quickstart-example-tls   16m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos ver más detalles del mismo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl describe certificate quickstart-example-tls
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl describe secret quickstart-example-tls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez comprobado que existen los certificados podemos hacer la actualización a producción de nuestro Ingress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#kuard-ingress.yaml&lt;/span&gt;
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  annotations:
    kubernetes.io/ingress.class: &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
    cert-manager.io/issuer: &lt;span class="s2"&gt;"letsencrypt-prod"&lt;/span&gt;

spec:
  tls:
  - hosts:
    - kuard.example.com &lt;span class="c"&gt;# Dominio de prueba&lt;/span&gt;
    secretName: quickstart-example-tls &lt;span class="c"&gt;# Nombre de nuestro Certificado&lt;/span&gt;
  rules:
  - host: kuard.example.com &lt;span class="c"&gt;# Dominio de prueba&lt;/span&gt;
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kuard
            port:
              number: 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y  volvemos a levantar nuestro Ingress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O sino eliminamos el Ingress y levantamos nuevamente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;También es necesario eliminar el certificado en estado de staging para que se pueda generar el certificado en producción.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt; kubectl delete secret quickstart-example-tls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez eliminado esperamos un poco hasta que se vuelva a generar un certificado esta vez apuntando al issuer de producción.&lt;br&gt;
En este punto accediendo desde el navegador puede comprobar la validez del certificado accediendo a su dominio kuard.example.com. &lt;/p&gt;

&lt;p&gt;En este punto terminamos la parte inicial de nuestro esquema, ya tenemos nuestro Ingress y Nuestro Cert Manager Funcionando correctamente ahora toca instalar OpenFaas para el servicio de Funciones.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Kuard muestra un poco de información sensible así que se recomienda eliminarlo cuando no se lo esté utilizando, puede eliminar el secret, el ingress y el deploy y eliminar el subdominio de prueba “kuard.example.com” para continuar con el servicio de OpenFaas&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt; kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-ingress.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt; kubectl delete secret quickstart-example-tls
&lt;span class="nv"&gt;$ &lt;/span&gt; kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; kuard-deploy.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En caso de querer revisar los logs de nuestro ingress nginx podemos utilizar un comando similar a este:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl logs -f $(kubectl get po -l "app.kubernetes.io/instance=nginxingress,app.kubernetes.io/component=controller" -o jsonpath="{.items[0].metadata.name}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OpenFaas
&lt;/h2&gt;

&lt;p&gt;Es un servicio de Funciones Self Hosted, que nos permite levantar e implementar funciones basados en eventos sobre Kubernetes, generando un endpoint, tal como Azure Functions o Amazon Lambda &lt;a href="https://docs.openfaas.com/"&gt;https://docs.openfaas.com/&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Puede leer más información sobre el proceso de &lt;a href="https://docs.openfaas.com/reference/ssl/kubernetes-with-cert-manager/"&gt;instalación&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para instalar OpenFaas sobre nuestro Kubernetes utilizaremos arkade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;arkade &lt;span class="nb"&gt;install &lt;/span&gt;openfaas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Utilizando helm podemos ver todos nuestros charts instalados incluido openfaas&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm list &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0j4Vgctc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8mblpgzx5e2nizhsjmv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0j4Vgctc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8mblpgzx5e2nizhsjmv.png" alt="Helm List" width="880" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez instalado openfaas vamos a crear los issuers para su namespace openfaas en el cual estaran almacenados&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# letsencrypt-issuer.yaml&lt;/span&gt;
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-staging
  namespace: openfaas
spec:
  acme:
    email: me@example.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: staging-issuer-account-key
    solvers:
    - http01:
        ingress:
          class: nginx
&lt;span class="nt"&gt;---&lt;/span&gt;
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-prod
  namespace: openfaas
spec:
  acme:
    email: me@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: prod-issuer-account-key
    solvers:
    - http01:
        ingress:
          class: nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez creado nuestro archivo de Issuers levantamos el mismo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; letsencrypt-issuer.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora procedemos a configurar nuestro openfaas creando un archivo tls.yaml y actualizando con helm nuestro chart de faas&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tls.yaml&lt;/span&gt;
ingress:
  enabled: &lt;span class="nb"&gt;true
  &lt;/span&gt;annotations:
    kubernetes.io/ingress.class: &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
    cert-manager.io/issuer: letsencrypt-staging &lt;span class="c"&gt;# utilizaremos staging de pruebas&lt;/span&gt;
  tls:
    - hosts:
        - openfaas.example.com
      secretName: openfaas-crt
  hosts:
    - host: openfaas.example.com
      serviceName: gateway
      servicePort: 8080
      path: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actualizamos nuestro chart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm upgrade openfaas &lt;span class="nt"&gt;--namespace&lt;/span&gt; openfaas &lt;span class="nt"&gt;--reuse-values&lt;/span&gt; &lt;span class="nt"&gt;--values&lt;/span&gt; tls.yaml openfaas/openfaas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esperamos un poco a que se apliquen los cambios y genere el certificado en staging y podemos comprobarlo con&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl describe certificate   &lt;span class="nt"&gt;-n&lt;/span&gt; openfaas   openfaas-crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al poder comprobar que se encuentre creado podemos cambiar ahora a prod nuestro staging&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tls.yaml&lt;/span&gt;
ingress:
  enabled: &lt;span class="nb"&gt;true
  &lt;/span&gt;annotations:
    kubernetes.io/ingress.class: &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
    cert-manager.io/issuer: letsencrypt-prod &lt;span class="c"&gt;# Cambiamos a prod&lt;/span&gt;
  tls:
    - hosts:
        - openfaas.example.com
      secretName: openfaas-crt
  hosts:
    - host: openfaas.example.com
      serviceName: gateway
      servicePort: 8080
      path: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Volvemos a actualizar nuestro chart con helm de openfaas&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm upgrade openfaas &lt;span class="nt"&gt;--namespace&lt;/span&gt; openfaas &lt;span class="nt"&gt;--reuse-values&lt;/span&gt; &lt;span class="nt"&gt;--values&lt;/span&gt; tls.yaml openfaas/openfaas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si el certificado no se regenera con el Issuer de producción puede ser necesario eliminarlo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; openfaas delete secret  openfaas-crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora hay q descargar las credenciales auth del admin del open faas para poder acceder desde el cliente o la web&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ PASSWORD=$(kubectl get secret -n openfaas basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode; echo)
$ echo $PASSWORD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este password guardalo con mucha seguridad ya que es la clave de acceso a su openfaas.&lt;/p&gt;

&lt;p&gt;Una vez que se genera el certificado, ya se encuentra listo para acceder a su openfaas para lo cual puede realizarlo desde el navegador accediendo a su dominio openfaas.example.com&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qyi0A3HF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9d8cesk8waizig4m5x0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qyi0A3HF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9d8cesk8waizig4m5x0.png" alt="OpenFaas" width="880" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como se puede observar se encuentra ya con https y está listo para poder empezar a deployar sus funciones, tanto desde la web como desde su cliente.&lt;/p&gt;

&lt;p&gt;Para la instalación del &lt;a href="https://docs.openfaas.com/cli/install/"&gt;cliente de openfaas&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursos utilizados:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm"&gt;https://docs.oracle.com/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm&lt;/a&gt;&lt;br&gt;
&lt;a href="https://rancher.com/docs/k3s/latest/en/"&gt;https://rancher.com/docs/k3s/latest/en/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://helm.sh/docs/intro/install/"&gt;https://helm.sh/docs/intro/install/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://github.com/alexellis/arkade"&gt;https://github.com/alexellis/arkade&lt;/a&gt; &lt;br&gt;
&lt;a href="https://cert-manager.io/docs/tutorials/acme/nginx-ingress/"&gt;https://cert-manager.io/docs/tutorials/acme/nginx-ingress/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.openfaas.com/deployment/kubernetes/"&gt;https://docs.openfaas.com/deployment/kubernetes/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://docs.openfaas.com/cli/install/"&gt;https://docs.openfaas.com/cli/install/&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Aqui esta el siguiente POST de una función en Python con OpenFaas &lt;br&gt;
&lt;a href="https://dev.to/jevillanueva/crear-una-funcion-en-python-para-desplegar-en-openfaas-22-2k9e"&gt;"Crear una función en Python para desplegar en OpenFaas (2/2)"&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahí les dejo mi página personal &lt;a href="https://jevillanueva.dev"&gt;jevillanueva.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>k3s</category>
      <category>openfaas</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Automatizando lectura de mensajes de Discord con Python y Alexa Skills</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sun, 26 Dec 2021 10:15:16 +0000</pubDate>
      <link>https://dev.to/jevillanueva/automatizando-lectura-de-mensajes-de-discord-con-python-y-alexa-skills-jid</link>
      <guid>https://dev.to/jevillanueva/automatizando-lectura-de-mensajes-de-discord-con-python-y-alexa-skills-jid</guid>
      <description>&lt;h4&gt;
  
  
  Automatización
&lt;/h4&gt;

&lt;p&gt;La automatización consiste en utilizar la tecnología para realizar tareas casi sin necesidad de realizar acciones humanas.&lt;/p&gt;

&lt;h4&gt;
  
  
  Alexa como asistente inteligente
&lt;/h4&gt;

&lt;p&gt;Un asistente inteligente es un software que realiza tareas y ofrece servicios a un individuo, ayuda en las tareas en sistemas automatizando y realizando acciones con la mínima interacción hombre-máquina. Una persona se comunica usando la voz o texto y el asistente lo procesa, identifica, interpreta, ejecuta y responde utilizando la voz o medios visuales.&lt;/p&gt;

&lt;p&gt;Alexa es el servicio  de voz ubicado en la nube de Amazon disponible en dispositivos Amazon y dispositivos con Alexa integrada, se encuentran dispositivos con solo altavoces como Echo, Echo Dot, dispositivos con pantalla cómo Echo Show, Fire Stick TV y aplicaciones en Android, Windows, y muchos otros dispositivos más incluido televisores smart.&lt;/p&gt;

&lt;h4&gt;
  
  
  Skills de alexa y Rutinas
&lt;/h4&gt;

&lt;p&gt;Una Skill son las habilidades que posee Alexa para realizar tareas, similar a una aplicación en un smartphone es una Skill de Alexa, a través de las Skills se permite realizar ciertas tareas, interactuar con otros dispositivos, estas habilidades son realizadas por  desarrolladores y compañías  para permitir a Alexa integrarse con otros Sistemas y dispositivos.&lt;/p&gt;

&lt;p&gt;Son proyectos de software o funciones que se alojan en alguna nube y se ejecutan según la demanda de los usuarios, cada vez que se invoca la habilidad específica.&lt;/p&gt;

&lt;p&gt;Las rutinas son un conjunto de reglas y ejecuciones que permiten ejecutar diferentes tareas que se programan a través de la Aplicación de Alexa, enlazando características de los diferentes dispositivos, skills, tiempo de ejecución de tareas, todas ellas permiten crear rutinas, tan simple como encender una alarma sonora a una hora específica, controlar la temperatura de un ambiente utilizando un termómetro y un ventilador, o recordarte comprar un objeto al llegar a un lugar, escuchar las noticias de diferentes proveedores con solo decirle “Quiero escuchar noticias”.&lt;/p&gt;

&lt;p&gt;Las rutinas más las Skills permiten sacar gran parte del potencial del asistente inteligente, a tal punto de influir en la vida misma y las acciones del dia a dia de cada persona, facilitando el trabajo, ayudando a recordar o informar, ayudando a mantenerse en salud óptima, divertirse y disfrutar de un momento de ocio. &lt;/p&gt;

&lt;h4&gt;
  
  
  Serverless
&lt;/h4&gt;

&lt;p&gt;La computación sin servidor o Serverless es un modelo en el cual los proveedores de cloud (AWS, Azure, GCP, etc.) son responsables de ejecutar un fragmento de código mediante la asignación dinámica de recursos. Cobrando solo por la cantidad de recursos utilizados para ejecutar el código. El código se envía al proveedor en la nube para ejecución en forma de una función, serverless puede ser denominado como “Funciones como servicio” o FaaS. &lt;/p&gt;

&lt;p&gt;La computación sin servidor no significa que no hay un servidor en el cual se ejecuta sino que los proveedores se encargan del mantenimiento y solo se cobra por la ejecución de las funciones, tradicionalmente se suele pagar  el mantenimiento de máquinas virtuales, plataformas como servicio aun cuando no se encuentran en uso por los clientes a través de solicitudes HTTP o Sockets y se encuentran en un estado Idle o de espera, con Serverless cuando no hay llamadas de ejecución, solicitudes HTTP o CRON, no hay recursos destinados a mantener activa la función en estado de espera, el costo se basa en la cantidad de recursos consumidos en cada ejecución por función.&lt;/p&gt;

&lt;p&gt;Entre los más populares se encuentra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Web Services: AWS Lambda&lt;/li&gt;
&lt;li&gt;Microsoft Azure: Azure Functions&lt;/li&gt;
&lt;li&gt;Google Cloud: Cloud Functions&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  AWS Lambda vs Azure Functions (Diferencias)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Configurabilidad
AWS Lambda, necesita configurar la asignación máxima de memoria entre 128 mb a 3 GB, se ejecuta bajo Amazon Linux.
Azure Functions, posee un plan único para todas las funciones, 1.5 GB de memoria, es posible ejecutar en Windows o Linux&lt;/li&gt;
&lt;li&gt;Lenguajes
AWS Lambda, permite JavaScript, Java, Python, C#, F#, PowerShell, Go y Ruby
Azure Functions, permite JavaScript, Java, Python, C#, F#, y PowerShell, TypeScript&lt;/li&gt;
&lt;li&gt;Costo
El precio de ambos es de aproximada 0.20 USD por millón de solicitudes y 16 dólares por millón de GB/s
AWS Lambda cobra por la capacidad total de memoria aprovisionada.
Azure Functions mide el consumo de memoria promedio real de las ejecuciones.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Iniciando el proyecto
&lt;/h4&gt;

&lt;p&gt;Crear el servidor de Discord (Si no se lo tiene)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c7vnCO7Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1wewbk9ai1vcnsz8l457.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c7vnCO7Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1wewbk9ai1vcnsz8l457.gif" alt="Crear Servidor Discord" width="576" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crear el canal de texto de noticias a utilizar, si no se tiene, puede ser público o privado.&lt;br&gt;
Para el acceso al canal se tiene que obtener su ID del canal el cual se obtiene desde el Discord, este dato será nuestro DISCORD_CHANNEL_ID con el que accederemos a un canal en específico para noticias.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R9AHbyEn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45bqp7w8zs7q6zrl4af3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R9AHbyEn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45bqp7w8zs7q6zrl4af3.png" alt="Crear un canal" width="629" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Gi_r0qS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/34d2gx4e2zxoxjdvrla3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Gi_r0qS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/34d2gx4e2zxoxjdvrla3.png" alt="Obtener el ID del Canal" width="286" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En caso de que no aparezca la opción para “Copiar ID” se debe activar desde Ajustes del Usuario &amp;gt; Avanzado &amp;gt; Modo Desarrollador&lt;/p&gt;

&lt;p&gt;Se necesita crear una aplicación de discord para conectarse con el servidor, desde &lt;a href="https://discord.com/developers/applications"&gt;Discord Developer&lt;/a&gt; , en la cual crearemos una aplicación.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--13ARr2TM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/59g9c6ii5njgtp4bj5ql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--13ARr2TM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/59g9c6ii5njgtp4bj5ql.png" alt="Crear Aplicación" width="880" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yvPpeGw1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9kwa70zihuocmdzj5apk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yvPpeGw1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9kwa70zihuocmdzj5apk.png" alt="Información general de la Aplicación" width="880" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez creada la aplicación debemos generar un bot dentro la aplicación el cual tendrá la capacidad de conectarse a los canales de Discord y obtener los mensajes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NtcUVlpL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxdlxjmf562iz01528mv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NtcUVlpL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxdlxjmf562iz01528mv.png" alt="Adicionar el Bot" width="880" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Al generar el bot podemos obtener su Token de acceso con el cual podremos acceder mediante el bot, el cual lo utilizaremos más adelante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yhYwbVPY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbjdaltviwammimf5jgo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yhYwbVPY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbjdaltviwammimf5jgo.png" alt="Detalles del Bot" width="880" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para agregar el bot de prueba a su servidor de Discord puede utilizar el siguiente enlace modificando los parámetros de acuerdo a los permisos que requiera.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discord.com/oauth2/authorize?client_id=APPLICATION_ID&amp;amp;permissions=PERMISSION_INTEGER&amp;amp;scope=bot"&gt;https://discord.com/oauth2/authorize?client_id=APPLICATION_ID&amp;amp;permissions=PERMISSION_INTEGER&amp;amp;scope=bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;APPLICATION_ID lo podremos obtener de la información general de la Aplicación, reemplazamos en la URL de autorización.&lt;/p&gt;

&lt;p&gt;PERMISSION_INTEGER es el número referencial a los permisos que tendrá el bot  usamos el 66560 para la lectura de mensajes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YOGg8L1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u91zp7pwung5wsos2xy6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YOGg8L1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u91zp7pwung5wsos2xy6.png" alt="Permisos del Bot" width="880" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para más información de los alcances existentes puede visitar: &lt;a href="https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes"&gt;https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jJNMqith--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bh1526riq9ob9uxaxrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jJNMqith--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bh1526riq9ob9uxaxrp.png" alt="Adición del Bot al Servidor" width="880" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez agregado el bot al Servidor ya podemos realizar la lectura de los mensajes, en caso de que se encuentre en un canal privado primero se debe agregar al bot al canal para poder acceder a los mensajes.&lt;/p&gt;

&lt;p&gt;Consumo del API de Discord para obtención de los mensajes de un canal&lt;/p&gt;

&lt;p&gt;Teniendo ya el bot configurado podemos realizar la lectura de los mensajes utilizando solicitudes HTTP para lo cual podemos realizar desde python de la siguiente manera.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://discordapp.com/api/channels/DISCORD_CHANNEL_ID/messages?limit=10"&lt;/span&gt;
&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Bot BOT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Crear la Skill
&lt;/h4&gt;

&lt;p&gt;Ingresamos con nuestra cuenta a &lt;a href="https://developer.amazon.com/alexa/console/ask"&gt;https://developer.amazon.com/alexa/console/ask&lt;/a&gt; desde el cual procederemos a crear nuestra Skill&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mAVctEit--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o526wxr6oyuak29d2bmv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mAVctEit--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o526wxr6oyuak29d2bmv.png" alt="Crear Skill" width="880" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Al crear nuestra Skill tenemos que configurar su nombre, el idioma primario para el que utilizaremos el Español, el modelo para el Skill será Custom ya que será proporcionado por nosotros, y la ubicación de nuestro backend que también la proporcionaremos nosotros mismos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lvnTOUlZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6q21zdqvlg30s4c404h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lvnTOUlZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6q21zdqvlg30s4c404h.png" alt="Configurar Skill" width="880" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para la demostración utilizaremos el Template inicial el cual contiene funcionalidades básicas&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aqK3pR3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cdwnhttohx1rvwadqki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aqK3pR3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cdwnhttohx1rvwadqki.png" alt="Template Skill" width="880" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez terminado de construir ya tenemos la Skill inicial en la cual nosotros podemos realizar la configuración de los Intents, Samples y Slots para que Alexa pueda entender nuestra Skill y realizar un correcto funcionamiento.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nombre de Invocación
&lt;/h4&gt;

&lt;p&gt;Lo primero que debemos revisar es el nombre de invocación de nuestra Skill, es el conjunto de palabras que permite a Alexa identificar y ejecutar nuestra Skill, ingresando a Skill Invocation Name cambiamos el nombre para la invocación, y guardamos nuestro modelo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p2wYDKhb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rbmx558mv00w3438vz1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p2wYDKhb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rbmx558mv00w3438vz1k.png" alt="Nombre Invocación Skill" width="880" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Definición de Intents, samples y slots
&lt;/h4&gt;

&lt;p&gt;Los Intents representan a las acciones que cumplen con la solicitud hablada de un usuario, para lo cual crearemos nuestro Intent desde el menú de Intents y nuestro nuevo intent será “NoticiasIntent”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jwo0G80A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7ff0l7dxlnfxo2nd9j6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jwo0G80A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7ff0l7dxlnfxo2nd9j6.png" alt="Intents de la Skill" width="880" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;De manera integrada en cada Skill tenemos  4 Intents  que son para Cancelar, Ayuda, Detener ejecución y navegar entre nuestra Skill.&lt;/p&gt;

&lt;p&gt;Posterior a crear nuestro intent debemos adicionar los Samples que representan la forma en la que los usuarios llaman a nuestro intent desde la Skill, y guardamos nuestro modelo&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h7xpFCva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qadqe4u9rhg70vww36vp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h7xpFCva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qadqe4u9rhg70vww36vp.png" alt="Samples de la Skill" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Los Slots son argumentos que nos permiten obtener parámetros del usuario que se extraen de los Intent y se envían como datos adicionales. En nuestro caso utilizaremos un slot para obtener un número y obtener un límite de mensajes de acuerdo a necesidad del usuario.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m8vKzTdn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9vagur3lzrugh37oxciz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m8vKzTdn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9vagur3lzrugh37oxciz.png" alt="Slot de la Skill" width="880" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez adicionado el Slot podemos agregar Samples que contengan nuestro Slot y tener llamadas con cantidades específicas de mensajes, quedándonos de la siguiente manera nuestro Intent con sus Samples y Slot!, y guardamos nuestro modelo Final&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E9Sa2KMe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sfitk5jnrzz5bwx48cc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E9Sa2KMe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sfitk5jnrzz5bwx48cc.png" alt="Intent Completo de la SKill" width="880" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hasta este punto ya tenemos la estructura que tendrá nuestra Skill y cómo será invocado mediante voz, ahora tenemos que construir nuestro Backend, una vez procesado y analizado la comunicación con el usuario se debe comunicar al Backend de la Skill que la tendremos alojada en Azure Functions&lt;/p&gt;

&lt;h4&gt;
  
  
  Creación de una Función con VS Code
&lt;/h4&gt;

&lt;p&gt;Para crear el Backend primero abriremos VS Code e instalaremos la extensión de Azure Functions, esto para crear y publicar más fácilmente nuestras funciones &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions"&gt;https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Desde la pestaña de Azure se crea un nuevo Proyecto en la carpeta que contendrá nuestras Funciones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zoiht1TB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tm2iq0py284vo1535gue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zoiht1TB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tm2iq0py284vo1535gue.png" alt="Crear Función" width="306" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seleccionamos el Lenguaje “Python”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--56tOz8Rw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwi81hgoimgld9ukh4e6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--56tOz8Rw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwi81hgoimgld9ukh4e6.png" alt="Seleccionar lenguaje" width="689" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El entorno virtual a utilizar, se puede realizar un skip si no se utilizara entorno virtual, pero es recomendado siempre utilizar un entorno virtual.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gK1ujNfR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iiplet3mlaiqm3a31wpl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gK1ujNfR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iiplet3mlaiqm3a31wpl.png" alt="Seleccionar entorno virtual" width="629" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seleccionamos el tipo de Template de nuestro proyecto, al ser un Backend de la Skill tiene el formato de Solicitud HTTP Trigger&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UzNctQBz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ifw8971yruq3r8d6whj4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UzNctQBz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ifw8971yruq3r8d6whj4.png" alt="Template de la Función" width="637" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como paso siguiente le proporcionamos el nombre a una función, en nuestro caso será “PotatoNoticiasTrigger”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3XT7VWHA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38c0g0318har0yu9pqx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3XT7VWHA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38c0g0318har0yu9pqx9.png" alt="Nombre de la función" width="633" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para este ejemplo utilizaremos un nivel de autorización anónimo&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--onsDycbi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7isavivqzklodv7k2wi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--onsDycbi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7isavivqzklodv7k2wi.png" alt="Autenticación de la función" width="644" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Por último lo abrimos en el mismo Workspace o en una nueva ventana&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M2TWOMbM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hms22d8cder94dbsumkf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M2TWOMbM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hms22d8cder94dbsumkf.png" alt="Ventana para el proyecto" width="645" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Con este último paso se realiza el generado del proyecto y tendremos una estructura similar de la siguiente forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── .funcignore
├── .gitignore
├── host.json
├── local.settings.json
├── PotatoNoticiasTrigger
│   ├── &lt;span class="k"&gt;function&lt;/span&gt;.json
│   ├── __init__.py
│   └── sample.dat
├── requirements.txt
└── .vscode
    ├── extensions.json
    ├── launch.json
    ├── settings.json
    └── tasks.json

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para probar podemos ejecutar desde el proyecto la función&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;func host start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q9J3jtVd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vnq2k3041qsq3btjq6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q9J3jtVd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vnq2k3041qsq3btjq6n.png" alt="Prueba Función" width="880" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Al momento de iniciar se levanta la instancia  de Azure Functions Core Tools, puede suceder una excepción&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Value cannot be null. Parameter name: Provider error on func start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puede ser causada por la versión de la herramienta por lo cual una forma es verificar la versión en el archivo host.json&lt;br&gt;
Para el proyecto actual se utiliza la version "[3.3.0, 4.0.0)",&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"extensionBundle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Microsoft.Azure.Functions.ExtensionBundle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[3.3.0, 4.0.0)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adicionamos a las librerías de requerimientos los siguientes para el diseño de la Skill y consumo de solicitudes&lt;/p&gt;

&lt;p&gt;requirements.txt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requests
ask-sdk-core
ask-sdk-webservice-support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instalaremos las librerías utilizando&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora podemos implementar la lógica de nuestra Skill sobre la función. Para lo cual primero empezaremos por añadir un archivo de configuración, para obtener en formato de variables de entorno los Keys e ID’s para proteger esa información de publicarla libremente.&lt;/p&gt;

&lt;p&gt;config.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;ALEXA_SKILL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ALEXA_SKILL_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;DISCORD_CHANNEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DISCORD_CHANNEL_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;DISCORD_BOT_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DISCORD_BOT_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Estos 3 valores son los que usamos desde los distintos lugares para acceder a nuestros demás servicios.&lt;/p&gt;

&lt;p&gt;ALEXA_SKILL_ID lo podemos obtener desde la consola de Alexa &lt;a href="https://developer.amazon.com/alexa/console/ask"&gt;https://developer.amazon.com/alexa/console/ask&lt;/a&gt;  en la cual podemos copiar nuestro Skill ID&lt;/p&gt;

&lt;p&gt;DISCORD_CHANNEL_ID ya lo habíamos obtenido anteriormente de la creación del canal&lt;/p&gt;

&lt;p&gt;DISCORD_BOT_TOKEN al igual lo obtuvimos con anterioridad al crear el BOT de Discord&lt;/p&gt;

&lt;h4&gt;
  
  
  Obtención de los N mensajes desde Discord
&lt;/h4&gt;

&lt;p&gt;Ahora crearemos el método en python que nos permitirá obtener los “N” últimos mensajes de discord&lt;/p&gt;

&lt;p&gt;discord.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PotatoNoticiasTrigger.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DISCORD_CHANNEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DISCORD_BOT_TOKEN&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_n_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# type: (int) -&amp;gt; List
&lt;/span&gt;    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://discordapp.com/api/channels/{0}/messages?limit={1}"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;DISCORD_CHANNEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Bot {0}'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DISCORD_BOT_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Implementación de las funciones básicas para un Skill en Python
&lt;/h4&gt;

&lt;p&gt;Ahora debemos implementar las funciones básicas de la Skill e incluir todos los Intents, el que vamos a implementar “NoticiasIntent” como los por defecto como de excepciones para poder responder a posibles fallas de ejecución. &lt;/p&gt;

&lt;p&gt;Los Intents son manejados en formato de clase, en el cual definimos de tipo AbstractRequestHandler, un método “can_handle” con el nombre del Intent a manejar en esa clase, y un método “handle” en el cual se realiza la ejecución de la acción para ser devuelta a Alexa a través de la Skill ya sea en voz o en texto visual.&lt;/p&gt;

&lt;h5&gt;
  
  
  Invocación
&lt;/h5&gt;

&lt;p&gt;default_intents.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LaunchRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_request_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LaunchRequest"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ejecucion de la Skill."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech_text&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bienvenido a Potato Noticias, puedes decir últimas noticias"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;set_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SimpleCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Potato Noticias"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;set_should_end_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Ayuda
&lt;/h5&gt;

&lt;p&gt;default_intents.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelpIntentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AMAZON.HelpIntent"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ejecucion Intent Ayuda"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Puedes decir últimas noticias, últimos 2 mensajes"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;set_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SimpleCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Potato Noticias - Ayuda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Cancelar o Detener
&lt;/h5&gt;

&lt;p&gt;default_intents.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CancelAndStopIntentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AMAZON.CancelIntent"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AMAZON.StopIntent"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ejecución Intent Cancel o Stop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hasta luego Atentamente Potato Noticias!"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;set_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SimpleCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Potato Noticias"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;set_should_end_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Terminar Sesión
&lt;/h5&gt;

&lt;p&gt;default_intents.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionEndedRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_request_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SessionEndedRequest"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="c1"&gt;# any cleanup logic goes here
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Manejo de Excepciones
&lt;/h5&gt;

&lt;p&gt;default_intents.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AllExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput, Exception) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput, Exception) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Lo siento, No pude procesarlo. Puedes intentarlo nuevamente o decir Ayuda"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El archivo final default_intents quedaria de la siguiente manera&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ask_sdk_core.dispatch_components&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AbstractExceptionHandler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ask_sdk_core.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;is_request_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ask_sdk_core.handler_input&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HandlerInput&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ask_sdk_model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ask_sdk_model.ui&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleCard&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LaunchRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_request_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LaunchRequest"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ejecucion de la Skill."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bienvenido a Potato Noticias, puedes decir últimas noticias"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;set_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SimpleCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Potato Noticias"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;set_should_end_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelpIntentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AMAZON.HelpIntent"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ejecucion Intent Ayuda"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Puedes decir últimas noticias, últimos 2 mensajes"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;set_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SimpleCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Potato Noticias - Ayuda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CancelAndStopIntentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AMAZON.CancelIntent"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AMAZON.StopIntent"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ejecucion Intent Cancel o Stop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hasta luego Atentamente Potato Noticias!"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;set_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SimpleCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Potato Noticias"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speech_text&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;set_should_end_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionEndedRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_request_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SessionEndedRequest"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="c1"&gt;# any cleanup logic goes here
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AllExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput, Exception) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput, Exception) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;speech&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Lo siento, No pude procesarlo. Puedes intentarlo nuevamente o decir Ayuda"&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speech&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Implementación de la lógica para el Intent de solicitud de N mensajes
&lt;/h4&gt;

&lt;p&gt;Hasta este punto ya hemos implementado los Intents básicos que debe poseer una Skill para facilitar a los usuarios su administración y manejo, ahora nos toca implementar de la misma manera nuestro propio Intent :D&lt;/p&gt;

&lt;p&gt;Primeramente definiremos un generate_reponse que recibe un array de mensajes y los convierte en texto para ser escuchado y texto para ser leído desde un dispositivo.&lt;/p&gt;

&lt;p&gt;noticias_intent.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# type: (List) -&amp;gt; Tuple[str,str]
&lt;/span&gt;    &lt;span class="n"&gt;speak_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;speak_out_visible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;itemCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;speak_out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;'Mensaje {0}: &amp;lt;break time="0.5s"/&amp;gt; {1}. &amp;lt;break time="1s"/&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;itemCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;speak_out_visible&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;'{0}: {1}.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;itemCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;itemCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;speak_out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speak_out_visible&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el caso de speak_out se añade la etiqueta break esto hace referencia al SSML (Speech Synthesis Markup Language), que es un estándar que permite a los asistentes de voz añadir pausas, efectos especiales de voz como susurro, o la pronunciación de palabras en un idioma extranjero de forma correcta&lt;/p&gt;

&lt;p&gt;Después creamos el Handler para nuestro NoticiasIntent, en el cual accederemos a discord y obtendremos los mensajes les daremos un formato para ser escuchados y vistos y sean devueltos hacia el asistente.&lt;/p&gt;

&lt;p&gt;noticias_intent.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NoticiasIntentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; bool
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_intent_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoticiasIntent"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# type: (HandlerInput) -&amp;gt; Response
&lt;/span&gt;        &lt;span class="n"&gt;cantidad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_slot_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cantidad"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cantidad&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cantidad&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_n_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cantidad&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_n_messages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;speak_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"No se encontraron mensajes"&lt;/span&gt;
            &lt;span class="n"&gt;visual_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"No se encontraron mensajes :("&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;speak_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;visual_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;speak_output&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;" Esos son todos los mensajes!."&lt;/span&gt;
            &lt;span class="n"&gt;visual_output&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;" Esos son todos los mensajes!."&lt;/span&gt;
        &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speak_output&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;set_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SimpleCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Potato Noticias"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visual_output&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;set_should_end_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hasta este punto ya tenemos los intents y nos faltaria integrarlo en el init de la función y realizar pruebas :D &lt;/p&gt;

&lt;h4&gt;
  
  
  Integración de los intents en el init de la función
&lt;/h4&gt;

&lt;p&gt;Reemplazamos todo el contenido de nuestro con lo siguiente, que integra tanto nuestros intents  por defecto como el Intent custom para lectura de discord.&lt;/p&gt;

&lt;p&gt;__ init __.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;azure.functions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ask_sdk_core.skill_builder&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SkillBuilder&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ask_sdk_webservice_support.webservice_handler&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WebserviceSkillHandler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PotatoNoticiasTrigger.default_intents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AllExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancelAndStopIntentHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HelpIntentHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LaunchRequestHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SessionEndedRequestHandler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PotatoNoticiasTrigger.noticias_intent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NoticiasIntentHandler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PotatoNoticiasTrigger.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ALEXA_SKILL_ID&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;skill_builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SkillBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skill_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ALEXA_SKILL_ID&lt;/span&gt;
    &lt;span class="c1"&gt;# Default Intents
&lt;/span&gt;    &lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_request_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LaunchRequestHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_request_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelpIntentHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_request_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancelAndStopIntentHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_request_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SessionEndedRequestHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_exception_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AllExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="c1"&gt;# Custom Intents
&lt;/span&gt;    &lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_request_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NoticiasIntentHandler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;webservice_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebserviceSkillHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;skill_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webservice_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_request_and_dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_body&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;mimetype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora podemos probar el Intent y conectarlo a la Skill a través de Ngrok :D&lt;/p&gt;

&lt;p&gt;Primeramente ejecutamos nuestra Función utilizando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;func host start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta llamada nos genera una salida similar en la cual nos muestra la salida de nuestra Función en local similar a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:7071/api/PotatoNoticiasTrigger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora gracias a la herramienta &lt;a href="https://ngrok.com/"&gt;Ngrok&lt;/a&gt;  que nos permite abrir tuneles publicos hacia nuestros Endpoints locales ejecutaremos en otra terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 7071
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Que nos generará una dirección temporal para que conectemos nuestro EndPoint de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Forwarding                    http://2dba-200-87-92-9.ngrok.io -&amp;gt; http://localhost:7071
Forwarding                    https://2dba-200-87-92-9.ngrok.io -&amp;gt; http://localhost:7071
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Utilizando la dirección HTTPS volvemos a la consola de Amazon y nos dirigimos a la parte de Endpoint en Default Region y añadimos nuestra dirección Ngrok más la ruta del Trigger teniendo algo similar a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://2dba-200-87-92-9.ngrok.io/api/PotatoNoticiasTrigger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seleccionamos la opción para el certificado SSL “My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BDS0okNU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odn7ofexduahneyzw9ix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BDS0okNU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odn7ofexduahneyzw9ix.png" alt="Endpoint Skill" width="880" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Guardamos el Endpoint, ahora tenemos que compilar para lo cual nos dirigimos hacia el menú principal y seleccionamos la Opción Build Model para construir el modelo de la Skill.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gA8my7ys--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjif7nadv8z4vt39q71p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gA8my7ys--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjif7nadv8z4vt39q71p.png" alt="Construir Modelo" width="880" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esperamos unos minutos a que termine la construcción del modelo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NnEOxdcc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pu62vc5r5ruanzc2pcnv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NnEOxdcc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pu62vc5r5ruanzc2pcnv.png" alt="Finalizado construcción" width="414" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pruebas de funcionamiento de la Skill integrada con el Endpoint Local&lt;/p&gt;

&lt;p&gt;Desde la pestaña de Test podemos probar nuestra Skill apuntando hacia el Backend local en nuestro equipo. Habilitamos la Skill en modo Development y ya podemos probar&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ornaSi9Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftm4bxtsie6ii7soaxng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ornaSi9Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftm4bxtsie6ii7soaxng.png" alt="Pestaña de prueba Skill" width="880" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zfFpQpQx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jczeq8h1fjelfc8h1e1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zfFpQpQx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jczeq8h1fjelfc8h1e1x.png" alt="Prueba de Ejecución" width="880" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vpugxY_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slwmij29hcuk3ozeukgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vpugxY_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slwmij29hcuk3ozeukgk.png" alt="Prueba Visual" width="880" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Publicación en Azure del Backend de la Skill
&lt;/h4&gt;

&lt;p&gt;Hemos logrado hacer funcionar la Skill y que nos responda desde Local ahora podemos publicarlo en Azure :D y actualizar el EndPoint y volver a compilar nuestro modelo.&lt;br&gt;
Desde VS Code seleccionamos la Opción Deploy to Function App&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pUaV5Zq6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7rmgjg51d4atq0cz2bno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pUaV5Zq6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7rmgjg51d4atq0cz2bno.png" alt="Deploy Function App" width="421" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seleccionamos la Suscripción de Azure que tengamos disponible, para usuarios nuevos pueden habilitar una suscripción de prueba y para estudiantes habilitar una suscripción de tipo estudiantil si se los permite sus cuentas institucionales.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Zc8mhM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/045n15jxe5nfg9d6rgds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Zc8mhM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/045n15jxe5nfg9d6rgds.png" alt="Seleccionar Suscripción" width="654" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seleccionar una nueva función en Azure&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Db0hNitA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c48orm865jjeujmlzyp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Db0hNitA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c48orm865jjeujmlzyp0.png" alt="Seleccionar nueva función" width="642" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ingresamos el  nombre de nuestra  nueva función&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A4-4ZhBs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mgt0i3e67pp1ifrr2pi4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A4-4ZhBs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mgt0i3e67pp1ifrr2pi4.png" alt="Nombre de la función" width="641" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seleccionamos la pila de ejecución que se recomienda que sea coincidente con la que tenemos en local&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--URCSb-zv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qxdae7ab0beq8qwgia1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--URCSb-zv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qxdae7ab0beq8qwgia1.png" alt="Runtime de la función" width="645" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seleccionamos la región donde se aloja nuestra función, en nuestro caso WEST US y se procede a crear nuestra función, esperamos unos minutos mientras se generan los servicios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--omuYC5xy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjvtujq460llbevhtbwz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--omuYC5xy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjvtujq460llbevhtbwz.png" alt="Implementación de la función" width="880" height="206"&gt;&lt;/a&gt;&lt;br&gt;
Tenemos que configurar nuestras variables de entorno con nuestros KEYS y ID Seleccionamos la función y abrimos en Portal&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q7phxRQH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hx22vrp73h0eiqu6iefv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q7phxRQH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hx22vrp73h0eiqu6iefv.png" alt="Open in Portal" width="459" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Desde las configuraciones en Azure Añadimos  3 Configuraciones de Aplicación con el mismo nombre de nuestras variables de entorno&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0JbgBsyP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrifr45d27nvin7pr17w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0JbgBsyP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrifr45d27nvin7pr17w.png" alt="Añadir variables de entorno" width="880" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez adicionado nuestras variables de entorno procedemos a guardar donde se reiniciará la instancia de la función &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HxxMy8b6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3obud00vwmvw4j13pakb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HxxMy8b6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3obud00vwmvw4j13pakb.png" alt="Guardar Variables de Entorno" width="662" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora desde la parte de funciones podemos obtener la URL de nuestro nuevo ENDPOINT similar a&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://potatonoticias.azurewebsites.net/api/PotatoNoticiasTrigger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;el cual actualizaremos en el Endpoint en el portal de Alexa y volveremos a construir el modelo esperemos que termine y podemos probar nuestra Skill&lt;/p&gt;

&lt;p&gt;De esta manera podemos concluir el contenido, consumir un canal de Discord, pasar por una Skill de Alexa  y publicar el backend de nuestra función en Azure, todo un conjunto de elementos integrados para automatizar y permitir a las personas escuchar potato noticias desde cualquier dispositivo con Alexa.&lt;/p&gt;

&lt;p&gt;Muchas gracias!!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Un agradecimiento a la comunidad de Python La Paz por permitirme dar una charla sobre esto, síganlos en sus redes sociales &lt;a href="https://www.facebook.com/PythonLaPaz"&gt;Facebook&lt;/a&gt; y &lt;a href="https://twitter.com/PythonLaPaz"&gt;Twitter&lt;/a&gt; y pregunten por su servidor de Discord, para conocer más sobre futuras charlas y eventos, también pueden ser expositores y enseñarnos su conocimiento para crecer con esta bonita comunidad.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  El repositorio público en Github lo pueden encontrar aqui del proyecto &lt;a href="https://github.com/jevillanueva/PotatoNoticias"&gt;Potato Noticias&lt;/a&gt;
&lt;/h5&gt;

&lt;h5&gt;
  
  
  La presentación se encuentra disponible en el siguiente enlace &lt;a href="https://docs.google.com/presentation/d/18hzMH73g16cx4x7exOMzi_ZGhM48Q19TW5DmXUywgik/edit?usp=sharing"&gt;Presentación&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Algunos enlaces en cuanto a contenido sobre todo lo expuesto.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bibliografías
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.redhat.com/es/topics/automation"&gt;https://www.redhat.com/es/topics/automation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://blog.grupocajamar.com/que-son-los-asistentes-virtuales-inteligentes/"&gt;https://blog.grupocajamar.com/que-son-los-asistentes-virtuales-inteligentes/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.amazon.com/es-ES/alexa"&gt;https://developer.amazon.com/es-ES/alexa&lt;/a&gt;&lt;br&gt;
&lt;a href="https://alexaecho.es/las-mejores-ideas-de-rutina-y-habilidades-de-alexa-para-automatizar-tu-vida/"&gt;https://alexaecho.es/las-mejores-ideas-de-rutina-y-habilidades-de-alexa-para-automatizar-tu-vida/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://serverless-stack.com/chapters/es/what-is-serverless.html"&gt;https://serverless-stack.com/chapters/es/what-is-serverless.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://es.wikipedia.org/wiki/Serverless_computing"&gt;https://es.wikipedia.org/wiki/Serverless_computing&lt;/a&gt;&lt;br&gt;
&lt;a href="https://iamondemand.com/blog/aws-lambda-vs-azure-functions-ten-major-differences"&gt;https://iamondemand.com/blog/aws-lambda-vs-azure-functions-ten-major-differences&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>azure</category>
      <category>discord</category>
      <category>alexa</category>
    </item>
  </channel>
</rss>
