Oracle Cloud Infrastructure Functions en Go y uso del SDK de OCI Go para acceder a los servicios de OCI desde Go

Esta es la tercera entrega de una serie parcial sobre Go y Oracle Cloud Infrastructure. En esta serie se describe cómo las aplicaciones Go se pueden crear y ejecutar en Oracle Cloud Infrastructure (OCI) en instancias informáticas (VM), en contenedores en Kubernetes o como funciones sin servidor. Los artículos muestran cómo automatizar la creación y el despliegue de estas aplicaciones Go mediante OCI DevOps. Un tema importante es cómo utilizar los servicios de OCI desde aplicaciones Go, tanto las que se ejecutan en OCI como el código Go que se ejecutan en otro lugar. Algunos de los servicios de OCI tratados son Object Storage, Streaming, Key Vault y Autonomous Database.

Con el fin de seguir junto con estos artículos, los lectores deben tener al menos conocimientos básicos de cómo crear aplicaciones Go. Se supone que los lectores tienen acceso a su propio entorno de desarrollo Go. Algunos de los ejemplos y capturas de pantalla mencionarán específicamente VS Code como herramienta de desarrollo. Sin embargo, también se pueden utilizar otros editores e IDE. El código Go presentado en estos artículos demuestra una serie de mecanismos en su forma más simple para una máxima claridad y con las menos dependencias. Los lectores no deben esperar una funcionalidad significativa o un código listo para la producción.

En los artículos se describe cómo empezar a utilizar OCI. Para probar los ejemplos, los lectores deberán tener acceso a un arrendamiento de OCI con permisos para crear los recursos de OCI que se tratan en estos artículos. La mayoría de los recursos utilizados están disponibles en la capa gratuita Siempre (instancia informática, VCN, Autonomous Database, Object Storage, Logging, Resource Manager) o tienen un nivel de asignación gratuita para un uso mensual limitado (Functions, API Gateway, Streaming, Vault, DevOps).

En la primera parte de esta serie se describe el aprovisionamiento de una instancia informática basada en la imagen de Oracle Linux Cloud Developer, su apertura para la actividad de red entrante y saliente, la creación y ejecución de una aplicación Go que atienda solicitudes HTTP y la conexión del registro producido por la aplicación a OCI Logging. La segunda parte se ocupa de la ingeniería de software, la automatización de la creación y el despliegue de la aplicación con el servicio OCI DevOps. Este servicio se utiliza para almacenar el código fuente de Go, crear el ejecutable de la aplicación, almacenarlo como artefacto desplegable y desplegar ese artefacto en una instancia informática. En el segundo artículo también se muestra cómo exponer un punto final HTTP para la aplicación a través de un gateway de API de OCI.

En esta tercera parte se muestra cómo crear funciones sin servidor en Go y desplegarlas en OCI. Se introduce el SDK de Go para OCI, primero para aplicaciones Go locales independientes y, posteriormente, para su uso desde funciones, aprovechando la autenticación de entidad de recurso. Este SDK se utiliza para interactuar con el servicio OCI Object Storage para crear cubos y escribir y leer archivos.

Inicialmente, la función se crea y despliega manualmente. Se agrega una ruta al despliegue en API Gateway para llamar a la función desde un cliente externo a OCI. A continuación, se crea un pipeline de despliegue DevOps de OCI para desplegar la función desde una imagen en Container Image Registry. Por último, se configura un pipeline de compilación para que tome orígenes en el repositorio de código, cree y publique una imagen de contenedor y, a continuación, dispare el pipeline de despliegue para la compilación y el despliegue completos.

Funciones de OCI Go

La función sin servidor en OCI se basa en la tecnología del Project Fn de código abierto. La lógica de negocio de la función está escrita en su idioma favorito, en este caso Go, e integrada en el marco de Fn que maneja el ciclo de vida de la función y las interacciones con la función. El marco de Fn se puede ejecutar en cualquier lugar: en su máquina local, en una máquina virtual en cualquier nube o local. Oracle Cloud Infrastructure ofrece un servicio PaaS totalmente gestionado OCI Functions para funciones sin servidor basadas en esa misma tecnología.

Una función está integrada en una imagen de contenedor. Esta imagen se envía a un registro de imágenes de contenedor. Para publicar la función, esta imagen se transfiere a un servidor Fn. Cada vez que se llama a la función, se inicia un contenedor desde la imagen y se procesa la solicitud. Los contenedores se mantienen en ejecución durante algún tiempo después de manejar una llamada, en un estado activo listo para manejar solicitudes adicionales. Cuando aumenta el número de solicitudes simultáneas, el servidor de Fn iniciará contenedores adicionales para la misma función para garantizar que se puedan manejar todas las solicitudes.

El atractivo de las funciones para los desarrolladores y operadores de aplicaciones es el hecho de que no es necesario gastar energía en el diseño, la creación y la gestión de la plataforma que ejecuta la lógica de negocio. Todo el enfoque puede estar en escribir esa lógica.

Ahora veremos cómo crear la función en Go, crearla en una imagen de contenedor, desplegarla y ejecutarla localmente. A continuación, llevaremos esta función al servicio OCI Functions y haremos que se ejecute en la nube.

Entorno de desarrollo de Fn

Para desarrollar funciones, necesitará un entorno que soporte Project Fn. Fn es una plataforma ligera de funciones sin servidor basada en Docker que puede ejecutar en su portátil, servidor o nube. Puede instalar Fn fácilmente en Linux o MacOS siguiendo las instrucciones de Fn Project Tutorials – Install Fn.

Puede optar por trabajar en la instancia informática go-app-vm que hemos creado en la primera y que también se utiliza en la segunda parte de esta serie. Este entorno de Oracle Linux no viene con la configuración de Fn, pero su instalación es bastante sencilla.

También puede trabajar en OCI Cloud Shell. Este entorno accesible para el explorador se configura con Fn. Para trabajar con Fn en OCI Cloud Shell, consulte Funciones de documentación de OCI: introducción al uso de Cloud Shell.

Desarrollo de una función Fn

En el entorno de desarrollo con la CLI de Fn instalada, navegue hasta el directorio en el que desea crear el subdirectorio de la función. En la línea de comandos, introduzca este comando y ejecútelo:

fn init --runtime go --trigger http greeter

Se crea un subdirectorio denominado greeter. Navegue hasta él y compruebe su contenido:

cd greeter ls -l

El archivo func.yaml contiene los metadatos sobre la función, que debe interpretar la estructura de Fn al crear y, posteriormente, al ejecutar la función. El archivo go.mod contiene la dependencia que tiene la función en el paquete fdk-go. La función real en sí está en func.go. La estructura de la función y su interacción con el tiempo de ejecución de Fn se pueden ver aquí: la función principal registra la función myHandler con el tiempo de ejecución de Fn, que indica y permite al tiempo de ejecución llamar a esta función para cualquier solicitud HTTP recibida. La función recibe el cuerpo de la solicitud en un parámetro io.Reader. También recibe la salida de io.Writer, en la que se puede escribir el cuerpo de respuesta. El parámetro ctx context.Context contiene metadatos para la solicitud original, incluidas las cabeceras HTTP, la URL completa, el método de solicitud y la función en sí, incluidas todas las variables de configuración definidas para ella.

Actualmente, la función myHandler decodifica el cuerpo de la solicitud, esperando que contenga una carga útil de JSON con un campo denominado nombre. Crea una Persona con su nombre definido en el valor de este campo o, en su ausencia, se define por defecto en Mundo. A continuación, crea la respuesta esperada: un objeto JSON con un único campo llamado mensaje, que contiene una cadena compuesta de Hello y el valor de nombre.

Aunque no hace nada realmente espectacular, la función es sólida y completa y podemos desplegarla e invocarla localmente. Para ello, necesitamos un contexto local y el servidor Fn local en funcionamiento. Compruebe los contextos mediante:

fn list contexts

Esto muestra una lista de al menos un contexto, posiblemente más de uno. Para trabajar con el servidor Fn local, asegúrese de que el contexto predeterminado sea el activo. Si es necesario definir el contexto actual como predeterminado, utilice:

fn use context default

Ahora cree una aplicación como host para la función:

fn create app go-oci-app fn list apps

Si la primera de estas sentencias falla con la conexión rechazada, es probable que el servidor aún no se esté ejecutando. Utilice el siguiente comando para iniciar el servidor y, a continuación, vuelva a intentarlo para crear la aplicación.

fn start

Con la aplicación creada correctamente, la función ahora se puede desplegar en ella. El siguiente comando se encarga de este despliegue; está precedido por el proceso de creación de imágenes de contenedor.

fn --verbose deploy --app go-oci-app --local

Especificar --local realiza el despliegue en el servidor local, pero no transfiere la imagen de función a un registro de Docker, lo que sería necesario si estuviéramos realizando el despliegue en un servidor Fn remoto.

