Cómo lanzar una aplicación web en Google Cloud Run con Cloud Build

En el pasado evento del Cloud Next, Google anunció un nuevo producto dentro de sus servicios: Cloud Run. Una evolución de App Engine que básicamente permite correr cualquier lenguage de backend sobre un contenedor de Docker.

En este artículo voy a describir como empezar con éste servicio y correr tu primera aplicación Node.js con ello. ¡Vamos allá!

Crear el proyecto en Google Cloud

Vamos a la consola de Google Cloud y creamos un nuevo proyecto, yo lo he llamado hello-cloud-run pero puedes llamarle como quieras. Recuerda el Project ID que lo utilizaremos despues.

crear proyecto en Google Cloud

Nuevo proyecto en Google Cloud

Creando el proyecto

Proyecto creado y Project ID

Para poder usar Google Cloud necesitas una cuenta de gmail, activar el billing. Si es la primera vez que te registras tienes 300$ de free trial.

Activar APIs

Tenemos que activar un par de APIs para no tener problemas, la de Cloud Run y la de Cloud Build que utilizaremos más tarde.

Activación de APIs en Google Cloud

Clickamos en Enable APIs and Services y buscamos Cloud Run

Cloud Run

Activamos el API de Cloud Run y hacemos lo mismo con Cloud Build

Activar API de Cloud Run

Activar API de Cloud Build

Código de nuestra app de ejemplo

Este es el código que he preparado para este ejemplo. Es una aplicación Node.js que al dirigirse el usuario a la URL raiz le devuelve un JSON con dos propiedades, la fecha de hoy y el tiempo que lleva activa la aplicación.

Creamos un proyecto de Node, ejecutando el siguiente comando por consola:

$ npm init -y

Y seguidamente instalamos express como dependencia:

$ npm i express

Creamos un fichero index.js con el siguiente contenido:

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

const dateUp = Date.now();

app.get('/', (req, res) => {
  const today = new Date();

  res.json({
    date: today,
    up: `${(Date.now() - dateUp)/1000} seg.`,
  });
});

app.listen(port, () => {
  console.log(`Server running on port: ${port}`);
  console.log('Press CTRL + C to quit');
});

Modificamos un poco el fichero package.json para añadirle el script de inicio:

...
"scripts": {
   "start": "NODE_ENV=production node index.js"
  },
...

De esta forma cuando ejecutemos el comando npm start correrá la mini aplicación en producción. Podemos probarlo en local para ver si todo va OK.

Lo siguiente es crear el fichero Dockerfile con el que vamso a definir el contenedor que va albergar esta mini aplicación de ejemplo. Tendrá el siguiente contenido:

FROM node:10

WORKDIR /usr/src/app

ENV PORT 8080
ENV HOST 0.0.0.0

COPY package*.json ./

RUN npm install --only=production

# Copy the local code to the container
COPY . .

# Build production app
# RUN npm run Build

# Start the service
CMD npm start

Aquí estamos diciendo que tome como base Node.js v10, el directorio del contenedor donde estará el código será /usr/src/app. Definimos como variables de entorno el puerto PORT 8080 y el HOST 0.0.0.0. Copiamos los ficheros package.json y package-lock.json al WORKDIR e instalamos las dependencias con RUN npm install --only=production.
Movemos el codigo de la app al contenedor con COPY . . y con el último comando se pone en marcha la app.

Podemos probar si todo está bien hasta ahora, generando la imagen y el contenedor de Docker. Eso lo conseguimos con los siguientes comandos en la terminal:

$ docker build --tag hello-cloud-run:01 .
$ docker run -p 8080:8080 hello-cloud-run:01

Para que esto te funcione necesitas tener Docker instalado en tu equipo

Con el comando de build hemos creado una imagen siguiendo el Dockerfile con el nombre hello-cloud-run:01 y el comando de run lo que hace es correrlo en http://localhost:8080

Si todo va bien debería salirte algo tal que así:

Aplicacion Node.js corriendo en un contenedor Docker en Localhost

Automatizar el despliegue del contenedor al Container Registry

Con nuestro proyecto configurado en Google Cloud y el código de la aplicación escrito y contenerizado, lo siguiente es subirlo al Container Registry

Para hacer más PRO este tutorial, vamos a utilizar un fichero YAML de configuración con Google Cloud Build. Similar a como haríamos con Travis CI por ejemplo, pero personalizado para Cloud Build.

De esta forma, cada vez que hagamos push a master de nuestro repositorio de código (en Github por ejemplo) se ejecutará Cloud Build y lo subirá al Container Registry además de ejecutarlo en Cloud Run. Magic!

