Tener un repositorio privado en AWS ECR resulta muy útil para mantener las versiones de nuestra aplicación dentro de la nube y ejecutar tareas con ellas. Pero todavía es más interesante gestionar estas versiones a través de un motor de CI/CD, además de etiquetarlas automáticamente para disponer de un registro de los cambios realizados en cada versión y poder promocionarlas entre nuestros distintos entornos.
En este breve artículo voy a mostrar cómo integrar una acción de push dentro de un flujo de trabajo de Github Actions. El momento en el que debemos subir nuestras imágenes depende del flujo de publicación que hayamos diseñado para la aplicación. En mi caso, suelo configurar dos entornos en mis proyectos (dev / pro), y publico automáticamente en el entorno de desarrollo cuando hago merge de un pull request con la rama develop.
Es en ese momento cuando genero una nueva imagen que, una vez probada en el entorno de desarrollo mediante tests automáticos y manuales, promociono al entorno de producción. Para desplegar nuestras imágenes, normalmente configuro un flujo de trabajo con los siguientes pasos:

Hora de escribir código
Con el diagrama anterior en mente, podemos empezar a configurar nuestro archivo de Github Actions dentro del proyecto. Veamos el siguiente fragmento de código:
# ./.github/workflows/develop_push.yml
# -----------------------------------------------
name: "On merge inside develop"
on:
push:
branches:
- develop
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
jobs:
applytf:
runs-on: ubuntu-latest
name: "Apply TF"
permissions:
pull-requests: write
contents: read
defaults:
run:
working-directory: ./iac
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Init
id: init
run: terraform init
- name: Terraform apply
run: terraform apply -auto-approve -input=false
publishdocker:
name: "Build docker and publish to ECR"
needs: [ applytf ]
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./src
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Bump version and push tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
release_branches: "main,develop"
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: example
IMAGE_TAG: ${{ steps.tag_version.outputs.new_version }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
El primer job aplica la infraestructura de Terraform dentro de nuestra cuenta de AWS. No es el objetivo principal de este artículo, pero podemos repasar lo que hace:
- En primer lugar, establece el directorio de trabajo dentro de la carpeta
./iac. Esta carpeta contiene todo nuestro código de Terraform que creará, junto con otros recursos, nuestro repositorio privado de ECR. - El segundo paso inicializa el entorno de Terraform y descarga los diferentes providers y módulos que necesitará para aplicar la infraestructura.
- El último paso aplica la infraestructura de Terraform dentro de nuestra cuenta en la nube. ¿Cómo lo hace? Si revisas el archivo completo, verás que al inicio definimos tres variables de entorno para conectar con nuestra cuenta de AWS. Estas credenciales son las que Terraform utilizará para ejecutar y desplegar la infraestructura.
El segundo job depende de la ejecución del job de Terraform. Paso a paso, esto es lo que hace:
- Establece el directorio de trabajo en la carpeta
./src. Suponemos que ahí tenemos todo el código junto con el archivo Dockerfile para construir nuestra imagen. - Bump version y push tag. Quizá el paso más interesante: con esta acción podemos crear automáticamente una versión major, minor o patch. Se genera la etiqueta en el punto específico del código y se añade un log con los cambios respecto a la etiqueta anterior.
- Como puedes ver, he configurado la propiedad
release_branchescon los nombres de las ramas develop y main. Esto es porque, como expliqué al inicio del artículo, suelo promocionar la misma versión desde develop al entorno de producción (sin reconstruirla). Así me aseguro de liberar en producción exactamente la misma versión que ha sido probada en develop. 👉 Código de la acción en Github: github-tag-action - Las dos acciones siguientes configuran las credenciales para acceder a AWS en el paso posterior. Responden con las credenciales necesarias y claves temporales que permiten al comando Docker hacer push de la imagen a nuestro ECR privado. 👉 Código de la acción en Github: amazon-ecr-login
- Finalmente, se construye la imagen Docker con el archivo Dockerfile ubicado en la carpeta
./src, etiquetándola con dos tags distintos:- latest → para marcarla como la última imagen disponible en el repositorio.
- Versión → generada a partir de la acción de etiquetado.
- Después se hace push de las imágenes al repositorio ECR y listo.
TL;DR
Como vemos, la acción para subir nuestras imágenes a un repositorio privado de ECR es bastante directa. Lo más importante es definir correctamente nuestros flujos de publicación, decidir exactamente cuándo queremos hacer el push y cuál será el ciclo de vida de las imágenes que subimos al servidor.