Debido a que desata una cantidad impresionante de mensajes de log que se producirán, el indicador --verbose no es uno que usará todo el tiempo. Sin embargo, te da una idea bastante buena de lo que está pasando. Se extraen varias imágenes de contenedor y, a continuación, se ejecuta un proceso de creación de contenedores de dos etapas para crear una imagen de contenedor para la función greeter. Las imágenes de proyecto de Fn predefinidas se utilizan para la etapa de compilación (fnproject/go:1.15-dev en el momento de la escritura) y como base para la imagen de tiempo de ejecución (fnproject/go:1.15).

El resultado final será el siguiente:

Updating function greeter using image greeter:0.0.2... Successfully created function: greeter with greeter:0.0.2 Successfully created trigger: greeter Trigger Endpoint: http://localhost:8080/t/go-oci-app/greeter

La imagen de función se denomina greeter:0.0.2. Encontrará esta imagen en el registro de contenedores local con:

docker images | grep greeter

La función se puede llamar a través de la CLI de Fn, utilizando su nombre y aplicación, de la siguiente manera:

fn invoke go-oci-app greeter

La función espera una carga útil de JSON que contenga un campo de nombre, así que proporcionemos exactamente eso:

echo -n '{"name":"Clark Kent"}' | fn invoke go-oci-app greeter --content-type application/json

La salida del despliegue de función también nos ha proporcionado el punto final del disparador para la función. Se trata de un punto final HTTP al que podemos enviar una solicitud HTTP y hacer que dispare la función. No tenemos ninguna interacción (visible) con Fn, aunque el punto final al que llamamos es realmente el punto final de Fn Server. La ruta URL indica a Fn que se dispare la aplicación y la función específica.

curl -X "POST" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' http://localhost:8080/t/go-oci-app/greeter

Crear función de OCI

Ahora, vamos a crear esta función en OCI en lugar de solo en nuestro entorno de desarrollo. Los pasos son muy similares a los que usamos para crear la función localmente; solo necesitamos usar un contexto diferente. No es el contexto local, sino uno para OCI.

Crear aplicación

Primero, vamos a crear la aplicación a través de la consola de OCI. Escriba la aplicación en el cuadro de búsqueda y haga clic en Funciones de aplicaciones en el área de servicios.

Haga clic en el botón Crear aplicación. Escriba el nombre de la aplicación: go-on-oci-app. Seleccione la VCN que se creó en la primera parte de la serie de artículos y su única subred pública. A continuación, haz clic en Crear para crear la aplicación.

way-to-go-on-oci-artícula

Preparación del entorno local para OCI Interaction and Function Image Push

Una vez creada la solicitud, se presenta la información general de la solicitud. La página también contiene instrucciones para crear la primera función, ya sea en OCI Cloud Shell o en una configuración local (que, por supuesto, también podría ser la instancia informática go-app-vm).

way-to-go-on-oci-artícula

Si utiliza OCI Cloud Shell, los pasos para crear este contexto son ligeramente diferentes (y más simples) que cuando trabaja en un entorno de desarrollo normal. No dude en seguir la configuración de OCI Shell. En este artículo, tomaremos el otro camino, utilizado para cualquier entorno de desarrollo local.

Hay una serie de pasos que se deben realizar en un entorno local (en el que se instaló anteriormente la CLI de Fn):

  1. Configure una clave de firma de API y almacene la clave privada en un archivo .pem en el directorio local HOME/.OCI y cargue la clave pública en la consola de OCI. Consulte las instrucciones en Documentación de OCI - Claves necesarias.
  2. Cree la configuración del archivo en el directorio .oci del entorno local; copie el fragmento de vista previa del archivo de configuración en el archivo de configuración. Actualice la entrada key_file en el archivo: haga referencia al archivo pem de la clave privada. Documentación de OCI: archivo de configuración de SDK y CLI.
  3. Para transferir imágenes de contenedor a OCI Container Image Registry, necesita un token de autenticación. En la primera parte de esta serie de artículos, ha creado un token para conectarse al repositorio de código DevOps desde el cliente de git. Puede reutilizar ese token para registrar el cliente de Docker en el registro de imágenes de contenedor o puede crear un nuevo token de autenticación. En el último caso, consulte Documentación de OCI – Obtención de un token de autenticación.
  4. Necesitará la CLI de OCI. Las instrucciones para instalar esta herramienta se encuentran en la documentación de OCI: Working with the OCI Command Line Interface – Quickstart. La CLI de OCI utilizará el archivo HOME/.oci/config y la clave privada a la que se hace referencia para realizar conexiones seguras a las API de OCI.

Después de estos pasos, puede probar el éxito de los pasos 1, 2 y 4 con este comando, que debe devolver una lista de los compartimentos de su arrendamiento:

oci iam compartment list

Opcional: crear repositorio de Container Image Registry

Si la cuenta de usuario utilizada para desplegar la función tiene los permisos de IAM necesarios, el despliegue creará el repositorio para las imágenes de función en Container Image Registry. Si esos privilegios no están disponibles o desea preparar el repositorio, puede hacerlo de la siguiente manera.

  1. Escriba regi en la barra de búsqueda. Haga clic en el enlace Container Registry > Containers & Artifacts.
  2. Haga clic en Create repository. Escriba el nombre del repositorio: go-on-oci/greeter. Se compone del prefijo del repositorio y el nombre de la función, en el que el repositorio contendrá las imágenes. Defina Access en Public.
  3. Haga clic en el botón Crear repositorio. Después de unos segundos, se crea un repositorio de imágenes de contenedor nuevo y vacío, listo para recibir las imágenes de función (contenedor) que enviaremos mediante la CLI de Fn.
way-to-go-on-oci-artícula

Creación de un contexto para OCI en la CLI de Fn

Al volver a la línea de comandos del entorno local, necesitamos crear un contexto de Fn para el compartimento actual en OCI y, posteriormente, seleccionarlo para su uso en operaciones de Fn. Ejecute estos comandos (que puede copiar desde el separador Introducción de la página go-on-oci-app):

fn create context go-on-oci --provider oracle fn use context go-on-oci
way-to-go-on-oci-artícula

Copie los comandos del paso 4 para actualizar el contexto con el OCID del compartimento y la URL de API de Oracle Functions. En mi caso:

fn update context oracle.compartment-id ocid1.compartment.oc1..aaaaaaaaqb4vxvxuho5h7eewd3fl6dmlh4xg5qaqmtlcmzjtpxszfc7nzbyq fn update context api-url https://functions.us-ashburn-1.oraclecloud.com

El comando será similar pero diferente para usted.

Proporcione el prefijo de nombre de repositorio único. Utilice go-on-oci y especifique el compartimento que contiene el repositorio de registro de imágenes en el que se debe publicar la imagen de función:

fn update context registry iad.ocir.io/idtwlqf2hanz/go-on-oci fn update context oracle.image-compartment-id <compartment-ocid> </compartment-ocid>

Conéctese al registro utilizando el token de autenticación como contraseña:

docker login iad.ocir.io

En mi caso, la región en la que trabajo es Ashburn, identificada por la clave de región iad.ocir.io. Se me solicita el nombre de usuario. Cadena que incluye el prefijo de espacio de nombres incluido en el nombre de Container Image Registry y cada repositorio. Posteriormente se solicita la contraseña. Aquí, usted proporciona un token de autenticación configurado para el usuario, que utilizamos antes en el artículo anterior cuando se realizó el inicio de sesión en el repositorio de código.

El siguiente comando muestra una lista de las aplicaciones en el contexto de Fn actual:

fn list apps

La lista contiene una aplicación, denominada go-on-oci-app.

El organizador de funciones creado, desplegado localmente y llamado anteriormente también se puede desplegar en la aplicación OCI para convertirse en una función sin servidor nativa de la nube. El comando que utilizamos para el despliegue es el mismo que antes. Su efecto es dramáticamente diferente debido al cambio de contexto. En lugar de un contexto local, ahora hay un contexto basado en un proveedor de OCI y enlazado a un arrendamiento y compartimento de OCI. La imagen de contenedor se transfiere a OCI Container Image Registry y la función se crea en el servicio OCI Function.

fn -v deploy --app go-on-oci-app

La salida es similar a la generada anteriormente, pero el proceso de creación es exactamente el mismo. Una vez que la imagen del contenedor de funciones está lista, las cosas comienzan a desviarse. La imagen se transfiere a OCI Container Image Registry y la función se despliega en la nube. Las líneas relacionadas en la salida:

=> exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:008dc3b990f1e69d67a7dd8649fbd63649d72f0bf1a161b2c2e073064f16c918 0.0s => => naming to iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 0.0s Parts: [iad.ocir.io idtwlqf2hanz go-on-oci greeter:0.0.3] Using Container engine docker to push Pushing iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 to docker registry...The push refers to repository [iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter] ... e57f007acf74: Pushed 0.0.3: digest: sha256:bb4f2abde44d97517520571a21c407e349ddfc6572583a8ba53717436fd0b7f5 size: 1155 Updating function greeter using image iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3... Successfully created function: greeter with iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 Fn: HTTP Triggers are not supported on Oracle Functions

En este punto, la función está en la nube y se puede llamar (aún utilizando la CLI de Fn):

fn invoke go-on-oci-app greeter

La primera llamada tardará bastante tiempo porque la función se inicia en frío y la imagen de contenedor subyacente debe instanciarse en un contenedor en ejecución. Cada invocación posterior de la función será mucho más rápida. Tenga en cuenta que si espera diez minutos y la función se pone en frío, el contenedor se detiene.

Esta imagen describe la situación a la que hemos llegado:

way-to-go-on-oci-artícula

Puede consultar la consola de OCI para obtener pruebas de lo que acaba de suceder. Escriba greeter en el cuadro de búsqueda de la consola. En Resources habrá una entrada >greeter > Functions>. Haga clic en el enlace para ir a la página que muestra los detalles de la función. Encontrará referencias a la imagen de función, la configuración de memoria y el punto final para llamar a la función. En las métricas, debe encontrar pruebas de la llamada a la función realizada mediante la CLI de Fn.

En los resultados de búsqueda de greeter, también encontrará el repositorio de contenedores go-on-oci/greeter. Al navegar al repositorio, encontrará detalles de las imágenes publicadas en él.

Crear ruta de gateway de API para función

No solo se puede llamar a OCI Functions. A pesar de que tienen un punto final HTTP que parece sugerir que puede llamarlos desde su navegador o rizar en la línea de comandos, en realidad no es tan simple. Las llamadas HTTP a funciones deben firmarse, y este proceso de firma no es simple y directo.

Una mejor forma de permitir a los consumidores llamar a funciones es a través de un gateway de API. Hemos utilizado un gateway de API en el artículo anterior para abrir una ruta pública a la aplicación myserver que se ejecuta en una instancia informática (potencialmente) privada. Ahora haremos lo mismo con la función greeter utilizando una ruta adicional en la API Gateway the-API-gateway y el despliegue myserver-API creado en el artículo anterior.

way-to-go-on-oci-artícula

Configuración del acceso de IAM para API Gateway

Se debe permitir que API Gateway llame a la función mediante una política que proporcione permiso para que API Gateway llame a funciones.

Cree la política para API Gateway para llamar a funciones. Para crear una política en la consola: escriba poli en la barra de búsqueda y haga clic en >Políticas > Identidad> en el área Servicios de la ventana emergente de resultados de búsqueda. Accederá a la página de visión general Políticas del compartimento actual.

La política define el permiso para que los gateways de API accedan a los recursos del compartimento. Cree una nueva política, escriba un nombre (invoke-function-for-api-gateway), una descripción y la siguiente sentencia:

ALLOW any-user to use functions-family in compartment <compartment-name> where ALL {request.principal.type= 'ApiGateway', request.resource.compartment.id = '<compartment-id></compartment-id>'}</compartment-name>

Sustituya por el nombre del compartimento, que probablemente sea go-on-oci. Sustituya por el identificador del compartimento en el que está trabajando.

Definición de la ruta para la función en el despliegue en API Gateway

Con los permisos cuidados, ahora podemos definir la ruta en API Gateway. Escriba gat en la barra de búsqueda de la consola. Haga clic en Gateways > Gestión de API. Haga clic en el enlace para *the-api-gateway. Haga clic en Deployments. En la lista de despliegues (que contiene un único despliegue), haga clic en el enlace myserver-api.

Haga clic en el botón Edit para abrir la especificación de despliegue. Haga clic en el enlace para el segundo paso: Rutas. Desplácese hacia abajo y haga clic en el botón + Otra ruta.

Escriba /greeting como la ruta para esta nueva ruta. Seleccione GET como método y Oracle Functions como tipo (de backend). Seleccione application go-on-oci-app y, a continuación, defina Function Name en greeter.

way-to-go-on-oci-artícula

Haga clic en Siguiente. A continuación, pulse Guardar cambios para aplicar los cambios y hacer que la nueva ruta sea real.

Llamada a la función a través de API Gateway

Con la nueva configuración de ruta y el despliegue refrescado en API Gateway, ahora podemos realizar una solicitud HTTP simple y directa al punto final público de API Gateway, disparando indirectamente la función de recepción y recibiendo su respuesta.

Con esta URL en cualquier explorador, debería poder obtener la respuesta de la función:

https://<url for="" api="" gateway="">/my-api/greeting</url>

La respuesta es un poco decepcionante, pero eso se espera con una función tan simplista.

Con curl, puede enviar una carga útil de JSON a la función y recibir una respuesta un poco más interesante.

curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<url for="" api="" gateway="">/my-api/greeting</url>

La respuesta dice {"message":"Hello Mickey Mouse"}.

Por lo tanto, ahora hemos establecido el flujo de extremo a extremo desde API Gateway hasta la función sin servidor. Y tenemos una manera de desplegar manualmente la función en función de las fuentes en nuestro entorno de desarrollo local. Para aprovechar nuestro trabajo, puede realizar algunos cambios en el código fuente en func.go y, a continuación, desplegar la función una vez más, un único comando con la CLI de Fn, y llamar a la ruta de saludo en API Gateway para ver que nuestro cambio está activo.

Por ejemplo: cambie la línea que define el valor de Msg a

Msg: fmt.Sprintf("Warmest greetings from your function dear %s", p.Name)

Guarde el origen func.go actualizado. A continuación, ejecute estos comandos para desplegar la función actualizada y, posteriormente, llamarla:

fn -v deploy --app go-on-oci-app
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<url for="" api="" gateway="">/my-api/greeting
    </url>

Esto debería dar lugar a una mejor respuesta. El proceso de creación y despliegue se condensa en un único comando manual en un entorno preparado. A continuación, examinaremos un proceso de despliegue automatizado para las funciones que utilizan OCI DevOps, seguido del proceso de creación automatizada anterior basado en el origen en un repositorio de código. Luego pasaremos a funciones que hacen un poco más que devolver saludos simples.

Despliegue automatizado de funciones

En la instalación anterior de esta serie, vimos el uso de los pipelines de despliegue de OCI DevOps para desplegar una aplicación en una instancia informática. Ahora utilizaremos un pipeline para el despliegue automatizado de una función. El enfoque general y los ingredientes son similares. Necesitamos un artefacto, un entorno (de destino) y el pipeline de despliegue con una etapa de despliegue de función, así como permisos de IAM para que el pipeline lea el artefacto y despliegue la función.

way-to-go-on-oci-artícula

Estos ingredientes con más detalle:

  1. Artefacto: referencia a, en este caso, una imagen de contenedor específica en OCI Container Image Registry, mediante la ruta de acceso completa al repositorio y la imagen y versión específicas.
  2. Un entorno: la referencia a la función que quiero (re)desplegar. El entorno en el caso del despliegue de función no es el compartimento o una aplicación en un compartimento (como se podría suponer), sino la función en sí que, por lo tanto, ya debe existir para que se pueda desplegar a través de un pipeline de despliegue DevOps de OCI. (Tenga en cuenta que la función no tiene que ser útil, ya que se puede basar en la imagen del contenedor de reutilización).
  3. Un pipeline de despliegue con una etapa de pipeline de despliegue del tipo Despliegue de función que conecta el artefacto y el entorno.
  4. Grupo dinámico que contiene el pipeline de despliegue y políticas de IAM que permiten al grupo dinámico leer artefactos (como imágenes de contenedor de funciones) y desplegar funciones (en términos generales, gestionar recursos de OCI).

Crear artefacto DevOps para la imagen de contenedor de función

En la consola de OCI, vaya a la página inicial de DevOps Project go-on-OCI. Abra el separador Artifacts. Haga clic en el botón Agregar artefacto. Tenga en cuenta que lo que definimos aquí es un enlace o un proxy del proyecto DevOps a un artefacto real, no al artefacto en sí.

Introduzca greeter-function como el nombre del artefacto DevOps. El tipo se debe definir en el repositorio de imágenes de contenedor. La ruta de acceso completa a la imagen consta de la clave de región, el prefijo y el espacio de nombres del repositorio, el nombre de la función y la etiqueta de versión de la función. En este caso, utilice un marcador de posición para la etiqueta version. La ruta se define ahora de la siguiente manera:

<region key="">/<namespace>/<repository prefix="">/greeter:${imageVersion}</repository></namespace></region>

Defina el campo desplegable Sustituir parámetros utilizados en este artefacto en Sí, sustituya los marcadores de posición.

way-to-go-on-oci-artícula