Primero creamos un trigger en Cloud Build dentro de la consola de Google Cloud:

Cloud Build Triggers

Al crearlo elegimos que la fuente será un repositorio de GitHub:

Crear Trigger

Nos autenticamos en el servicio (En este caso GitHub) y elegimos el repositorio que lanzará el trigger.
Para ello primero debes crear tu repositorio en Github.

Puedes utilizar el de mi mi repo (Haciendo un fork) como base, o crear el tuyo propio y luego elegirlo entre los que tienes en tu cuenta:

Trigger Github repository

Y en las settings elige que para el Build Configuration vamos a utilizar un fichero, el cloudbuild.yaml que seguidamente crearemos.

Screenshot-2019-07-02-at-23.30.39

Y listo, ya lo tienes. En las opciones puedes elegir si quieres que se dispare cada vez que subes a una rama, o cada vez que hagas un tag

Trigger creado.

Añadimos permisos

Una vez activado el API de Cloud Run tenemos que realizar los siguientes pasos para permitir acceso desde el exterior a nuestra aplicación.

  1. Asignamos el rol de Cloud Rn Admin al Cloud Build service account:
    1. Desde la consola de Google Cloud, accede al menu IAM
    2. En la lista de miembros, localiza y selecciona [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com
    3. Clicka en el botón EDIT (Icono de lápiz) de la fila para dar aprobar el nuevo rol a la cuenta.
    4. CLicka en Add another role
    5. Selecciona Cloud Run y luego Cloud Run Admin
    6. Clicka en Save
  2. Asignamos el rol IAM Service Account User al Cloud Build Service Account desde el Cloud Run Runtime service account
    1. Desde la consola de Google Cloud, accede al menu de Service Accounts
    2. En la lista de miembros, localiza y selecciona [PROJECT_NUMBER]-compute@developer.gserviceaccount.com
    3. Clicka en SHOW INFO PANEL arriba a la derecha
    4. En el panel de PERMISSIONS, clicka en el botón de Add Member
    5. Introduce la service account de Cloud Build: [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com en el nuevo campo de New Member
    6. En el desplegable de Role, selecciona Service Accounts y luego Service Account User.
    7. Clicka en Save

Ahora en nuestro código vamos a crear el fichero cloudbuild.yaml que ejecutará los comandos necesarios para construir la imagen de docker, subirla al container registry y desplegarla en Cloud Run:

steps:
  # build the container image
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/hello-cloud-run:${SHORT_SHA}', '.']
  # push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/$PROJECT_ID/hello-cloud-run']
  # deploy container image to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['beta', 'run', 'deploy', 'hello-cloud-run', '--image', 'gcr.io/$PROJECT_ID/hello-cloud-run:${SHORT_SHA}', '--region', 'us-central1', '--allow-unauthenticated']
  env:
  - 'PORT=8080'
images:
- gcr.io/$PROJECT_ID/hello-cloud-run

Siendo <PROJECT_ID> el identificador de tu proyecto que vimos al inicio.

Probando que todo funcione

Lo que nos queda hacer es subir nuestro código a un repositorio, que puede ser Github, como por ejemplo como lo tengo yo en mi repo y cada vez que hagamos un cambio y lo subamos a la rama Master, el build se ejecutará, subirá el código al Container Registry y lo desplegará en Cloud Run!

Cuando hagas push revisa dentro de la consola de Google Cloud, si el Cloud Build ha disparado un evento:

Histórico de triggers en Cloud Build

Si ha sido correcto, puedes revisar en el Container Registry si la imagen de Docker ha sido creada:

Imagen de Docker en el Container Registry

Y por último, en Cloud Run revisa que se está ejecutando

Cloud Run Ejecutándose

Un último detalle es permitir invocaciones externas al servicio porque por defecto (al menos creo que porque aún es un servicio en Beta) el servicio queda privado.

Se podría configurar a través de Cloud Build pero no he conseguido hacerlo funcionar. Una opción más rápida es una vez que el servicio está creado, desde la consola de Google Cloud, activarlo:

Se añade allUsers a los nuevos miembros y el rol de Cloud Run > Cloud Run Invoker

Permitir invocaciones externas

Puedes ver una explicación más detallada en éste post de Dev.to

Y ya! Clicka en la URL que aparece asociada a tu proyecto y si todo va bien tendrás algo así en el navegador:

App corriendo

Referencias


Desarrollador web Frontend y apasionado de JavaScript. Aquí te enseño todo lo que aprendo y conozco sobre JavaScript y la programación web en general.

¿Te gusta lo que lees?

Invítame a un café virtual ☕️ 😉

Buy me a coffeeBuy me a coffee