{"id":32526,"date":"2019-10-31T21:47:32","date_gmt":"2019-10-31T18:47:32","guid":{"rendered":"https:\/\/prohoster.info\/blog\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\/"},"modified":"2019-10-31T21:47:32","modified_gmt":"2019-10-31T18:47:32","slug":"protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","status":"publish","type":"post","link":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","title":{"rendered":"Proceso de desarrollo y prueba con Docker y Gitlab CI","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Te propongo que revises la transcripci\u00f3n de la presentaci\u00f3n de Alexander Sigachev de Inventos \"Proceso de desarrollo y pruebas con Docker + Gitlab CI\"<\/strong><\/p>\n<p><\/p>\n<p>Aquellos que reci\u00e9n comienzan a implementar el proceso de desarrollo y pruebas con Docker + Gitlab CI a menudo se hacen preguntas b\u00e1sicas. \u00bfPor d\u00f3nde empezar? \u00bfC\u00f3mo organizarlo? \u00bfC\u00f3mo probar?<\/p>\n<p><\/p>\n<p>Esta presentaci\u00f3n es valiosa porque describe de manera estructurada el proceso de desarrollo y pruebas utilizando Docker y Gitlab CI. El informe es de 2017. Creo que de este informe se pueden extraer fundamentos, metodolog\u00edas, ideas y experiencias de uso. <\/p>\n<p>\n<center><div class=\"youtube-placeholder\" data-id=\"lJsqRwULRVA\" onclick=\"loadVideo(this)\">\r\n        <img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/lJsqRwULRVA\/hqdefault.jpg\" alt=\"Reproducir video\" loading=\"lazy\" width=\"480\" height=\"360\" style=\"width:100%;height:auto;\">\r\n        <div class=\"play-button\"><\/div>\r\n    <\/div><\/center><\/p>\n<p>A quienes les interese, les invito a leer m\u00e1s. <noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p><\/p>\n<p>Me llamo Alexander Sigachev. Trabajo en la empresa Inventos. Compartir\u00e9 mi experiencia utilizando Docker y c\u00f3mo lo estamos implementando gradualmente en los proyectos de la empresa.<\/p>\n<p><\/p>\n<p>Tema de la presentaci\u00f3n: El proceso de desarrollo utilizando Docker y Gitlab CI. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e30c4757e4bda1bcc3e20b16e8e2709e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Esta es mi segunda presentaci\u00f3n sobre Docker. En el momento de mi primera presentaci\u00f3n, utiliz\u00e1bamos Docker solo en desarrollo en las m\u00e1quinas de los desarrolladores. Hab\u00eda alrededor de 2-3 personas que usaban Docker. Con el tiempo, adquirimos experiencia y avanzamos un poco m\u00e1s. Enlace a nuestra <noindex><a rel=\"nofollow\" href=\"https:\/\/www.slideshare.net\/gled\/docker-development-70411088\">primera presentaci\u00f3n<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 habr\u00e1 en esta presentaci\u00f3n? Compartiremos nuestra experiencia sobre los desaf\u00edos que encontramos y c\u00f3mo resolvimos varios problemas. No siempre fue f\u00e1cil, pero nos permiti\u00f3 avanzar.<\/p>\n<p><\/p>\n<p>Nuestro lema: dockeriza todo lo que nuestras manos alcanzan.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25ae4f97f1c502384abfe697bac502b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 problemas resolvemos?<\/p>\n<p><\/p>\n<p>Cuando hay varios equipos en la empresa, un programador se convierte en un recurso compartido. A veces, hay etapas en las que se saca a un programador de un proyecto y se le asigna a otro proyecto por un tiempo.<\/p>\n<p><\/p>\n<p>Para que el programador se adapte r\u00e1pidamente, necesita descargar el c\u00f3digo fuente del proyecto y poner en marcha el entorno lo m\u00e1s pronto posible, lo que le permitir\u00e1 seguir adelante resolviendo las tareas de este proyecto.<\/p>\n<p><\/p>\n<p>Normalmente, si se empieza desde cero, hay poca documentaci\u00f3n en el proyecto. La informaci\u00f3n sobre c\u00f3mo configurarlo solo est\u00e1 en manos de los veteranos. Los empleados configuran su espacio de trabajo por su cuenta en uno o dos d\u00edas. Para acelerar esto, utilizamos Docker.<\/p>\n<p><\/p>\n<p>La siguiente raz\u00f3n es la estandarizaci\u00f3n de las configuraciones en desarrollo. Seg\u00fan mi experiencia, los desarrolladores siempre muestran iniciativa. En uno de cada cinco casos, se introduce un dominio personalizado, por ejemplo, vasya.dev. Al lado est\u00e1 el vecino Petya, que tiene el dominio petya.dev. Desarrollan un sitio o alg\u00fan componente del sistema utilizando este nombre de dominio.<\/p>\n<p><\/p>\n<p>Cuando el sistema se expande y estos nombres de dominio comienzan a aparecer en las configuraciones, surge un conflicto de entornos de desarrollo y se sobrescribe la ruta del sitio.<\/p>\n<p><\/p>\n<p>Lo mismo ocurre con la configuraci\u00f3n de la base de datos. Algunos no se preocupan por la seguridad y trabajan con la contrase\u00f1a vac\u00eda de root. A otros, durante la instalaci\u00f3n, MySQL les pidi\u00f3 una contrase\u00f1a y esta result\u00f3 ser 123. A menudo sucede que la configuraci\u00f3n de la base de datos se modifica constantemente dependiendo del commit del desarrollador. Alguien la corrigi\u00f3, alguien no. Hubo trucos en los que sac\u00e1bamos alguna configuraci\u00f3n de prueba en <code>.gitignore<\/code> y cada desarrollador deb\u00eda instalar la base de datos. Esto complicaba el proceso de inicio. Adem\u00e1s de todo, hay que recordar la base de datos. Hay que inicializar la base de datos, establecer la contrase\u00f1a, definir el usuario, crear la tabla, y as\u00ed sucesivamente.<\/p>\n<p><\/p>\n<p>Otro de los problemas son las diferentes versiones de las bibliotecas. A menudo, el desarrollador trabaja con diferentes proyectos. Hay un proyecto legado que se inici\u00f3 hace cinco a\u00f1os (desde 2017, nota de la redacci\u00f3n). Al principio se empez\u00f3 con MySQL 5.5. Y hay proyectos m\u00e1s modernos, donde tratamos de implementar versiones m\u00e1s actuales de MySQL, como 5.7 o superiores (en 2017, nota de la redacci\u00f3n).<\/p>\n<p><\/p>\n<p>Quien trabaja con MySQL sabe que estas bibliotecas arrastran dependencias. Es bastante problem\u00e1tico ejecutar 2 bases de datos al mismo tiempo. Por lo menos, es complicado conectar a antiguos clientes a la nueva base de datos. Esto, a su vez, genera varios problemas.<\/p>\n<p><\/p>\n<p>El siguiente problema es que, cuando un desarrollador trabaja en su m\u00e1quina local, utiliza recursos locales, archivos locales, RAM local. Toda la interacci\u00f3n durante el desarrollo de soluciones se lleva a cabo bajo el supuesto de que funciona en una sola m\u00e1quina. Un ejemplo puede ser cuando en Producci\u00f3n tenemos 3 servidores backend, y el desarrollador guarda archivos en el directorio ra\u00edz y desde all\u00ed nginx toma los archivos para responder a las solicitudes. Cuando ese c\u00f3digo llega a Producci\u00f3n, resulta que el archivo est\u00e1 presente en uno de los 3 servidores.<\/p>\n<p><\/p>\n<p>Actualmente se est\u00e1 desarrollando el \u00e1mbito de los microservicios. Al dividir nuestras grandes aplicaciones en peque\u00f1os componentes que interact\u00faan entre s\u00ed, se permite seleccionar tecnolog\u00edas adecuadas para un conjunto espec\u00edfico de tareas. Adem\u00e1s, esto permite dividir el trabajo y la responsabilidad entre los desarrolladores.<\/p>\n<p><\/p>\n<p>El desarrollador Frontend, al trabajar con JS, pr\u00e1cticamente no influye en el Backend. El desarrollador Backend, a su vez, desarrolla, en nuestro caso, Ruby on Rails, y no interfiere con el Frontend. La interacci\u00f3n se realiza a trav\u00e9s de API.<\/p>\n<p><\/p>\n<p>Como bonificaci\u00f3n, gracias a Docker hemos logrado optimizar los recursos en Staging. Cada proyecto, debido a su especificidad, requer\u00eda configuraciones particulares. Era necesario destinar un servidor virtual por proyecto y configurarlos por separado, o bien compartir un entorno variable, donde los proyectos podr\u00edan influir entre s\u00ed dependiendo de la versi\u00f3n de las bibliotecas.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/fa5c99f90faef84a356d4791fd915830.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Herramientas. \u00bfQu\u00e9 utilizamos? <\/p>\n<p><\/p>\n<ul>\n<li>El propio Docker. En el Dockerfile se describen las dependencias de una aplicaci\u00f3n. <\/li>\n<li>Docker-compose es una uni\u00f3n que combina varias de nuestras aplicaciones Docker.<\/li>\n<li>Usamos GitLab para almacenar el c\u00f3digo fuente.<\/li>\n<li>GitLab-CI lo utilizamos para la integraci\u00f3n continua.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/b6041827a6ba928e0d33add6b79c581c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La presentaci\u00f3n consta de dos partes.<\/p>\n<p><\/p>\n<p>La primera parte hablar\u00e1 sobre c\u00f3mo ejecutamos Docker en las m\u00e1quinas de los desarrolladores.<\/p>\n<p><\/p>\n<p>La segunda parte hablar\u00e1 sobre c\u00f3mo interactuar con GitLab, c\u00f3mo ejecutamos pruebas y c\u00f3mo implementamos en Staging.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/45746d3323d35e0d18f066c8bfcb2214.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker es una tecnolog\u00eda que permite, utilizando un enfoque declarativo, describir los componentes necesarios. Este es un ejemplo de Dockerfile. Aqu\u00ed anunciamos que heredamos de la imagen oficial de Docker Ruby:2.3.0. Contiene Ruby versi\u00f3n 2.3 instalado. Instalamos las bibliotecas necesarias para la construcci\u00f3n y NodeJS. Describimos que creamos un directorio. <code>\/app<\/code>Designamos el directorio app como el directorio de trabajo. En este directorio colocamos un Gemfile y Gemfile.lock m\u00ednimos necesarios. Luego, ejecutamos la construcci\u00f3n de proyectos, que instala las dependencias de esta imagen. Indicamos que el contenedor estar\u00e1 listo para escuchar en el puerto externo 3000. El \u00faltimo comando es el que inicia nuestra aplicaci\u00f3n. Si ejecutamos el comando para iniciar el proyecto, la aplicaci\u00f3n intentar\u00e1 ejecutarse y lanzar\u00e1 el comando indicado.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ba503c305ae90f49b39f4ae2cbda1668.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Este es un ejemplo m\u00ednimo de un archivo docker-compose. En este caso, mostramos c\u00f3mo se conectan dos contenedores: el servicio de base de datos y el servicio web. Nuestras aplicaciones web, en la mayor\u00eda de los casos, requieren alg\u00fan tipo de base de datos como backend para el almacenamiento de datos. En este caso, usamos MySQL, as\u00ed que el ejemplo es con MySQL, pero no hay nada que impida usar otra base de datos (PostgreSQL, Redis).<\/p>\n<p><\/p>\n<p>Tomamos de la fuente oficial en Docker Hub la imagen de MySQL 5.7.14 sin cambios. La imagen que representa nuestra aplicaci\u00f3n web la construimos desde el directorio actual. Durante el primer lanzamiento, se genera nuestra imagen. Despu\u00e9s, ejecuta el comando que aqu\u00ed realizamos. Si volvemos atr\u00e1s, notaremos que se defini\u00f3 un comando de lanzamiento a trav\u00e9s de Puma. Puma es un servicio escrito en Ruby. En el segundo caso, lo sobrescribimos. Este comando puede ser arbitrario dependiendo de nuestras necesidades o tareas.<\/p>\n<p><\/p>\n<p>Tambi\u00e9n describimos que hay que redirigir el puerto de nuestra m\u00e1quina host de desarrollador del 3000 al puerto 3000 del contenedor. Esto se realiza autom\u00e1ticamente mediante iptables y su mecanismo integrado en Docker. <\/p>\n<p><\/p>\n<p>El desarrollador tambi\u00e9n puede, como antes, dirigirse a cualquier direcci\u00f3n IP disponible, por ejemplo, 127.0.0.1 local o la direcci\u00f3n IP externa de la m\u00e1quina.<\/p>\n<p><\/p>\n<p>La \u00faltima l\u00ednea indica que el contenedor web depende del contenedor db. Cuando llamamos al lanzamiento del contenedor web, previamente docker-compose iniciar\u00e1 nuestra base de datos. Tras el inicio de la base de datos (de hecho, despu\u00e9s de iniciar el contenedor. \u00a1No garantiza la disponibilidad de la BBDD!) se iniciar\u00e1 nuestra aplicaci\u00f3n, nuestro backend.<\/p>\n<p><\/p>\n<p>Esto permite evitar errores cuando la base de datos no est\u00e1 activa y ayuda a ahorrar recursos cuando detenemos el contenedor de la base de datos, liberando as\u00ed recursos para otros proyectos. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4acbcf5de89b98db055e8abc419202de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>El uso de la contenedorizaci\u00f3n de la base de datos en el proyecto nos proporciona una versi\u00f3n fija de MySQL para todos los desarrolladores. Esto ayuda a evitar ciertos errores que pueden surgir debido a discrepancias entre versiones, cuando cambian la sintaxis, la configuraci\u00f3n y los valores predeterminados. Permite especificar nombres de host comunes para la base de datos, el inicio de sesi\u00f3n y la contrase\u00f1a. Nos alejamos de la confusi\u00f3n de nombres y conflictos en los archivos de configuraci\u00f3n que exist\u00edan anteriormente. <\/p>\n<p><\/p>\n<p>Tenemos la posibilidad de utilizar una configuraci\u00f3n m\u00e1s \u00f3ptima para el entorno de desarrollo, que ser\u00e1 diferente de la predeterminada. MySQL, por defecto, est\u00e1 configurado para m\u00e1quinas de bajo rendimiento y su rendimiento es muy bajo de forma predeterminada.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/417d77a06b18e2df9b71440c0f2a1653.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker permite utilizar el int\u00e9rprete de Python, Ruby, NodeJS, PHP en la versi\u00f3n necesaria. Nos libramos de la necesidad de usar un gestor de versiones. Anteriormente, us\u00e1bamos un paquete rpm para Ruby, que permit\u00eda cambiar la versi\u00f3n seg\u00fan el proyecto. Tambi\u00e9n permite, gracias al contenedor Docker, migrar el c\u00f3digo y versionarlo junto con sus dependencias de manera fluida. No tenemos problemas para entender la versi\u00f3n tanto del int\u00e9rprete como del c\u00f3digo. Para actualizar la versi\u00f3n, es necesario eliminar el contenedor viejo y levantar un nuevo contenedor. Si algo sale mal, podemos eliminar el nuevo contenedor y levantar el viejo.<\/p>\n<p><\/p>\n<p>Despu\u00e9s de construir la imagen, los contenedores ser\u00e1n id\u00e9nticos tanto en Desarrollo como en Producci\u00f3n. Esto es especialmente relevante para instalaciones grandes.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/185a1bfe257f7e3e0eecdc6e0e0d330f.jpg\" style=\"display:block;margin: 0 auto;\" \/> En el Frontend usamos JavaScript y NodeJS.<\/p>\n<p><\/p>\n<p>Ahora, nuestro \u00faltimo proyecto es con ReactJS. El desarrollador ejecut\u00f3 todos los contenedores y desarroll\u00f3 usando hot-reload.<\/p>\n<p><\/p>\n<p>A continuaci\u00f3n, se lanza la tarea para compilar JavaScript y el c\u00f3digo compilado en est\u00e1tico se sirve a trav\u00e9s de nginx, ahorrando recursos.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4c2e28319d56d7fc81845b4133a1bfe6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Aqu\u00ed he presentado un esquema de nuestro \u00faltimo proyecto.<\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 problemas resolvimos? Surgi\u00f3 la necesidad de construir un sistema con el que interact\u00faan dispositivos m\u00f3viles. Ellos reciben datos. Una de las capacidades es enviar notificaciones push a estos dispositivos. <\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 hicimos para esto?<\/p>\n<p><\/p>\n<p>Dividimos la aplicaci\u00f3n en componentes como: parte administrativa en JS, backend que trabaja a trav\u00e9s de la interfaz REST bajo Ruby on Rails. El backend interact\u00faa con la base de datos. El resultado que se genera se env\u00eda al cliente. La administraci\u00f3n interact\u00faa con el backend y la base de datos a trav\u00e9s de la interfaz REST.<\/p>\n<p><\/p>\n<p>Tambi\u00e9n tuvimos la necesidad de enviar notificaciones push. Antes, ten\u00edamos un proyecto en el que se implement\u00f3 un mecanismo que se encargaba de la entrega de notificaciones en plataformas m\u00f3viles. <\/p>\n<p><\/p>\n<p>Desarrollamos el siguiente esquema: el operador interact\u00faa con la administraci\u00f3n desde el navegador, la administraci\u00f3n interact\u00faa con el backend, se establece la tarea de enviar notificaciones push.<\/p>\n<p><\/p>\n<p>Las notificaciones push interact\u00faan con otro componente, que est\u00e1 implementado en NodeJS.<\/p>\n<p><\/p>\n<p>Se construyen colas y luego el env\u00edo de notificaciones contin\u00faa de acuerdo con su propio mecanismo.<\/p>\n<p><\/p>\n<p>Aqu\u00ed se dibujan dos bases de datos. En este momento, estamos utilizando 2 bases de datos independientes a trav\u00e9s de Docker, que no est\u00e1n conectadas entre s\u00ed. Adem\u00e1s de que comparten una red virtual, los datos f\u00edsicos se almacenan en diferentes directorios en la m\u00e1quina del desarrollador.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/dfde59db7e6886d2d851dcf884049318.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lo mismo pero en cifras. Aqu\u00ed es importante la reutilizaci\u00f3n del c\u00f3digo.<\/p>\n<p><\/p>\n<p>Si antes habl\u00e1bamos de la reutilizaci\u00f3n del c\u00f3digo en forma de bibliotecas, en este ejemplo nuestro servicio que maneja las notificaciones Push se reutiliza como un servidor completo. Proporciona una API. Nuestra nueva aplicaci\u00f3n interact\u00faa con \u00e9l.<\/p>\n<p><\/p>\n<p>En ese momento, utiliz\u00e1bamos la versi\u00f3n 4 de NodeJS. Ahora (en 2017 \u2014 nota del editor) en desarrollos recientes usamos la versi\u00f3n 7 de NodeJS. No hay problemas al atraer nuevas versiones de bibliotecas en nuevos componentes. <\/p>\n<p><\/p>\n<p>Si es necesario, se puede realizar una refactorizaci\u00f3n y actualizar la versi\u00f3n de NodeJS en el servicio de notificaciones Push. <\/p>\n<p><\/p>\n<p>Y si podemos mantener la compatibilidad a trav\u00e9s de la API, entonces podemos reemplazarlo en otros proyectos que se usaron anteriormente.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5c58c65be078a5a1b9fc17ee2e7b4ad4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 se necesita para agregar Docker? A\u00f1adimos a nuestro repositorio un Dockerfile que describe las dependencias necesarias. En este ejemplo, los componentes est\u00e1n divididos l\u00f3gicamente. Es un conjunto m\u00ednimo para un desarrollador backend.<\/p>\n<p><\/p>\n<p>Al crear un nuevo proyecto, creamos un Dockerfile y describimos el ecosistema necesario (Python, Ruby, NodeJS). En docker-compose se describe la dependencia necesaria: la base de datos. Indicamos que se necesita una base de cierta versi\u00f3n y que almacene los datos en tal lugar.<\/p>\n<p><\/p>\n<p>Usamos un tercer contenedor separado con nginx para servir est\u00e1ticos. Se prev\u00e9 la posibilidad de cargar im\u00e1genes. El backend las coloca en un volumen previamente preparado, que tambi\u00e9n est\u00e1 montado en el contenedor con nginx, que sirve los est\u00e1ticos.<\/p>\n<p><\/p>\n<p>Para almacenar la configuraci\u00f3n de nginx y mysql, a\u00f1adimos una carpeta Docker, en la que guardamos las configuraciones necesarias. Cuando un desarrollador clona el repositorio en su m\u00e1quina, ya recibe un proyecto listo para el desarrollo local. No hay dudas sobre qu\u00e9 puerto o configuraciones aplicar.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e7a6b308a75cacf8ce60687a9e6081dd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A continuaci\u00f3n, tenemos varios componentes: administraci\u00f3n, inform-API, notificaciones push.<\/p>\n<p><\/p>\n<p>Para poner todo esto en marcha, hemos creado otro repositorio, al que llamamos dockerized-app. En este momento, estamos utilizando varios repositorios para cada componente. Se diferencian l\u00f3gicamente: en GitLab se presenta como una carpeta, mientras que en la m\u00e1quina del desarrollador es una carpeta para un proyecto espec\u00edfico. En un nivel inferior se encuentran los componentes que se van a integrar.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f4e2c45c8ec20fca0bac74b3a3892543.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Este es un ejemplo del contenido de dockerized-app. Tambi\u00e9n hemos sacado aqu\u00ed el directorio Docker, donde a\u00f1adimos las configuraciones necesarias para la interacci\u00f3n de todos los componentes. Hay un README.md que describe brevemente c\u00f3mo ejecutar el proyecto.<\/p>\n<p><\/p>\n<p>Aqu\u00ed hemos utilizado dos archivos docker-compose. Esto se hace para tener la posibilidad de iniciar de forma escalonada. Cuando un desarrollador trabaja con el n\u00facleo, si no necesita notificaciones push, simplemente ejecuta el archivo docker-compose, lo que ahorra recursos.<\/p>\n<p><\/p>\n<p>Si es necesario integrar con notificaciones push, entonces se ejecutan docker-compose.yaml y docker-compose-push.yaml.<\/p>\n<p><\/p>\n<p>Dado que docker-compose.yaml y docker-compose-push.yaml est\u00e1n en la carpeta, se crea autom\u00e1ticamente una red virtual unificada.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/535019c82c72c1602176fbaf4aa5640f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Descripci\u00f3n de los componentes. Este es un archivo m\u00e1s detallado, que se encarga de compilar componentes. \u00bfQu\u00e9 es notable aqu\u00ed? Aqu\u00ed introducimos el componente equilibrador.<\/p>\n<p><\/p>\n<p>Esta es una imagen de Docker lista, donde se ejecuta nginx y una aplicaci\u00f3n que escucha el socket de Docker. A medida que se activan y desactivan los contenedores, se regenera din\u00e1micamente la configuraci\u00f3n de nginx. Nos dirigimos a los componentes a trav\u00e9s de nombres de dominio de tercer nivel.<\/p>\n<p><\/p>\n<p>Para un entorno de desarrollo, utilizamos el dominio .dev \u2014 api.informer.dev. Las aplicaciones con el dominio .dev son accesibles desde la m\u00e1quina local del desarrollador.<\/p>\n<p><\/p>\n<p>Luego, se transferir\u00e1n las configuraciones a cada proyecto y se iniciar\u00e1n todos los proyectos simult\u00e1neamente.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/bc1e11e6f1fd1c837d2c8087d04145c4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Si lo representamos gr\u00e1ficamente, el cliente es nuestro navegador o alguna herramienta que utilizamos para hacer solicitudes al equilibrador.<\/p>\n<p><\/p>\n<p>El equilibrador determina, por el nombre de dominio, a qu\u00e9 contenedor se debe dirigir la solicitud.<\/p>\n<p><\/p>\n<p>Esto puede ser nginx, que entrega JS de las interfaces administrativas, o puede ser nginx, que entrega API o archivos est\u00e1ticos que son servidos por nginx en forma de carga de im\u00e1genes.<\/p>\n<p><\/p>\n<p>En el esquema se puede ver que los contenedores est\u00e1n unidos en una red virtual y est\u00e1n ocultos detr\u00e1s de un proxy.<\/p>\n<p><\/p>\n<p>En la m\u00e1quina del desarrollador, se puede acceder al contenedor conociendo la IP, pero en principio no lo aplicamos. Pr\u00e1cticamente no surge la necesidad de un acceso directo.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/2908c31fd9d3c79f6189bdbeeb6d028f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 ejemplo ver para dockerizar mi aplicaci\u00f3n? En mi opini\u00f3n, un buen ejemplo es la imagen oficial de Docker para MySQL.<\/p>\n<p><\/p>\n<p>Es bastante complicada. Hay muchas versiones. Pero su funcionalidad cubre muchas necesidades que pueden surgir durante el desarrollo posterior. Si dedicas tiempo a entender c\u00f3mo interact\u00faa todo esto, creo que no tendr\u00e1s problemas implement\u00e1ndolo por tu cuenta.<\/p>\n<p><\/p>\n<p>En hub.docker.com, generalmente hay enlaces a github.com, donde se encuentran los datos en bruto que se pueden utilizar para construir la imagen por s\u00ed mismo.<\/p>\n<p><\/p>\n<p>A continuaci\u00f3n, en este repositorio hay un script docker-endpoint.sh, que se encarga de la inicializaci\u00f3n inicial y del procesamiento posterior del lanzamiento de la aplicaci\u00f3n.<\/p>\n<p><\/p>\n<p>Tambi\u00e9n en este ejemplo hay una opci\u00f3n de configuraci\u00f3n a trav\u00e9s de variables de entorno. Al definir variables de entorno al iniciar un contenedor individual o a trav\u00e9s de docker-compose, podemos indicar que necesitamos establecer una contrase\u00f1a vac\u00eda para docker para root en MySQL o alguna otra que deseemos.<\/p>\n<p><\/p>\n<p>Hay una opci\u00f3n para crear una contrase\u00f1a aleatoria. Decimos que necesitamos un usuario, que se requiere establecer una contrase\u00f1a para el usuario y que necesitamos crear una base de datos.<\/p>\n<p><\/p>\n<p>En nuestros proyectos, unificamos un poco el Dockerfile que se encarga de la inicializaci\u00f3n. Lo ajustamos a nuestras necesidades para simplemente extender los derechos del usuario que utiliza la aplicaci\u00f3n. Esto permiti\u00f3 crear f\u00e1cilmente una base de datos desde la consola de la aplicaci\u00f3n. En aplicaciones Ruby, hay comandos para crear, modificar y eliminar bases de datos.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4a9b56e21819d28dd39753134d23fee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Este es un ejemplo de c\u00f3mo se ve una versi\u00f3n espec\u00edfica de MySQL en github.com. Se puede abrir el Dockerfile y ver c\u00f3mo se realiza la instalaci\u00f3n.<\/p>\n<p><\/p>\n<p>docker-endpoint.sh es el script que responde por el punto de entrada. Durante la inicializaci\u00f3n inicial, se requieren ciertas acciones de preparaci\u00f3n y todas estas acciones se han extra\u00eddo al script de inicializaci\u00f3n.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4153fa46526f3d802931dd1db48b08cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Pasemos a la segunda parte.<\/p>\n<p><\/p>\n<p>Para almacenar el c\u00f3digo fuente, hemos pasado a gitlab. Es un sistema bastante potente, que tiene una interfaz visual.<\/p>\n<p><\/p>\n<p>Uno de los componentes de Gitlab es Gitlab CI. Permite describir una secuencia de comandos que luego se utilizar\u00e1n para organizar el sistema de entrega de c\u00f3digo o ejecutar pruebas autom\u00e1ticas.<\/p>\n<p><\/p>\n<p>Informe sobre Gitlab CI 2 <noindex><a rel=\"nofollow\" href=\"https:\/\/goo.gl\/uohKjI\">https:\/\/goo.gl\/uohKjI<\/a><\/noindex> \u2014 informe del club Ruby Russia \u2014 es bastante detallado y puede interesarte.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5d26d20500f34eeb9768e6c3d91bbefa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ahora veremos qu\u00e9 se requiere para activar Gitlab CI. Para iniciar Gitlab CI, solo necesitamos colocar un archivo .gitlab-ci.yml en la ra\u00edz del proyecto.<\/p>\n<p><\/p>\n<p>Aqu\u00ed describimos lo que queremos hacer: una secuencia de estados como pruebas y despliegues.<\/p>\n<p><\/p>\n<p>Ejecutamos scripts que llaman directamente a docker-compose para construir nuestra aplicaci\u00f3n. Este es un ejemplo justamente del backend.<\/p>\n<p><\/p>\n<p>Luego indicamos que es necesario ejecutar las migraciones para cambiar la base de datos y realizar pruebas.<\/p>\n<p><\/p>\n<p>Si los scripts se ejecutan correctamente y no regresan un c\u00f3digo de error, entonces el sistema pasa a la segunda etapa de despliegue.<\/p>\n<p><\/p>\n<p>La etapa de despliegue actualmente est\u00e1 implementada para staging. No hemos organizado reinicios sin tiempo de inactividad.<\/p>\n<p><\/p>\n<p>Forzamos el apagado de todos los contenedores y luego levantamos de nuevo todos los contenedores, construidos en la primera etapa durante las pruebas.<\/p>\n<p><\/p>\n<p>Ejecutamos las migraciones de base de datos actuales que fueron escritas por los desarrolladores.<\/p>\n<p><\/p>\n<p>Hay una nota que aplicar esto solo para la rama master.<\/p>\n<p><\/p>\n<p>No se ejecuta al cambiar otras ramas.<\/p>\n<p><\/p>\n<p>Hay la posibilidad de organizar despliegues por ramas.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/8a5b880bd9a0d4bab0b276568e2ba413.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Para organizar esto m\u00e1s adelante, necesitamos instalar Gitlab Runner.<\/p>\n<p><\/p>\n<p>Esta herramienta est\u00e1 escrita en Golang. Es un solo archivo, como es habitual en el mundo de Golang, que no requiere dependencias.<\/p>\n<p><\/p>\n<p>Al iniciar, registramos Gitlab Runner.<\/p>\n<p><\/p>\n<p>Obtenemos en la interfaz web de Gitlab una clave.<\/p>\n<p><\/p>\n<p>Luego llamamos al comando de inicializaci\u00f3n en la l\u00ednea de comandos.<\/p>\n<p><\/p>\n<p>Configuramos Gitlab Runner en modo di\u00e1logo (Shell, Docker, VirtualBox, SSH)<\/p>\n<p><\/p>\n<p>El c\u00f3digo en Gitlab Runner se ejecutar\u00e1 en cada commit seg\u00fan la configuraci\u00f3n de .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/37c8ac7499f0cfba2eb07a84083e44e1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>As\u00ed es como se ve visualmente en Gitlab en la interfaz web. Despu\u00e9s de conectar Gitlab CI, aparece una bandera que indica en qu\u00e9 estado est\u00e1 el build en ese momento.<\/p>\n<p><\/p>\n<p>Vemos que hace 4 minutos se realiz\u00f3 un commit que pas\u00f3 todas las pruebas y no caus\u00f3 problemas.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/33ac79c45f0651a5f8bde75092c4ac44.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Podemos examinar m\u00e1s a fondo las compilaciones. Aqu\u00ed vemos que ya hemos pasado por dos estados. Estado de prueba y estado de despliegue en staging.<\/p>\n<p><\/p>\n<p>Si hacemos clic en una compilaci\u00f3n espec\u00edfica, all\u00ed habr\u00e1 una salida de consola de los comandos que se ejecutaron durante el proceso seg\u00fan .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d26dd28708c7fc8a409af4be4d48f502.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>As\u00ed es como se ve el historial de nuestro producto. Vemos que hubo intentos exitosos. Cuando las pruebas fallan, no se avanza al siguiente paso y el c\u00f3digo en staging no se actualiza.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ee7bd81dc283c7af0c194779d4230780.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 problemas resolvimos en staging cuando implementamos Docker? Nuestro sistema est\u00e1 compuesto por componentes y surgi\u00f3 la necesidad de reiniciar solo una parte de los componentes que fueron actualizados en el repositorio, en lugar de todo el sistema.<\/p>\n<p><\/p>\n<p>Para ello, tuvimos que organizar todo en carpetas separadas.<\/p>\n<p><\/p>\n<p>Despu\u00e9s de hacerlo, tuvimos un problema en que Docker-compose crea un espacio de red para cada carpeta y no ve los componentes vecinos.<\/p>\n<p><\/p>\n<p>Para solucionarlo, creamos una red en Docker manualmente. En Docker-compose especificamos que usara esa red para este proyecto.<\/p>\n<p><\/p>\n<p>As\u00ed, cada componente que se inicia con esta red puede ver componentes en otras partes del sistema.<\/p>\n<p><\/p>\n<p>El siguiente problema es la separaci\u00f3n de staging entre varios proyectos.<\/p>\n<p><\/p>\n<p>Para que todo se vea bien y lo m\u00e1s parecido posible a producci\u00f3n, es recomendable usar los puertos 80 o 443, que se utilizan com\u00fanmente en la WEB.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f863cf1833271bf132455a70ad7d2706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00bfC\u00f3mo lo resolvimos? Asignamos un Gitlab Runner a todos los grandes proyectos.<\/p>\n<p><\/p>\n<p>Gitlab permite ejecutar varios Gitlab Runners distribuidos, que simplemente tomar\u00e1n todas las tareas de manera aleatoria y las ejecutar\u00e1n.<\/p>\n<p><\/p>\n<p>Para evitar el caos, limitamos el grupo de nuestros proyectos a un solo Gitlab Runner, que maneja nuestras cargas sin problemas.<\/p>\n<p><\/p>\n<p>Movimos nginx-proxy a un script de inicio separado y especificamos las redes de todos los proyectos en \u00e9l. <\/p>\n<p><\/p>\n<p>Nuestro proyecto tiene una red, y el balanceador tiene varias redes con nombres de proyectos. Puede dirigir el tr\u00e1fico seg\u00fan los nombres de dominio.<\/p>\n<p><\/p>\n<p>Las solicitudes llegan a trav\u00e9s del dominio en el puerto 80 y se redirigen a un grupo de contenedores que atiende ese dominio.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25c769f029b272a17fd06bbe60cf0ea0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00bfCu\u00e1les fueron otros problemas? Todos los contenedores se ejecutan por defecto como el usuario root. Este root no es el mismo root del sistema host.<\/p>\n<p><\/p>\n<p>Sin embargo, si ingresas al contenedor, estar\u00e1s como root y el archivo que creamos en ese contenedor tiene permisos de root.<\/p>\n<p><\/p>\n<p>Si un desarrollador ingres\u00f3 al contenedor y ejecut\u00f3 comandos que generaron archivos, luego sali\u00f3 del contenedor, en su directorio de trabajo tiene un archivo al que no tiene acceso.<\/p>\n<p><\/p>\n<p>\u00bfC\u00f3mo se puede resolver esto? Se pueden agregar usuarios que estar\u00e1n en el contenedor.<\/p>\n<p><\/p>\n<p>\u00bfQu\u00e9 problemas surgieron cuando agregamos un usuario?<\/p>\n<p><\/p>\n<p>Al crear un usuario, a menudo no coinciden el ID de grupo (UID) y el ID de usuario (GID).<\/p>\n<p><\/p>\n<p>Para resolver este problema en el contenedor, utilizamos usuarios con ID 1000.<\/p>\n<p><\/p>\n<p>En nuestro caso, esto coincide con el hecho de que pr\u00e1cticamente todos los desarrolladores utilizan el sistema operativo Ubuntu. Y en Ubuntu, el primer usuario tiene ID 1000.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d4911374e4953fdcd807c387efeafec3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00bfCu\u00e1les son nuestros planes?<\/p>\n<p><\/p>\n<p>Leer la documentaci\u00f3n sobre Docker. El proyecto se est\u00e1 desarrollando activamente, la documentaci\u00f3n cambia. Los datos que se obtuvieron hace dos o tres meses ya est\u00e1n empezando a quedar obsoletos. <\/p>\n<p><\/p>\n<p>Parte de los problemas que resolvimos ya podr\u00edan estar solucionados por medios est\u00e1ndar.<\/p>\n<p><\/p>\n<p>Realmente quiero avanzar y pasar directamente a la orquestaci\u00f3n.<\/p>\n<p><\/p>\n<p>Uno de los ejemplos es el mecanismo integrado en Docker llamado Docker Swarm, que se ofrece de forma predeterminada. Me gustar\u00eda lanzar algo en producci\u00f3n basado en la tecnolog\u00eda de Docker Swarm.<\/p>\n<p><\/p>\n<p>La creaci\u00f3n de contenedores complica el trabajo con los registros. Actualmente, los registros est\u00e1n aislados. Est\u00e1n dispersos por los contenedores. Una de las tareas es facilitar el acceso a los registros a trav\u00e9s de una interfaz web.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Proceso de desarrollo y prueba con Docker y Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/cff8c72dd8af7de1fa0227601bd1d69c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Fuente: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/449742\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &#171;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&#187; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u0431\u0430\u0437\u0435 Docker + Gitlab CI \u0447\u0430\u0441\u0442\u043e \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b. \u0421 \u0447\u0435\u0433\u043e \u043d\u0430\u0447\u0430\u0442\u044c? \u041a\u0430\u043a \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c? \u041a\u0430\u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c? \u042d\u0442\u043e\u0442 \u0434\u043e\u043a\u043b\u0430\u0434 \u0445\u043e\u0440\u043e\u0448 \u0442\u0435\u043c, \u0447\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":24326,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-32526","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"es_ES\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker \u0438 Gitlab CI | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:47:32+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:47:32+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Proceso de desarrollo y pruebas con Docker y Gitlab CI | ProHoster","description":"Propongo revisar la transcripci\u00f3n de la presentaci\u00f3n de Alexander Sigachev de Inventos \"El proceso de desarrollo y pruebas con Docker + Gitlab CI\" para aquellos que reci\u00e9n comienzan a implementar el proceso de desarrollo.","canonical_url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"es_ES","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker \u0438 Gitlab CI | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0421\u0438\u0433\u0430\u0447\u0435\u0432\u0430 \u0438\u0437 Inventos &quot;\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 Docker + Gitlab CI&quot; \u0422\u0435, \u043a\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438.","og:url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:47:32+00:00","article:modified_time":"2019-10-31T18:47:32+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"32526","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 11:18:24","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:57:22","updated":"2026-01-21 11:18:24","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/32526","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=32526"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/32526\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/24326"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=32526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=32526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=32526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}