Golang y Docker para desarrollo y producción

blog-image

Cada vez que un desarrollador se une a un nuevo proyecto, es necesario que este monte el entorno, instale librerias, dependencias, y configure las variables de entorno.

Además, hacer que el entorno de desarrollo sea el mismo que el de producción ahorra muchos sustos a la hora de realizar deploys, a la vez que nos ayuda a cumplir los objetivos del sprint en metodologías agile.

En esta entrada, os enseñaremos a escribir un único Dockerfile que nos permita crear un contenedor de desarrollo que recargue los cambios de manera dinámica (sin reconstruir el contenedor) usando gravityblast/fresh.

Fresh es una herramienta de línea de comandos que construye y (re)inicia una aplicación web cada vez que guardas un archivo Go.

Hands on code

Primero crearemos una aplicación de ejemplo, una pequeña API en go utilizando la librería de gin-gonic. Si no conoces esta librería, en github puedes encontrar el código e información.

¿No conoces go?

¿Por qué no empiezas con nuestro curso?

Creamos el directorio para la app, y introducimos el siguiente código para crear la API en go:

$ mkdir example
$ cd example
$ mkdir app
$ cd app
$ touch app.go
package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func setupRouter() *gin.Engine {

    r := gin.Default()

    // Ping exmple api
    r.GET("/ping", func(c *gin.Context) {
        c.String(http.StatusOK, "pong")
    })

    return r

}

func main() {

    r := setupRouter()
    // Listen and Server in 0.0.0.0:8080
    r.Run(":8080")

}

El código se explica por si solo, si no recuerda consultar la documentación de gin-gonic.

Una vez guardado, crearemos el DockerFile necesario para ejecutar la aplicación en el contenedor.

$ cd .. 
$ touch Dockerfile

Añadiremos las siguientes funciones a nuestro fichero Docker

FROM golang Crearemos una imagen Docker partiendo de golang, que instala golang y establece GOPATH.
ARG app_env permite que app_env se establezca durante la compilación.
ENV APP_ENV $app_env establece una variable de entorno app_env, de esta manera, la variable persistirá en el contenedor para su uso en el código.**
COPY ./app /go/src/example.com/user/example/app** copia el código local en el directorio GOPATH.
WORKDIR /go/src/example.com/user/example/app configura el directorio de trabajo para que todos los comandos siguientes se ejecuten desde este directorio
RUN go get ./ instalar todas las dependencias.
RUN go build construir el binario.
CMD if …Si arg_env se configuró para ejecutarse en producción, se ejecuta el binario, si no, se usa gravityblast/fresh para ejecutar el código con recarga en caliente.
EXPOSE 8080 Este es el puerto en el que se ejecuta la aplicación.

El fichero DockerFile, tendrá la siguiente forma:

FROM golang

ARG app_env
ENV APP_ENV $app_env

COPY ./app /go/src/example.com/user/example/app
WORKDIR /go/src/example.com/user/example/app

RUN go get ./
RUN go build

CMD if [ ${APP_ENV} = production ]; \
    then \
    app; \
    else \
    go get github.com/gravityblast/fresh && \
    fresh; \
    fi
    
EXPOSE 8080

Ejecutando la app en modo desarrollo

Construimos la imagen con el siguiente comando

$ docker build ./

Despues de construirlo, lo ejecutamos con el siguiente comando (puedes utilizar $ docker images para encontrar la id de la imagen).

$ docker run -it -p 8080:8080 -v [put your path here]/app:/go/src/example.com/user/example/app [image id]

Ahora puedes encontrar la aplicación ejecutandose en localhost:8080 y si haces cambios en el código ¡Se reflejan automaticamente en el navegador! Esto sucede gracias a Fresh y al volumen compartido creado con -v.

Ejecutando la app en producción

Para construir la imagen. utiliza el siguiente comando:

$ docker build ./ --build-arg app_env=production

Para ejecutar la imagen en producción:

$ docker run -i -t -p 8080:8080 [image id]

Y listo!!!

Conclusión

Utilizar docker como entorno de desarrollo y producción es algo muy fácil de conseguir, y ahorra muchos quebraderos de cabeza, sobre todo al trabajar con entornos que necesitan una configuración del sistema para utilizarlos.