Haga clic en el botón Agregar para completar y guardar la definición del artefacto.

Definir el entorno DevOps para la función

Abra el separador Entornos en el proyecto DevOps. Contiene el entorno go-on-oci-vm creado para el despliegue de myserver en la instancia de Compute (en el artículo anterior). Haga clic en el botón Crear entorno.

En el primer paso, Información básica, haga clic en el mosaico Funciones - Crear un entorno para una función. Introduzca greeter-function-in-app-go-on-oci-app como nombre del entorno. Pulse Siguiente para ir al segundo paso con los detalles del entorno. Confirme la región, el compartimento, la aplicación y la función. Probablemente no tenga que cambiar ninguno de estos valores. Si lo hace, asegúrese de que la función greeter en la aplicación go-on-oci-app está seleccionada.

Haga clic en Crear entorno para guardar la definición.

way-to-go-on-oci-artícula

Creación del pipeline de despliegue para desplegar la función

En la página de visión general del proyecto DevOps, haga clic en Crear pipeline. Se presenta el formulario Crear pipeline. Escriba un nombre (deploy-greeter-function-to-go-on-oci-app) y, opcionalmente, una descripción. A continuación, haz clic en Crear pipeline. El pipeline de despliegue se crea, aunque está bastante vacío: no es un entorno en el que se debe desplegar, no hay artefactos que se vayan a desplegar y no hay ningún archivo de configuración para definir los pasos que se van a ejecutar.

En el editor de pipeline que aparece, haga clic en el mosaico Agregar etapa (o en el icono más). La siguiente página muestra una lista de tipos de etapa. Haga clic en el mosaico con la etiqueta Utiliza la estrategia de actualización de funciones incorporada.

Presione el botón Siguiente.

Escriba el nombre de la etapa, por ejemplo, update-function-greeter. Seleccione el entorno definido anteriormente para la función: greeter-function-in-app-go-on-oci-app.

En la cabecera Artifact, haga clic en Select Artifact. Se presenta una lista de todos los artefactos del proyecto DevOps de tipo Imagen de Docker. Seleccione la única entrada, la que se creó anteriormente para Function Container Image.

Tenga en cuenta que el botón Seleccionar artefacto ya no está activado: solo se puede asociar una sola imagen de contenedor a esta etapa.

way-to-go-on-oci-artícula

Haga clic en Agregar. La etapa de pipeline se crea en el pipeline. Y el pipeline ahora está listo para ejecutarse: su definición está completa. ¿O lo es? El artefacto al que utiliza este pipeline no está definido de forma inequívoca: la etiqueta de versión de la ruta de acceso para la imagen de contenedor contiene el marcador de posición ${imageVersion}. Para garantizar que se utiliza la versión adecuada para el despliegue, este marcador de posición se debe sustituir por el valor correcto. Y esto se organiza definiendo un parámetro en el pipeline que se denomina imageVersion y se define como una etiqueta de versión existente.

Haga clic en el separador Parámetros del pipeline. Defina un nuevo parámetro denominado imageVersion. Su valor por defecto puede ser cualquier cosa, pero también puede corresponder a una etiqueta de versión existente para la imagen de contenedor de función greeter. Guarde la definición del parámetro.

Parece que el pipeline está listo para ejecutarse, pero aún tenemos que asegurarnos de que se le permita hacer su trabajo. Antes de probar cualquier erupción, lea la siguiente sección.

Grupo dinámico y políticas

En el artículo anterior, se definió un grupo dinámico para todos los pipelines de despliegue del compartimento. El nuevo pipeline es automáticamente miembro de ese grupo. También definimos una política que otorgaba permisos al grupo dinámico para leer todos los artefactos, que incluye imágenes de contenedor (función) en los repositorios de Container Image Registry del compartimento. Otra política que también se ha creado otorga al grupo dinámico el permiso muy amplio para gestionar todos los recursos del compartimento. Podemos beneficiarnos del amplio alcance de esa política, ya que también abarca la creación y actualización de funciones.

Ejecución del pipeline de despliegue

Ejecute el pipeline de despliegue pulsando Ejecutar pipeline.

Una vez completado el despliegue, verá los marcadores verdes que proclaman el éxito. Sin embargo, no hay ninguna otra indicación obvia de este éxito porque el resultado final es exactamente la situación que habíamos logrado con el despliegue manual de la función desde la línea de comandos de la CLI de Fn.

way-to-go-on-oci-artícula

Para hacer las cosas un poco más interesantes, haremos un cambio en el código de la función. A continuación, cree la imagen de contenedor para la función (localmente) y transfiera la nueva imagen de función al registro de imágenes de contenedor. A continuación, iniciaremos el pipeline de despliegue de nuevo; esta vez, cuando tenga éxito, representará una nueva situación que podemos experimentar llamando a la ruta my-API/greeting en API Gateway.

Implantación de función de cambio

Realice un cambio pequeño pero visible a func.go en el entorno local: asegúrese de que la respuesta de la nueva versión de la función es notablemente diferente de la versión actual. Guarde el cambio.

En las siguientes secciones, crearemos una nueva versión de la imagen de contenedor de funciones a partir del origen cambiado y, finalmente, la ejecutaremos en OCI Functions.

Creación de una nueva imagen de contenedor de funciones (localmente)

Estos siguientes comandos modificarán primero la etiqueta de versión utilizada para etiquetar la función con un aumento en el tercer dígito (bm es abreviado para bump). A continuación, la imagen del contenedor de funciones se crea utilizando los orígenes modificados. El tercer comando muestra las imágenes del contenedor local, filtrando por imágenes con el nombre del receptor. Ahora, ejecute los comandos.

fn bm
fn build 
docker images | grep greeter

Debería poder encontrar la imagen recién creada con su nombre totalmente cualificado, incluida la clave de región de OCI, el espacio de nombres, el prefijo del repositorio y el nombre de función greeter, con la etiqueta de versión agregada.

Etiqueta de imagen de contenedor con etiqueta de nueva versión y transferencia al registro

Vamos a definir un nuevo identificador para la imagen, utilizando este comando que establece la etiqueta de versión en 0.1.0:

docker tag <fully qualified="" image="" name="">:<newly assigned="" bumped="" version="" label="">  <fully qualified="" image="" name="">:0.1.0

    </fully></newly></fully>

A continuación, transfiera la nueva imagen de contenedor de funciones al repositorio de OCI Container Image Registry mediante:

docker push <fully qualified="" image="" name="">:0.1.0

    </fully>

Tenga en cuenta que en este punto no hemos vuelto a desplegar la función en función de esta nueva versión de la imagen de contenedor. Todo lo que hicimos fue crear la imagen y transferirla al registro en OCI. La llamada a la función OCI no mostrará ninguna diferencia.

Ejecutar el pipeline de despliegue (para la nueva imagen de función)

Ejecutar el pipeline de despliegue una vez más. Defina el valor del parámetro imageVersion en 0.1.0.

Cuando el pipeline se completa con éxito, la nueva versión de la función con todos los emocionantes cambios que aplicó está activa.

Llamada a la función recién desplegada

Puede ver la nueva versión de la función en acción invocándola en la línea de comandos mediante la CLI de Fn:

fn invoke go-on-oci-app greeter

(Debido a que el contexto de la CLI de Fn sigue siendo go-on-OCI del proveedor de Oracle y está configurado para el compartimento go-on-OCI que contiene la función greeter, esta llamada se dirigirá a la función OCI, que en este punto se basa en una nueva versión de la imagen de contenedor).

También puede acurrucarse a la ruta en API Gateway que llama a la función:

curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<api gateway="" endpoint="">/my-api/greeting

    </api>

Creación automatizada de funciones

Hasta ahora, hemos creado la imagen del contenedor de funciones manualmente en el entorno de desarrollo local mediante la CLI de Fn. Sin embargo, al igual que hicimos en el artículo anterior para la aplicación Go que fue producida como ejecutable por un pipeline de compilación, ahora convertiremos la construcción de la función en un proceso automatizado. Se crea un pipeline de compilación DevOps de OCI para obtener orígenes del repositorio de código, ejecutar una etapa de compilación gestionada que produzca una imagen de contenedor local y, a continuación, publicar esta imagen como artefacto en el repositorio de Container Image Registry. Como último paso, el pipeline de compilación dispara el pipeline de despliegue para publicar la última definición de la función en el entorno de tiempo de ejecución de OCI Functions.

Cuando todos los elementos están en su lugar, el conjunto total interconectado de componentes de OCI se ve como se visualiza en la siguiente figura.

way-to-go-on-oci-artícula

El artefacto y el pipeline de despliegue que se muestran en esta figura ya están definidos en el proyecto DevOps, al igual que el repositorio de Application, Function y Container Image Registry para las imágenes de la función. Utilizaremos el repositorio de código configurado en el artículo anterior. Todo lo que necesitamos crear es la función de nivel de creación del pipeline de compilación con sus tres etapas.

