-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IV-21-22] Objetivo 5 #35
base: main
Are you sure you want to change the base?
Changes from all commits
a4622ca
73089b8
6fb94c5
5f86a7a
f980104
3c5cda8
486c2bb
f7cbdd5
d5ddd18
dbd93a4
189e521
304b525
64f01b6
269e8c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,146 @@ | ||
# Contenedor de pruebas | ||
|
||
Para tener una forma de hacer que la aplicación sea portable y esté lista para integrase con CI/CD, debemos de elegir una imagen base que lo acompañe. Los principios básicos que se debe de seguir esta imagen base son: | ||
Para tener una forma de hacer que la aplicación sea portable y esté lista para integrarse con CI/CD, debemos de elegir una imagen base que lo acompañe. Los principios básicos que se debe de seguir esta imagen base son: | ||
|
||
* Debe de ser estable, esto implica que siempre debe de funcionar siempre de igual manera, es decir, que dadas las mismas entradas y condiciones se producirán invariablemente las mismas salidas y condiciones. Esto evitará errores y problemas que dependan del entorno. Por tanto siempre tendremos que utilizar bibliotecas compatibles. | ||
* Debe de ser una imagen ligera, siempre que se pueda, es decir, tener las funcionalidades necesarias de Go para cumplir con la correcta construcción y ejecución de nuestro proyecto. Basicamente, esto sirve para acelerar la construcción, la implementación y también reducir costos con el almacenamiento y la salida de la red si está utilizando algún proveedor de la nube. | ||
* Debe de ser una imagen ligera, siempre que se pueda, es decir, tener las funcionalidades necesarias de Go para cumplir con la correcta construcción y ejecución de nuestro proyecto. Básicamente, esto sirve para acelerar la construcción, la implementación y también reducir costos con el almacenamiento y la salida de la red si está utilizando algún proveedor de la nube. | ||
* Debe de recibir actualizaciones frecuentes, de esta manera se evitarán problemas de seguridad y rendimiento. | ||
|
||
### Imagen de Golang | ||
* Debe ofrecer un buen rendimiento, esto abarca desde el tamaño de la imagen hasta la duración de la ejecución de los tests pasando por el tiempo construcción del contenedor. | ||
|
||
Teniendo en cuenta los requisitos nombrados podremos lograr un tamaño mínimo de imagen de Docker utilizando imagenes base que se centran en el minimalismo, como Alpine Linux. Dentro de Docker Hub nos vamos a centrar en la imagen oficial de Golang suministrada por Dockerhub, ya que hoy dia es la que más actualizaciones recibe y con mayor frecuencia. Tenemos otras opciones de *Verified Publisher* que son entidades comerciales que publican imagenes muy confiables y estan mantenidas por ellos, como Circle CI o portainer, en el caso de Circle CI las actualizaciones se reciben cada 3/4 semanas y en el caso de portainer la última actualización se recibió hace 4 años. Por tanto, teniendo en cuenta los principios básicos vamos a centramos en las imagenes oficiales de Dockerhub. | ||
|
||
## Imagen de Golang | ||
|
||
Teniendo en cuenta los requisitos nombrados podremos lograr un tamaño mínimo de imagen de Docker utilizando imágenes base que se centran en el minimalismo, como Alpine Linux. Dentro de Docker Hub nos vamos a centrar en la imagen oficial de Golang suministrada por Dockerhub, ya que hoy día es la que más actualizaciones recibe y con mayor frecuencia. Tenemos otras opciones de *Verified Publisher* que son entidades comerciales que publican imágenes muy confiables y están mantenidas por ellos, como Circle CI o portainer, en el caso de Circle CI las actualizaciones se reciben cada 3/4 semanas y en el caso de portainer la última actualización se recibió hace 4 años. Por tanto, teniendo en cuenta los requisitos nombrados vamos a centrarnos en las imágenes oficiales de Dockerhub. | ||
|
||
Las variantes que nos encontramos son: | ||
|
||
* `golang:<version>`, es la imagen por defecto. Si no estamos seguros de cuáles son nuestras necesidades, probablemente esta es la mejor opción. Además puede incluir etiquetas como pueden ser *bullseye*, *buster* o *stretch*. Estas etiquetas son los nombres de código de la suite para las versiones de Debian e indican en que versión se basa la imagen. | ||
* `golang:<version>-alpine`, esta imagen se basa en el proyecto Alpine Linux. Las imagenes Alpine Linux son mucho más livianas que la mayoría de imágenes base de distribución (~5 MB). Esta variante es experimental y no es oficialmente compatible con el [proyecto Go](https://github.com/golang/go/issues/19938). La principal advertencia a tener en cuenta es que utiliza **musl libc** en lugar de **glibc**, puede llegar a provocar un comportamiento inesperador en nuestra aplicación. En [este artículo](https://news.ycombinator.com/item?id=10782897) se conversa acerca de los problemas que puede traer este tipos de imagenes. | ||
* `golang:<version>-alpine`, esta imagen se basa en el proyecto Alpine Linux. Las imágenes Alpine Linux son mucho más livianas que la mayoría de imágenes base de distribución (~5 MB). Esta variante es experimental y no es oficialmente compatible con el [proyecto Go](https://github.com/golang/go/issues/19938). La principal advertencia a tener en cuenta es que utiliza **musl libc** en lugar de **glibc**, puede llegar a provocar un comportamiento inesperador en nuestra aplicación. En [este artículo](https://news.ycombinator.com/item?id=10782897) se conversa acerca de los problemas que puede traer este tipo de imágenes. [En esta página](https://wiki.musl-libc.org/functional-differences-from-glibc.html) se comentan las diferencias funcionales entre `glibc` y `musl libc`. Las principales diferencias que hay entre ambas librerías son cuestiones que no van a afectar directamente a nuestro proyecto, por ejemplo, si hiciesemos uso de un gestor de paquetes con una imagen Alpine tendremos que usar `apk` y con imagenes que usen la librería glibc se usaría `apt`, pero esto no nos concierne ya que no tenemos que instalar paquetes para que nuestro proyecto funcione correctamente. [Aquí](https://honnef.co/posts/2015/06/statically_compiled_go_programs__always__even_with_cgo__using_musl/) se comenta que si se usa el paquete cgo para usar funciones de C en Go necesitará este enlazarse con una librería libc, en este caso si podriamos tener problemas al elegir una imagen y otra, pero como digo para nuestro proyecto no existen tales dependecias, solamente existe la de Task y las dependecias reflejadas en go.mod. | ||
* `golang:<version>-windowsservercore`, esta imagen se basa en Windows Server Core. | ||
|
||
|
||
### Versiones de Go | ||
Dentro de las posibles imágenes tenemos que saber elegir la versión de Go para ejecutar nuestro proyecto, las distintas versiones las podemos encontrar [aquí](https://go.dev/doc/devel/release). Tenemos que usar una versión que permita obtener los resultados esperados en nuestra aplicación, que tenga soporte y actualizaciones frecuentemente. Tanto [aquí](https://endoflife.date/go) como la [página oficial](https://go.dev/doc/devel/release), podemos ver que las versiones 1.16 y 1.17 son actualmente tienen soporte, por tanto las versiones 1.15 y anteriores quedan descartadas, según los requisitos nombrados. Vamos a tener en cuenta, por ahora, ambas versiones y realizaremos pruebas con ambas para obtener una conclusión final. Cabe mencionar que la versión 1.17 recibe actualizaciones más a menudo que la versión 1.16. | ||
|
||
|
||
Como candidatos a nuestro proyecto: | ||
|
||
* [golang:1.17-stretch](https://github.com/docker-library/golang/blob/6b93987c3ec7bb3082dd54a46e9b6b8de95b0eb1/1.17/stretch/Dockerfile) Debian 11, se elige esta version de Debian porque es la última version más estable que se ha lanzado. | ||
* [golang:1.17-alpine](https://github.com/docker-library/golang/blob/6b93987c3ec7bb3082dd54a46e9b6b8de95b0eb1/1.17/alpine3.15/Dockerfile) Alpine 3.15, se elige está porque es rápida y ligera, una de las más populares imagenes base para contenedores Docker. | ||
* [golang:1.17.6-bullseye](https://github.com/docker-library/golang/blob/6b93987c3ec7bb3082dd54a46e9b6b8de95b0eb1/1.17/bullseye/Dockerfile) Debian 11, se selecciona como candidato porque actualmente es la versión estable de Debian, lo podemos comprobar tanto [aquí](https://wiki.debian.org/Status/Stable) como [aquí](https://wiki.debian.org/DebianBullseye). Dado que uno de nuestros requisitos es elegir una versión estable, esta opción se ajusta a nuestros criterios. | ||
* [golang:1.17.6-alpine](https://github.com/docker-library/golang/blob/6b93987c3ec7bb3082dd54a46e9b6b8de95b0eb1/1.17/alpine3.15/Dockerfile) Alpine 3.15, se elige está porque es rápida y ligera, una de las más populares imágenes base para contenedores Docker. | ||
* [golang:1.16-bullseye](https://github.com/docker-library/golang/blob/6b93987c3ec7bb3082dd54a46e9b6b8de95b0eb1/1.16/bullseye/Dockerfile). | ||
* [golang:1.16-alpine](https://github.com/docker-library/golang/blob/6b93987c3ec7bb3082dd54a46e9b6b8de95b0eb1/1.16/alpine3.15/Dockerfile). | ||
|
||
Se elige la versión 1.17 de Golang porque es una de las versiones más recientes y que recibe con mayor frecuencia actualizaciones, nuestro proyecto hasta el momento ha estado trabajando en este versión de Go. Se puede observar el soporte y últimas actualizaciones de Go en [esta página](https://endoflife.date/go). | ||
Vamos a analizar las opciones. | ||
|
||
## Análisis de construcción | ||
|
||
### Tiempos de construcción | ||
|
||
En esta sección se generan los contenedores usando la herramienta `time`. Se ejecuta la orden `time docker build --no-cache . -f ./Dockerfile -t go_<bullseye|alpine>_1.1X`. Los resultados son los siguientes: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Las comparaciones de tiempo de construcción no sirven para nada. |
||
|
||
* Alpine, versión de Go 1.17 | ||
|
||
```shell | ||
Successfully built 8c6c1dda8a53 | ||
Successfully tagged go_alpine_1.17:latest | ||
|
||
real 0m5,843s | ||
user 0m0,063s | ||
sys 0m0,057s | ||
``` | ||
|
||
* Alpine, versión de Go 1.16 | ||
|
||
```shell | ||
Successfully built 7f201f405681 | ||
Successfully tagged go_alpine_1.16:latest | ||
|
||
real 0m5,742s | ||
user 0m0,044s | ||
sys 0m0,054s | ||
``` | ||
|
||
* Bullseye, versión de Go 1.17 | ||
|
||
```shell | ||
Successfully built d2c15c730270 | ||
Successfully tagged go_bullseye_1.17:latest | ||
|
||
real 0m5,761s | ||
user 0m0,068s | ||
sys 0m0,050s | ||
``` | ||
* Bullseye, versión de Go 1.17 | ||
|
||
```shell | ||
Successfully built dafa5fb683e3 | ||
Successfully tagged go_bullseye_1.16:latest | ||
|
||
real 0m5,757s | ||
user 0m0,068s | ||
sys 0m0,046s | ||
``` | ||
|
||
Los resultados son prácticamente iguales, variando a nivel de milésimas. | ||
|
||
### Tamaño de la imagen | ||
Se analiza en espacio que ocupa cada imagen base: | ||
|
||
``` | ||
mywallet stretch 232c3b59a871 About a minute ago 879MB | ||
mywallet alpine cbc92daa90aa 2 minutes ago 351MB | ||
go_alpine_1.16 latest 7f201f405681 337MB | ||
go_bullseye_1.16 latest dafa5fb683e3 954MB | ||
go_bullseye_1.17 latest d2c15c730270 976MB | ||
go_alpine_1.17 latest 8c6c1dda8a53 351MB | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. En general, debéis usar siempre la última versión del sistema operativo, que es la que corrige las vulnerabilidades. Comparar versiones por tamaño no tiene sentido, porque son cosas diferentes. |
||
``` | ||
|
||
Encontramos una diferencia bastante notoria relativa al peso de las imagenes, la imagen Alpine es bastante más ligera que el resto. | ||
Encontramos una diferencia bastante notoria relativa al peso de las imágenes, la imagen Alpine es bastante más ligera que el resto. La más ligera es la de la versión 1.16 de Go, pero en tan solo 14MB. Teniendo en cuenta nuestros requisitos, la imagen de Alpine es una clara candidata para nuestro proyecto. | ||
|
||
Aun no se puede tomar una decisión final, analizando el rendimiento de las imágenes encontré [esta página](https://nickjanetakis.com/blog/benchmarking-debian-vs-alpine-as-a-base-docker-image) donde se realiza un benchmarking entre imágenes Debian y Alpine. La conclusión es que son dos imágenes con las que se obtienen resultados muy similares y que a no ser que se encuentren errores significativos con Alpine por su tamaño y velocidad es más recomendable. | ||
|
||
|
||
### Tiempos de ejecución | ||
|
||
Se ejecutan los contenedores que hemos generado. Los resultados que obtenemos son los siguientes: | ||
|
||
* 1.17 BULLSEYE | ||
|
||
ok MyWallet/internal/mywallet 0.009s | ||
|
||
ok MyWallet/internal/mywallet 0.001s | ||
|
||
Me parecio extraño el primer tiempo, volví a ejecutar y se obtuvieron varias veces el segundo tiempo. | ||
|
||
* 1.17 ALPINE | ||
|
||
ok MyWallet/internal/mywallet 0.001s | ||
|
||
* 1.16 ALPINE | ||
|
||
ok MyWallet/internal/mywallet 0.001s | ||
|
||
* 1.16 BULLSEYE | ||
|
||
ok MyWallet/internal/mywallet 0.001s | ||
|
||
Aun no se puede tomar una decisión final, analizando el rendimiento de las imagenes encontré [esta página](https://nickjanetakis.com/blog/benchmarking-debian-vs-alpine-as-a-base-docker-image) donde se realiza un benchmarking entre imagenes Debian y Alpine. La conclusión es que son dos imagenes con las que se obtienen resultados muy similares y que a no ser que se encuentren errores significativos con Alpine por su tamaño y velocidad es más recomendable. Destacar las desventajas que nos encontramos con Alpine es L que utiliza **musl libc** en lugar de **glibc**, puede llegar a provocar un comportamiento inesperador en nuestra aplicación. En [este artículo](https://news.ycombinator.com/item?id=10782897) se conversa acerca de los problemas que puede traer este tipos de imagenes. | ||
|
||
Por tanto, mi decisión final es que a no ser que se encuentre algún error importante con imagenes Alpine se usará `golang:1.17-alpine` como imagen base para nuestro proyecto. | ||
### Conclusión | ||
|
||
Los tiempos de ejecución y de construcción son prácticamente iguales, la principal diferencia se encuentra en el tamaño de la imagen, en este apartado Alpine sale claro vencedor. Por tanto, mi decisión final es que a no ser que se encuentre algún error importante con imágenes Alpine se usará `golang:1.17-alpine` como imagen base para nuestro proyecto. La versión de Go que he elegido es la 1.17, si es verdad que la versión 1.16 pesaba algo menos, pero me parece algo poco significativo, más significativo me parece que una versión reciba más actualizaciones que la otra, por esto se elije la versión 1.17. | ||
|
||
### Facilitar uso de Docker con nuestro task runner | ||
## Facilitar uso de Docker con nuestro task runner | ||
|
||
Se ha automatizado la ejecución de los tests en el task runner. Esto se consigue añadiendo la siguiente directiva a nuestro Taskfile.yml: | ||
|
||
```docker run -t -v `pwd`:/app/test luisarostegui/mywallet``` | ||
|
||
### Buenas prácticas en nuestro Dockerfile | ||
## Buenas prácticas en nuestro Dockerfile | ||
|
||
* Vamos a usar una imagen ligera (Alpine) para optimizar el tamaño y poder tener el control sobre los paquetes necesarios. | ||
* Usar variables con ENV para directorios de trabajo. | ||
* Ejecutar tanto las instalaciones de las dependencias como el task runner como usuario y no como superusuario. | ||
|
||
### Justificación de directivas en el Dockerfile | ||
## Justificación de directivas en el Dockerfile | ||
|
||
1. Con la directiva FROM especificamos la imagen base. | ||
2. La directiva LABEL la utilizamos para especificar nombre y correo de la persona encargada del Docker. | ||
|
@@ -62,7 +151,7 @@ Se ha automatizado la ejecución de los tests en el task runner. Esto se consigu | |
7. WORKDIR, especificamos la ruta donde queremos trabajar. | ||
8. ENTRYPOINT, indicamos la acción a ejecutar, en este caso `task test`. | ||
|
||
### Comentarios en el Dockerfile | ||
## Comentarios en el Dockerfile | ||
|
||
``` | ||
#Imagen base para docker | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Muy buen trabajo en esta sección, se nota que lo has trabajado este objetivo.