Creación del pipeline de compilación

En la página de visión general de DevOps Project go-on-oci, haga clic en el botón Crear pipeline de compilación. Se presenta una página para especificar el nombre (por ejemplo, función build-greeter) y una descripción. Pulse Crear para que se agregue el pipeline de compilación al proyecto DevOps.

Haga clic en el enlace build-greeter-function de la lista para navegar a la página de detalles.

Primera etapa: compilación gestionada

La primera etapa de cualquier pipeline de compilación es una etapa Compilación gestionada. Esta etapa proporciona instrucciones para que el pipeline retenga un servidor de compilación, copie los orígenes especificados de los repositorios de código en el servidor y ejecute una serie de acciones en ese servidor. En el momento de esta escritura, podemos utilizar una sola imagen para el servidor de compilación. Es una imagen de Oracle Linux (8 GB de memoria, 1 OCPU) que tiene una serie de herramientas preinstaladas y tiempos de ejecución de lenguaje. Para crear la imagen de contenedor de funciones, es relevante que el servidor de compilación esté equipado con Docker y Fn CLI.

Haga clic en el icono más o en la tarjeta Agregar etapa. Aparece el asistente de dos pasos Agregar una etapa. En el primer paso del asistente, asegúrese de que la tarjeta Compilación gestionada está seleccionada para el tipo de etapa. Haga clic en Siguiente.

Se muestra la segunda página. Defina un nombre para la etapa de compilación: build-go-source-to-function-container-image. También puede agregar una descripción.

En la actualidad, no podemos seleccionar una imagen de construcción diferente, por lo que nos conformamos con la disponible, lo que está bien para nuestros propósitos.

Defina la ruta del archivo de especificación de compilación en /functions/greeter/go-function-build-spec.yaml. Este archivo contiene las instrucciones para crear los orígenes Go en la función greeter (o cualquier otra función Go) y, finalmente, crear la imagen del contenedor de funciones.

Haga clic en el botón Select en Primary code repository. Ahora podemos especificar desde qué repositorio de código obtendrá la compilación sus orígenes. Seleccione OCI Code Repository como tipo de conexión de origen. A continuación, seleccione el repositorio go-on-oci-repo. Trabajaremos con fuentes en la rama principal, así que no cambie ese valor por defecto. Escriba go-on-oci-sources como valor para el nombre de origen de compilación. Una etapa de compilación gestionada puede utilizar orígenes de varios repositorios. En la especificación de compilación, podemos hacer referencia a cada una de las ubicaciones raíz de estos orígenes mediante la etiqueta definida como Nombre de origen de compilación. Haga clic en Guardar.

way-to-go-on-oci-artícula

Pulse el botón Agregar. De esta forma se completa la definición de la etapa de compilación gestionada. Esto es todo lo que se necesita para tomar fuentes y procesarlas en artefactos. Las instrucciones detalladas ejecutadas por esta etapa de compilación gestionada y en el servidor de compilación se definen en el archivo go-function-build-spec.yaml. Este archivo contiene las instrucciones para los pasos detallados reales ejecutados en el servidor de compilación.

version: 0.1
component: build
timeoutInSeconds: 6000
runAs: root
shell: bash
env:
# these are local variables to the build config
variables:
SOURCE_DIRECTORY: "go-on-oci-sources/functions/greeter"
FUNCTION_NAME: "greeter"

# # the value of a vaultVariable is the secret-id (in OCI ID format) stored in the OCI Vault service
# you can then access the value of that secret in your build_spec.yaml commands
vaultVariables:

# exportedVariables are made available to use in sucessor stages in this Build Pipeline
# For this Build to run, the Build Pipeline needs to have a BUILDRUN_HASH parameter set
exportedVariables:
- BUILDRUN_HASH


steps:
- type: Command
name: "Export variables"
timeoutInSeconds: 40
command: |
export BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7`
echo "BUILDRUN_HASH: " $BUILDRUN_HASH
echo "fully qual sources" ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
echo "container image version from build pipeline parameter" ${imageVersion}      
go version

- type: Command
timeoutInSeconds: 600
name: "Install golangci-lint"
command: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.37.1

- type: Command
timeoutInSeconds: 600
name: "Verify golangci-lint version"
command: |
/root/go/bin/golangci-lint version

- type: Command
timeoutInSeconds: 600
name: "Run go mod tidy for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go mod tidy

- type: Command
timeoutInSeconds: 600
name: "Run go vet for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go vet .

- type: Command
timeoutInSeconds: 600
name: "Run gofmt for Go Application"
command: |
gofmt -w ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}

- type: Command
timeoutInSeconds: 600
name: "Run Lint for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
/root/go/bin/golangci-lint run .

- type: Command
timeoutInSeconds: 600
name: "Run Unit Tests for Go Application (with verbose output)"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go test -v 

- type: Command
timeoutInSeconds: 600
name: "Build Go Function into Function Container Image"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
pwd
fn build --verbose
image=$(docker images | grep $FUNCTION_NAME  | awk -F ' ' '{print $3}') ; docker tag $image go-function-container-image    


outputArtifacts:
- name: go-function-container-image
type: DOCKER_IMAGE
# this location tag doesn't effect the tag used to deliver the container image
# to the Container Registry
location: go-function-container-image:latest

La especificación de compilación consta de tres partes:

  1. Configurar a quién ejecutar la secuencia de comandos, qué shell utilizar y qué variables utilizar
  2. Pasos de compilación: comandos de shell para ejecutar en el servidor de compilación
  3. Los artefactos de salida indican qué archivos al final de todos los pasos de compilación son significativos y se pondrán a disposición de otros pasos del pipeline (por ejemplo, publicar como artefacto).

Los pasos de creación se pueden resumir de la siguiente manera:

  1. Imprimir variables de entorno y la versión Go instalada actualmente (en el servidor de creación vainilla)
  2. Instalar golangci-lint
  3. Verificar el éxito y la versión de la instalación de golangci-lint
  4. Ejecute go mod tidy para organizar el archivo go.mod con dependencias.
  5. Ejecute el veterinario de inspección para ejecutar una primera inspección en Go Sources
  6. Ejecute go fmt para formatear los orígenes según las reglas de formato genéricas
  7. Ejecute golangci-lint para pelar (compruebe) las fuentes en función de varias reglas de pelado
  8. Ejecutar pruebas de unidad
  9. Crear orígenes de función en una imagen de contenedor de función mediante la CLI de Fn (almacenar en el registro de imágenes local)
  10. Etiquete la imagen de contenedor de función con el nombre go-function-container-image. Nombre utilizado en la siguiente etapa para buscar la imagen que se va a publicar

Estos pasos son en gran medida equivalentes a la compilación gestionada definida en el artículo anterior para la aplicación Go que finalmente se convirtió en un ejecutable binario desplegado en una VM. Los pasos 9 y 10 son diferentes: convierten la aplicación Go en una imagen de contenedor de función que es el producto final de la creación.

Segunda etapa: publicación de artefacto

En la página de visión general del pipeline de compilación, haga clic en el icono más situado en la parte inferior de la etapa de compilación gestionada actual. En el menú contextual que aparece, haga clic en Agregar etapa. Aparece el asistente de etapas.

Haga clic en Entregar artefactos. A continuación, haga clic en Next.

Introduzca el nombre de esta etapa: publish-greeter-function-container-image. Necesitamos seleccionar el artefacto en el proyecto DevOps que queremos publicar. Este artefacto es la imagen de contenedor go-on-oci/greeter:${imageVersion}. Haga clic en Seleccionar artefactos y seleccione la imagen de contenedor.

En el área Asociar artefactos con resultado de compilación, tenemos que indicar para cada uno de los artefactos seleccionados cuál de los resultados de una etapa de compilación gestionada es el origen para publicar el artefacto. La especificación de compilación define una salida etiquetada go-function-container-image. Esta salida hace referencia a la imagen de contenedor que genera el proceso de creación de funciones en el servidor de creación. Introduzca la etiqueta go-function-container-image en el campo Build config/result artifact name. Pulse el botón Add para crear la etapa Deliver Artifacts.

Tercera etapa: pipeline de despliegue de disparador

En la página de visión general del pipeline de compilación, haga clic en el icono más situado en la parte inferior de la etapa Entregar artefactos. En el menú contextual que aparece, haga clic en Agregar etapa. Aparece el asistente de etapas.

Haga clic en Trigger deployment. A continuación, haga clic en Siguiente.

Escriba un nombre para la etapa: trigger-deployment-of-greeter-function-to-go-on-oci-app y, opcionalmente, una descripción. Haga clic en el botón Select deployment pipeline. Seleccione el pipeline deploy-greeter-function-to-go-on-oci-app. Se muestran detalles del pipeline, incluidos los parámetros (imageVersion) y el artefacto utilizado por el despliegue.

Haga clic en Agregar para completar la definición de etapa y agregarla al pipeline de compilación.

De esta forma se completa el pipeline de compilación: capta orígenes, los procesa en un artefacto desplegable, publica el artefacto en el repositorio de imágenes y dispara el pipeline de despliegue para que lo tome desde allí.

way-to-go-on-oci-artícula

Ejecutar pipeline de compilación y pipeline de despliegue de disparador

Haga clic en Iniciar ejecución manual. Defina un valor para el parámetro imageVersion, por ejemplo, 0.2.0. Haga clic en el botón para iniciar el pipeline de compilación.

Ahora tardará unos minutos en completar el pipeline de compilación y disparar el despliegue posterior de la imagen de función recién creada.

way-to-go-on-oci-artícula

Cuando haya terminado todo y se informe que se ha realizado correctamente, puede llamar a la ruta en API Gateway que lleva a la función greeter para comprobar si la respuesta es realmente la nueva esperada.

curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<api gateway="" endpoint="">/my-api/greeting
{"message":"Extremely hot greetings from your automatically built and deployed function dear  Mickey Mouse"}

    </api>

Este es un momento para una pequeña celebración. Hemos logrado un proceso automatizado de extremo a extremo que toma las fuentes de aplicaciones Go del repositorio de código y ofrece, después de pelar, examinar, probar y crear, una nueva versión en ejecución en vivo de una función OCI sin servidor. Para producir más actualizaciones en la función, todo lo que tenemos que hacer es confirmar el cambio y disparar el pipeline de compilación. Y como siguiente paso, incluso podemos disparar el pipeline de compilación automáticamente cuando se produce una confirmación (específica) en el repositorio de código.

En la segunda parte de este artículo, analizaremos el uso de los servicios de OCI de las aplicaciones Go en general y de las funciones de Go en particular. Se introduce el uso del SDK de Go para OCI y se demuestran las interacciones con el servicio OCI Object Storage.

SDK de Go para OCI: interacción con OCI Object Storage desde aplicaciones Go

Oracle Cloud Infrastructure es la plataforma que puede crear y ejecutar aplicaciones desarrolladas en Go. Ya sea en instancias informáticas o como funciones sin servidor, y también en contenedores en un cluster de Kubernetes gestionado (como veremos en la quinta parte de esta serie). Es bueno darse cuenta de que OCI es mucho más para los equipos de desarrollo de Go que una plataforma de tiempo de ejecución. OCI ofrece muchos servicios que se pueden aprovechar de las aplicaciones. Servicios para almacenar archivos, datos relacionales o "NoSQL", para gestionar la publicación y el consumo de mensajes. Servicios para transferir y analizar datos. Y servicios que admiten operaciones en aplicaciones, como la supervisión.

La interacción con los servicios de OCI se puede realizar a través de las API de REST disponibles para todos los aspectos de todos los servicios. Llamar a servicios REST con cargas útiles JSON a través de HTTP es bastante fácil desde una aplicación Go. Hay un factor que complica: estas llamadas de API deben firmarse. La firma de Oracle Cloud Infrastructure utiliza el esquema de autenticación de "firma" (con una cabecera de autorización) y el proceso de firma no es exactamente trivial. Para obtener más información sobre este proceso de firma, consulte Documentación de OCI – Firmas de solicitud de API de REST de OCI.

Afortunadamente para el desarrollo de aplicaciones Go que llaman a los servicios de OCI, podemos hacer uso del SDK de Go para OCI. Este kit de desarrollo de código abierto facilita la firma de las solicitudes de API de OCI. Mediante el SDK, las llamadas a los servicios de OCI se realizan como llamadas locales mediante estructuras predefinidas de tipo estricto en lugar de tratar con cuerpos de solicitud y respuesta de JSON. El SDK de Go para OCI utiliza el mismo archivo de configuración que hemos utilizado anteriormente para la CLI de Fn y la CLI de OCI. La ubicación por defecto de este archivo es $HOME/.oci. Este archivo dirige el SDK de Go a un arrendamiento y una región específicos para una cuenta de usuario mediante la mitad de clave privada del par configurado para el usuario. Las aplicaciones Go que utilizan el SDK de Go para OCI pueden basarse simplemente en esta configuración sin tener que tratar ninguno de los detalles.

La documentación del SDK de Go para OCI se puede encontrar en la documentación de OCI – SDK para Go.

En esta sección, desarrollaremos una aplicación Go simple que utilice el servicio OCI Object Storage para crear y leer archivos. Al principio, se trata de una aplicación independiente que se puede compilar y ejecutar en todas partes (siempre que el archivo de configuración de OCI esté disponible). A continuación, analizamos la ejecución de la aplicación Go dentro de OCI, en una máquina virtual o como función. Este enfoque especial es relevante porque cuando se utiliza el SDK de Go dentro de OCI, puede aprovechar la identidad y los privilegios del componente que ejecuta la aplicación. Esto significa que el código que se ejecuta en OCI no necesita traer su propio archivo de configuración de OCI y, por lo tanto, es aún más sencillo.

Aplicación Any Go que habla con OCI Object Storage Service

En primer lugar, vamos a crear una aplicación Go muy sencilla que pueda conectarse a OCI a través del SDK. A continuación, utilizaremos esta base para agregar la interacción con el servicio de almacenamiento de objetos.

El cliente Go más sencillo de OCI

La aplicación Go más sencilla que se comunica con OCI mediante el SDK de Go para OCI se crea de la siguiente forma:

  1. Crear un directorio para la aplicación
  2. Crear un archivo go.mod con dependencias en el paquete Go SDK for OCI
  3. Ejecute go mod tidy para crear el archivo go.sum y descargar los paquetes necesarios
  4. Cree el archivo OCI-client.go con el código para hablar con OCI

La suposición es que el archivo de configuración de OCI que se ha tratado anteriormente en este artículo se encuentra en la ubicación por defecto con el nombre por defecto: $HOME/.oci/config. Si el archivo está en una ubicación diferente, puede utilizar la función ConfigurationProviderFromFile en el paquete github.com/oracle/oci-go-sdk/v65/common, que acepta la ubicación personalizada del archivo de configuración.

El archivo go.mod contiene este contenido:

module oci-client

go 1.16

require github.com/oracle/oci-go-sdk/v65 v65.2.0

El bit github.com/oracle/oci-go-sdk/v65 hace referencia a la versión más reciente (en el momento de la escritura) del SDK de Go. Los orígenes se descargan en función de esta referencia. En la aplicación Go, esto significa que podemos aprovechar los paquetes del SDK para acceder a varios servicios de la cartera de OCI.

El archivo oci-client.go contiene este código que utiliza los paquetes comunes e de identidad:

package main

import (
"context"
"fmt"

"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/identity"
)

func main() {    
c, _ := identity.NewIdentityClientWithConfigurationProvider(common.DefaultConfigProvider())
tenancyID, _ := common.DefaultConfigProvider().TenancyOCID()
request := identity.GetCompartmentRequest{
    CompartmentId: &tenancyID,
}
response, _ := c.GetCompartment(context.Background(), request)
fmt.Printf("Name of Root Compartment: %v", *response.Compartment.Name)
}

La primera línea de la función adquiere el cliente que se puede utilizar posteriormente para la mayoría de las interacciones con OCI, como la llamada GetCompartment que devuelve los detalles del compartimento raíz.

La aplicación se puede ejecutar mediante go run oci-client.go y producirá una salida muy sencilla:

Name of Root Compartment: <name of="" your="" oci="" tenancy="">

    </name>

Aunque el resultado no es especialmente notable, el hecho de que haya resultados es una prueba de que el SDK de Go para OCI está funcionando y está listo para ser aprovechado para actividades más elevadas.

Tenga en cuenta que el código ignora por completo los errores que se pueden producir. Si se producen errores, sustituya los caracteres de subrayado por una variable para capturar y manejar ese error.

Ir aplicación Creación y recuperación de objetos en el servicio OCI Object Storage y desde él

El servicio OCI Object Storage ofrece almacenamiento barato y persistente para diferentes tipos de datos. Los objetos, grandes y pequeños, se pueden almacenar mediante programación en este servicio para que se conserven durante períodos de tiempo cortos o largos y se pueda acceder a ellos mediante programación o a través de URL directas. Object Storage proporciona control de versiones, diferentes reglas de retención, cifrado de cualquier dato almacenado, gestión del ciclo de vida de objetos, eliminación automatizada basada en tiempo y diferentes niveles de almacenamiento.

Los objetos del servicio de almacenamiento de objetos se organizan en bloques. Un cubo es un contenedor lógico de objetos que vive en un compartimento específico. Algunas operaciones se pueden realizar en todos los objetos de un cubo.

El SDK de Go para OCI ofrece funciones y tipos que facilitan y facilitan el trabajo con las aplicaciones del servicio de almacenamiento de objetos de Go. Las operaciones para manipular cubos y crear, suprimir y recuperar objetos están disponibles fácilmente.

Para obtener más información sobre el paquete de Object Storage en el SDK de Go para OCI, consulte esta referencia: Documentación del paquete de Object Storage en el SDK de Go para OCI.

El repositorio de origen de este artículo contiene las aplicaciones de carpeta/store-n-retrieve, que tiene una aplicación Go simple que se conecta a su arrendamiento de OCI y crea un cubo, seguida de la creación de un objeto y la recuperación de ese mismo objeto. La aplicación utiliza el mismo DefaultConfigProvider que se utilizó en la sección anterior para firmar solicitudes a las API de OCI mediante el archivo $HOME/.oci/config.

La dependencia de esta aplicación en el SDK de Go para OCI se define en go.mod.

way-to-go-on-oci-artícula

La primera parte del código crea una instancia ObjectStorageClient. Muchas funciones se definen en la interfaz subyacente, todas ellas compatibles con algún tipo de interacción con el servicio de almacenamiento de objetos. ObjectStorageClient se crea mediante common.DefaultConfigProvider() (al igual que antes), utilizando el archivo de configuración de OCI por defecto con una referencia a un archivo que contiene la clave privada.

Se llama a la función getNamespace para obtener el espacio de nombres para el arrendamiento actual. A continuación, se llama a ensureBucketExists con el nombre del cubo para crear el cubo si aún no existe.

package main

import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"

"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/objectstorage"
)

const (
bucketName      = "go-bucket" // feel free to use a different name for the bucket
compartmentOCID = "<compartment-ocid></compartment-ocid>" // replace with the OCID of the go-on-oci compartment in your tenancy
objectName      = "welcome.txt" // feel free to use a different name for the object
)

func main() {
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(common.DefaultConfigProvider())
if cerr != nil {
fmt.Printf("failed to create ObjectStorageClient : %s", cerr)
}
ctx := context.Background()
namespace, cerr := getNamespace(ctx, objectStorageClient)
if cerr != nil {
fmt.Printf("failed to get namespace : %s", cerr)
} else {
fmt.Printf("Namespace : %s", namespace)
}

err := ensureBucketExists(ctx, objectStorageClient, namespace, bucketName, compartmentOCID)
if err != nil {
fmt.Printf("failed to read or create bucket : %s", err)
}
.........................

Las funciones invocadas en este fragmento para recuperar el espacio de nombres y comprobar la existencia del cubo (y crearlo si no existe) son bastante sencillas. Se definen de la siguiente manera:

func getNamespace(ctx context.Context, client objectstorage.ObjectStorageClient) (string, error) {
request := objectstorage.GetNamespaceRequest{}
response, err := client.GetNamespace(ctx, request)
if err != nil {
    return *response.Value, fmt.Errorf("failed to retrieve tenancy namespace : %w", err)
}
return *response.Value, nil
}

func ensureBucketExists(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
req := objectstorage.GetBucketRequest{
    NamespaceName: &namespace,
    BucketName:    &name,
}
// verify if bucket exists.
response, err := client.GetBucket(ctx, req)
if err != nil {
    if response.RawResponse.StatusCode == 404 {
        err = createBucket(ctx, client, namespace, name, compartmentOCID)
        return err
    }
    return err
}
fmt.Printf("bucket %s already exists", bucketName)
return nil
}

// bucketname needs to be unique within compartment. there is no concept of "child" buckets. using "/" separator characters in the name, the suggestion of nested bucket can be created
func createBucket(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
request := objectstorage.CreateBucketRequest{
    NamespaceName: &namespace,
}
request.CompartmentId = &compartmentOCID
request.Name = &name
request.Metadata = make(map[string]string)
request.PublicAccessType = objectstorage.CreateBucketDetailsPublicAccessTypeNopublicaccess
_, err := client.CreateBucket(ctx, request)
if err != nil {
    return fmt.Errorf("failed to create bucket on OCI : %w", err)
} else {
    fmt.Printf("created bucket : %s", bucketName)
}
return nil
}

La segunda parte de esta aplicación crea un objeto en el cubo y, posteriormente, recupera ese objeto. Cuando la aplicación haya terminado de ejecutarse, tendrá un efecto persistente: el cubo se ha creado (si aún no existe) y se ha creado o actualizado un objeto (si la aplicación se ha ejecutado antes). Puede proteger la página de detalles del cubo en la consola de OCI para ver tanto el cubo como el objeto que se ha creado.

..................

contentToWrite := []byte("We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.")
objectLength := int64(len(contentToWrite))
err = putObject(ctx, objectStorageClient, namespace, bucketName, objectName, objectLength, ioutil.NopCloser(bytes.NewReader(contentToWrite)))
if err != nil {
    fmt.Printf("failed to write object to OCI Object storage : %s", err)
}

var contentRead []byte
contentRead, err = getObject(ctx, objectStorageClient, namespace, bucketName, objectName)
if err != nil {
    fmt.Printf("failed to get object %s from OCI Object storage : %s", objectName, err)
}
fmt.Printf("Object read from OCI Object Storage contains this content: %s", contentRead)
}

func putObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string, contentLen int64, content io.ReadCloser) error {
request := objectstorage.PutObjectRequest{
    NamespaceName: &namespace,
    BucketName:    &bucketName,
    ObjectName:    &objectname,
    ContentLength: &contentLen,
    PutObjectBody: content,
}
_, err := client.PutObject(ctx, request)
fmt.Printf("Put object %s in bucket %s", objectname, bucketName)
if err != nil {
    return fmt.Errorf("failed to put object on OCI : %w", err)
}
return nil
}

func getObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string) (content []byte, err error) {
request := objectstorage.GetObjectRequest{
    NamespaceName: &namespace,
    BucketName:    &bucketName,
    ObjectName:    &objectname,
}
response, err := client.GetObject(ctx, request)
if err != nil {
    return nil, fmt.Errorf("failed to retrieve object : %w", err)
}
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(response.Content)
if err != nil {
    return nil, fmt.Errorf("failed to read content from object on OCI : %w", err)
}
return buf.Bytes(), nil
}

La ejecución de esta aplicación – go run object-organizer.go – da como resultado esta salida:

Namespace : idtwlqf2hanz
created bucket : go-bucket
Put object welcome.txt in bucket go-bucket
Object read from OCI Object Storage contains this content: We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.

OCI Functions basado en Go hablando con OCI Object Storage Service

En la sección anterior se ha tratado cualquier aplicación Go que interactúe con los servicios de OCI a través del SDK. Entonces, ¿qué más se puede decir sobre OCI Functions in Go? Sigue leyendo, porque resulta que podemos simplificar la vida cuando el código de Go que desarrolles y desde el que quieras trabajar con el SDK de Go se despliegue como una función de OCI o se ejecute en una instancia informática en OCI. En estos casos, podemos aprovechar la autenticación de entidad de recurso (para Functions) y la autenticación de entidad de instancia (para el código que se ejecuta en una instancia informática), lo que significa que no tenemos que incluir el archivo de configuración de OCI, y nuestro código que habla con el SDK puede ser un poco más simple.

Obtenga más información sobre la autenticación de entidad de recurso en la documentación de OCI sobre la autenticación de entidad de recurso para funciones.

En esta sección, hablamos de una función de OCI denominada object-broker. Encontrará los orígenes en el repositorio de origen de este artículo, en funciones de ruta/agente de objetos. Como antes, la función se define mediante un archivo func.yaml con metadatos y un archivo func.go con el enlace entre la función y la estructura de Fn. El archivo go.mod define las dependencias de la función. La lógica para interactuar con el servicio OCI Object Storage está en el objeto de archivo organizer.go. Esto define un func público CreateObject al que se llama desde func.go.

CreateObject crea un objeto con el nombre especificado en el cubo especificado. Las primeras líneas de la función CreateObject en el objeto-organizer.go se han modificado para trabajar con esta autenticación de entidad de recurso. auth.ResourcePrincipalConfigurationProvider() utilizado ahora no necesita que se incluya un archivo de configuración de OCI y una clave privada en la aplicación. Asume que el código se ejecuta dentro de OCI y, más específicamente, dentro de un recurso (que puede ser una función o, por ejemplo, un servidor de compilación DevOps) que se conoce como entidad de recurso porque se incluye en un grupo dinámico y hereda los permisos de los miembros de ese grupo. En poco tiempo, usted llegará a las instrucciones para tomar las medidas necesarias para esto.

func CreateObject(objectName string, bucketName string, compartmentOCID string) (string, err) {
configurationProvider, err := auth.ResourcePrincipalConfigurationProvider()
if err != nil {
    fmt.Printf("failed to get oci configurationprovider based on resource principal authentication : %s", err)
}
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(configurationProvider)

Volvamos nuestra atención al lado de func.go. Func(tion) myHandler maneja el disparador de función. Llama a CreateObject, pero no antes de determinar el nombre del objeto que debe producir la solicitud y el nombre del cubo para el cubo que va a contener el objeto. Estos nombres tienen un valor por defecto, pero la función intenta buscar valores de parámetros de consulta de solicitud HTTP que proporcionen valores específicos. Tenga en cuenta que esto solo funciona para un disparador HTTP en la función, y no para una llamada realizada mediante una llamada fn.

func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
objectName := "defaultObjectName.txt"
bucketName := "the-bucket"
fnctx := fdk.GetContext(ctx)             // fnctx contains relevant elements about the Function itself
fnhttpctx, ok := fnctx.(fdk.HTTPContext) // fnhttpctx contains relevant elements about the HTTP Request that triggered it
if ok {                                  // an HTTPContent was found which means that this was an HTTP request (not an fn invoke) that triggered the function
    u, err := url.Parse(fnhttpctx.RequestURL())
......

Puede que desee inspeccionar los detalles sobre el contexto de Fn en Documentación para Project Fn Go FDK, específicamente en el ejemplo.

La función debe saber en qué compartimento de OCI debe residir el cubo. Dichos valores de tiempo de ejecución se definen normalmente en la función de la aplicación mediante una configuración. Los valores de las configuraciones se pueden leer en la función desde un mapa en el contexto, como se muestra en esta línea:

if compartmentOCID, ok := fnctx.Config()["compartmentOCID"]; ok {

Creación y despliegue de la función con la CLI de Fn

Suponiendo que se encuentra en un terminal en el entorno de desarrollo local donde está instalada la CLI de Fn y el directorio actual es funciones/agente de objetos, puede realizar una compilación local de la función con una salida detallada:

fn -v build

Cuando la compilación se ve bien, el siguiente paso es el despliegue de la función. Si el contexto de Fn está definido para utilizar el contexto go-on-OCI (comprobar con contextos de lista de fn), se desplegará la función en la aplicación go-on-OCI-app en OCI:

fn -v deploy --app go-on-oci-app

La función solo puede realizar un trabajo significativo si se ha definido la configuración para el valor de OCID del compartimento. Para ello, puede utilizar la consola o la siguiente sentencia con la CLI de Fn:

fn cf f go-on-oci-app object-broker compartmentOCID <compartment-ocid>


    </compartment-ocid>

Ahora la función está desplegada y tiene su configuración. Puede esperar que una llamada a la función con este comando se realice correctamente:

fn invoke go-on-oci-app object-broker

Sin embargo, hay un aspecto final que manejar: la función utiliza las API del servicio OCI Object Storage para manipular cubos y objetos, pero tiene que haber recibido permisos explícitos para hacerlo. Para ello utilizamos un grupo dinámico y dos políticas.

Permisos para que las funciones manipulen objetos y cubos

Al igual que hemos utilizado grupos dinámicos para crear un usuario con privilegios que represente los pipelines de despliegue y los pipelines de compilación, también tenemos que crear un grupo dinámico que contenga las funciones a las que queremos otorgar permisos. Para crear el grupo dinámico, escriba dyn en la barra de búsqueda. Haga clic en el enlace Grupos dinámicos en el panel de resultados de búsqueda.

En la página de visión general de los grupos dinámicos, haga clic en Create Dynamic Group.

Introduzca el nombre del grupo dinámico para los pipelines de despliegue, por ejemplo, functions-in-go-on-oci, y, opcionalmente, escriba una descripción. Defina la siguiente regla que selecciona todas las funciones que forman parte del compartimento:

All {resource.type = 'fnfunc', resource.compartment.id = '<compartment_id>'}

    </compartment_id>

Por supuesto, sustituya por el identificador del compartimento en el que está trabajando. A continuación, presione Crear.

Para crear una política en la consola: escriba poli en la barra de búsqueda y haga clic en Políticas > Identidad en el área Servicios en la ventana emergente de resultados de búsqueda. Accederá a la página de visión general Políticas del compartimento actual.

La primera sentencia de política define el permiso para que la función gestione objetos en el compartimento. La segunda sentencia agrega el permiso para gestionar cubos. Defina un nombre, una descripción y las siguientes sentencias:

allow dynamic-group functions-in-go-on-oci to manage objects in compartment go-on-oci
allow dynamic-group functions-in-go-on-oci to manage buckets in compartment go-on-oci

En la siguiente figura se muestran los permisos que ahora se aplican a la función cuando se guarda la política que contiene estas sentencias:

way-to-go-on-oci-artícula

Ahora se puede llamar a la función y debe poder hacer lo que esté utilizando los nombres por defecto para el cubo y el objeto.

fn invoke go-on-oci-app object-broker

Verifique que se ha creado el cubo y que contiene el objeto recién creado mediante la consola desde la URL de OCI hasta la página de cubos.

Agregar ruta en API Gateway a función de disparador

Para poder llamar a la función de agente de objetos a través de HTTP desde cualquier lugar, volveremos a utilizar API Gateway. Escriba gat en la barra de búsqueda de la consola. Haga clic en >Gateways > Gestión de API. Haga clic en el enlace para the-api-gateway. Haga clic en Deployments. En la lista con despliegues, que contiene un único despliegue, haga clic en el enlace myserver-api.

Haga clic en Edit para abrir la especificación de despliegue. Haga clic en el enlace para el segundo paso: Rutas. Desplácese hacia abajo y haga clic en + Otra ruta.

Escriba /object-broker como ruta para esta nueva ruta. Seleccione GET como método y Oracle Functions como tipo (de backend). Seleccione application go-on-oci-app y, a continuación, defina Function Name en object-broker. Pulse Siguiente y, a continuación, pulse Guardar cambios para aplicar los cambios y hacer que la nueva ruta sea real.

La imagen completa que ahora se configura del consumidor HTTP a través de API Gateway to Function y, finalmente, del cubo y el objeto tiene el siguiente aspecto:

way-to-go-on-oci-artícula

Llame a la función desde el explorador o utilizando curl en la línea de comandos mediante:

curl -X "GET" "http://<api gateway="" endpoint="">/my-api/object-broker?objectName=new-exciting-object.txt&bucketName=the-ultimate-collection"


    </api>

Creación y despliegue automatizados

Este agente de objetos de función se desplegó manualmente en la línea de comandos mediante la CLI de Fn. Eso funcionó bien, por supuesto. Sin embargo, si ahora comenzara el desarrollo en esta función, pasando por varios ciclos de desarrollo, probablemente desearía introducir la automatización en el proceso de creación y despliegue.

Al igual que hemos hecho antes, puede configurar fácilmente los elementos necesarios en OCI DevOps para lograr pipelines automatizados para el despliegue (de la imagen de contenedor de función en el registro de imágenes de contenedor) y la compilación (a partir del repositorio de código y como resultado una imagen de contenedor recién horneada para la función). El elemento principal específico de la función es el archivo de especificación de compilación para la etapa de compilación gestionada en el pipeline de compilación. Este archivo se proporciona como go-function-build-spec.yaml en el mismo directorio que func.go y func.yaml.

Después de crear un artefacto DevOps para la imagen de contenedor de función, un entorno para la función y los dos pipelines para la creación y el despliegue, la configuración del proceso DevOps automatizado tendría el siguiente aspecto:

way-to-go-on-oci-artícula

Conclusión

Un área de enfoque de este artículo son las funciones sin servidor, escritas en Go y en ejecución en Oracle Cloud Infrastructure. Se ha tratado la creación y el despliegue automatizados de estas funciones, así como el uso de API Gateway para proporcionar acceso a la función a consumidores HTTP externos.

El segundo tema principal fue el SDK de Go para OCI para interactuar con los servicios de OCI desde las aplicaciones de Go. En el artículo se muestra cómo utilizar el código Go para acceder al servicio Object Storage y almacenar y recuperar archivos.

Los dos temas se combinaron en función objeto-agente. Esta función de OCI aprovecha la autenticación de la entidad de recurso y los permisos otorgados mediante un grupo dinámico. Mediante una configuración de tiempo de ejecución, la función aprende sobre la configuración específica del entorno actual.

En el siguiente artículo, la interacción con Oracle Database será el tema principal. Creación de una conexión de una aplicación Go a una instancia local de Oracle Database, así como a una instancia de Autonomous Database que se ejecuta en OCI, y realización de operaciones SQL con estas bases de datos desde la comodidad de la aplicación Go. Otros temas incluyen trabajar con Oracle Wallet para una gestión adecuada de las credenciales de la base de datos, incluida la cartera en el proceso de despliegue, y combinar interacciones con OCI Object Storage y los servicios de Autonomous Database en una sola aplicación.

Chatbot de Oracle
Disconnected