{"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\/en\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","title":{"rendered":"Development and testing process with Docker and Gitlab CI","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>I invite you to review the transcript of Alexander Sigachev's report from Inventos \"The Process of Development and Testing with Docker + Gitlab CI\"<\/strong><\/p>\n<p><\/p>\n<p>Those who are just starting to implement the development and testing process based on Docker + Gitlab CI often ask basic questions. Where to start? How to organize? How to test?<\/p>\n<p><\/p>\n<p>This report is valuable because it provides a structured overview of the development and testing process using Docker and Gitlab CI. The report dates back to 2017. I believe it offers fundamental principles, a methodology, ideas, and practical experience. <\/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=\"Play 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>For those interested, please read on. <noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p><\/p>\n<p>My name is Alexander Sigachev. I work at Inventos. I will share my experience using Docker and how we are gradually implementing it in our projects.<\/p>\n<p><\/p>\n<p>The topic of the report: The Development Process Using Docker and Gitlab CI. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e30c4757e4bda1bcc3e20b16e8e2709e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This is my second report on Docker. At the time of my first report, we were only using Docker in Development on developers' machines. The number of employees using Docker was around 2-3 people. Gradually, we gained experience and progressed a bit further. Here is a link to our <noindex><a rel=\"nofollow\" href=\"https:\/\/www.slideshare.net\/gled\/docker-development-70411088\">first report.<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>What will be covered in this report? We will share our experiences, discussing the pitfalls we've encountered and how we resolved various problems. It wasn't always pretty, but it helped us move forward.<\/p>\n<p><\/p>\n<p>Our motto: Dockerize everything within reach.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25ae4f97f1c502384abfe697bac502b1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>What problems are we solving?<\/p>\n<p><\/p>\n<p>When there are several teams in a company, a developer is often a shared resource. There are times when a programmer is pulled from one project and assigned to another for a period.<\/p>\n<p><\/p>\n<p>For a developer to quickly get familiarized, they need to download the project source code and launch the environment as quickly as possible to start solving tasks related to the project.<\/p>\n<p><\/p>\n<p>Usually, if starting from scratch, there is little documentation in the project. Information about setup exists only with the veterans. Employees usually configure their workstations within one or two days. To speed this up, we implemented Docker.<\/p>\n<p><\/p>\n<p>The next reason is the standardization of settings in Development. In my experience, developers are always proactive. In one out of five cases, a custom domain is introduced, for example, vasya.dev. Nearby, there is a colleague, Petya, with the domain petya.dev. They develop a website or some component of the system using this domain name.<\/p>\n<p><\/p>\n<p>When the system grows and these domain names start to appear in configurations, a conflict arises in the Development environments, and the website path gets overwritten.<\/p>\n<p><\/p>\n<p>The same happens with database settings. Some people don\u2019t care about security and work with an empty root password. Others, during the MySQL installation, were required to set a password, and it ended up being just '123'. Often, the database configuration changes continuously depending on the developer's commit. Some fix it, some do not. There were tricky solutions when we would take some test configuration and <code>.gitignore<\/code> each developer had to set up the database. This complicated the startup process. In addition to everything else, one needs to remember about the database. The database needs to be initialized, a password should be set, a user must be created, a table should be created, and so on.<\/p>\n<p><\/p>\n<p>Another issue is different versions of libraries. It often happens that a developer works on various projects. There is a legacy project that started five years ago (from 2017 \u2014 editor's note). At the beginning, it started with MySQL 5.5. There are also modern projects where we try to implement more recent versions of MySQL, such as 5.7 or newer (as of 2017 \u2014 editor's note).<\/p>\n<p><\/p>\n<p>Those who work with MySQL know that these libraries come with dependencies. It is quite problematic to run two databases together. At the very least, it is difficult to connect old clients to the new database. This, in turn, creates several problems.<\/p>\n<p><\/p>\n<p>The next problem arises when a developer works on a local machine; they use local resources, local files, and local RAM. All interactions during the development of solutions occur within the confines of a single machine. For example, we might have three backend servers in Production, but the developer saves files in the root directory, from where nginx retrieves files to respond to requests. When such code goes into Production, the file only exists on one of the three servers.<\/p>\n<p><\/p>\n<p>The direction of microservices is currently evolving. We break down our large applications into smaller, interacting components. This allows us to select technologies tailored to specific task stacks. It also helps distribute work and areas of responsibility among developers.<\/p>\n<p><\/p>\n<p>A frontend developer, while working in JS, has little impact on the backend. The backend developer, in our case, develops using Ruby on Rails and does not interfere with the frontend. Interaction occurs via API.<\/p>\n<p><\/p>\n<p>As a bonus, we managed to utilize resources on Staging through Docker. Each project, due to its specifics, required certain configurations. Physically, we needed to allocate either a virtual server for each project and configure them separately, or share a variable environment where projects could influence one another depending on the versions of libraries.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/fa5c99f90faef84a356d4791fd915830.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Tools. What are we using? <\/p>\n<p><\/p>\n<ul>\n<li>Docker itself. The Dockerfile outlines the dependencies of a single application. <\/li>\n<li>Docker Compose is the link that brings together several of our Docker applications.<\/li>\n<li>We use GitLab for storing source code.<\/li>\n<li>We use GitLab-CI for continuous integration.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/b6041827a6ba928e0d33add6b79c581c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>The presentation consists of two parts.<\/p>\n<p><\/p>\n<p>The first part will discuss how we ran Docker on developers' machines.<\/p>\n<p><\/p>\n<p>The second part will explain how to interact with GitLab, how we run tests, and how we deploy to Staging.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/45746d3323d35e0d18f066c8bfcb2214.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker is a technology that allows us to describe the necessary components using a declarative approach. This is an example of a Dockerfile. Here we declare that we inherit from the official Docker image Ruby:2.3.0. It contains the installed Ruby version 2.3. We install the required build libraries and NodeJS. We describe that we are creating a directory. <code>\/app<\/code>We set the app directory as the working directory. In this directory, we place the necessary minimal Gemfile and Gemfile.lock. Then, we build the projects that install this dependency image. We specify that the container will be ready to listen on the external port 3000. The last command is the one that directly starts our application. If we execute the project start command, the application will attempt to run and execute the specified command.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ba503c305ae90f49b39f4ae2cbda1668.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This is a minimal example of a docker-compose file. In this case, we show how two containers are connected. This refers directly to the database service and the web service. In most cases, our web applications require a database as a backend for data storage. Since we are using MySQL, this example utilizes MySQL, but there's nothing preventing the use of another database such as PostgreSQL or Redis.<\/p>\n<p><\/p>\n<p>We are taking the MySQL 5.7.14 image without modifications from the official Docker Hub source. The image responsible for our web application is built from the current directory. It builds the image upon the first launch, after which it executes the command provided here. If we go back, we will see that a startup command was defined via Puma. Puma is a service written in Ruby. In the second case, we override this command. This command can be arbitrary depending on our needs or tasks.<\/p>\n<p><\/p>\n<p>We also describe the need to forward the port on our developer host machine from 3000 to the container's port 3000. This is performed automatically using iptables and a mechanism built directly into Docker. <\/p>\n<p><\/p>\n<p>The developer can also, as before, access any available IP address, for example, 127.0.0.1 for local access or the external IP address of the machine.<\/p>\n<p><\/p>\n<p>The last line indicates that the web container depends on the db container. When we trigger the web container's startup, docker-compose will first start the database for us. After the database starts (in fact, after the container is launched! Database readiness is not guaranteed), it will launch our application, our backend.<\/p>\n<p><\/p>\n<p>This helps avoid errors when the database is not up and allows for resource savings when we stop the database container, thereby freeing up resources for other projects. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4acbcf5de89b98db055e8abc419202de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>What does using database containerization bring to the project? We fix the version of MySQL for all developers. This helps avoid some errors that may arise from version mismatches, where syntax, configuration, and default settings change. It allows us to specify common hostnames for the database, login, and password. We move away from the chaos of naming and configuration file conflicts that existed before. <\/p>\n<p><\/p>\n<p>We have the opportunity to use a more optimized config for the Development environment, which will differ from the default. MySQL is set up by default for weak machines, and its performance out of the box is very low.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/417d77a06b18e2df9b71440c0f2a1653.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Docker allows us to use the required version of the Python, Ruby, NodeJS, and PHP interpreters. We eliminate the need to use a version manager. Previously, we used an RPM package for Ruby that allowed switching versions depending on the project. Additionally, Docker containers enable us to smoothly migrate code and version it along with dependencies. We do not face issues understanding the versions of both the interpreter and the code. To upgrade a version, we need to bring down the old container and spin up a new one. If something goes wrong, we can drop the new container and bring back the old one.<\/p>\n<p><\/p>\n<p>After building the image, containers in both Development and Production will be identical. This is particularly relevant for large installations.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/185a1bfe257f7e3e0eecdc6e0e0d330f.jpg\" style=\"display:block;margin: 0 auto;\" \/> On the Frontend, we use JavaScript and NodeJS.<\/p>\n<p><\/p>\n<p>Our latest project is on ReacJS. The developer launched all the containers and developed using hot-reload.<\/p>\n<p><\/p>\n<p>Next, a task is triggered to build JavaScript, and the resulting static code is served via Nginx, saving resources.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4c2e28319d56d7fc81845b4133a1bfe6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Here I presented a diagram of our latest project.<\/p>\n<p><\/p>\n<p>What tasks were we addressing? We needed to build a system that interacts with mobile devices. They receive data. One of the capabilities is to send push notifications to these devices. <\/p>\n<p><\/p>\n<p>What did we do for this?<\/p>\n<p><\/p>\n<p>We divided the application into components such as: the admin part in JS, backend that operates via a REST interface using Ruby on Rails. The backend interacts with the database. The result generated is sent to the client. The admin panel interacts with the backend and the database via the REST interface.<\/p>\n<p><\/p>\n<p>We also needed to send Push notifications. Previously, we had a project that implemented a mechanism responsible for delivering notifications to mobile platforms. <\/p>\n<p><\/p>\n<p>We devised a scheme: an operator in the browser interacts with the admin panel, the admin panel interacts with the backend, and a task is set to send Push notifications.<\/p>\n<p><\/p>\n<p>Push notifications interact with another component implemented in NodeJS.<\/p>\n<p><\/p>\n<p>Queues are built and the notifications are sent according to their mechanism.<\/p>\n<p><\/p>\n<p>Here are two databases depicted. Currently, we use 2 independent databases via Docker, which are not connected to each other, aside from sharing a common virtual network, while the physical data are stored in different directories on the developer's machine.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/dfde59db7e6886d2d851dcf884049318.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>The same thing but in numbers. Code reusability is important here.<\/p>\n<p><\/p>\n<p>While we previously discussed code reuse in the form of libraries, in this example, our service responsible for Push notifications is reused as a complete server. It provides an API, which our new development interacts with.<\/p>\n<p><\/p>\n<p>At that time, we were using NodeJS version 4. Now (in 2017 \u2014 editor's note), we are using version 7 in new developments. There are no issues in newer components adopting new library versions. <\/p>\n<p><\/p>\n<p>If necessary, we can refactor and upgrade the NodeJS version of the Push notification service. <\/p>\n<p><\/p>\n<p>And if we can maintain API compatibility, it can be replaced in other projects previously utilized.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5c58c65be078a5a1b9fc17ee2e7b4ad4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>What do we need to add Docker? We add a Dockerfile to our repository that describes the necessary dependencies. In this example, components are divided logically. This is the minimum set for a backend developer.<\/p>\n<p><\/p>\n<p>When creating a new project, we create a Dockerfile, describing the required ecosystem (Python, Ruby, NodeJS). The docker-compose file describes the necessary dependencies \u2014 the database. We specify that a database of a certain version is needed, to store data in specific locations.<\/p>\n<p><\/p>\n<p>We use a separate third container with nginx for serving static files. There is an option to upload images. The backend places them in a pre-prepared volume, which is also mounted in the container with nginx that serves the static files.<\/p>\n<p><\/p>\n<p>To store the nginx and mysql configuration, we added a Docker folder where we keep the necessary configs. When a developer clones the repository to their machine, they get a project that is ready for local development. There is no question regarding what port or which settings to apply.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/e7a6b308a75cacf8ce60687a9e6081dd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Next, we have several components: admin, info API, push notifications.<\/p>\n<p><\/p>\n<p>To launch all of this, we created another repository, which we named dockerized-app. Currently, we use several repositories for each component. They simply differ logically \u2014 in GitLab, it appears as a folder, while on the developer's machine, it is a folder for a specific project. At a lower level are the components that will be integrated together.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f4e2c45c8ec20fca0bac74b3a3892543.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This is an example of the contents of dockerized-app. We also include a Docker directory here, where we fill in the configurations required for the interactions of all components. There is a README.md that briefly describes how to run the project.<\/p>\n<p><\/p>\n<p>Here we applied two docker-compose files. This is done to allow for step-by-step launching. When a developer works with the core, they do not need push notifications, so they simply run the docker-compose file, thus saving resources.<\/p>\n<p><\/p>\n<p>If there is a need for integration with push notifications, then docker-compose.yaml and docker-compose-push.yaml are launched.<\/p>\n<p><\/p>\n<p>Since docker-compose.yaml and docker-compose-push.yaml are in the same folder, a single virtual network is automatically created.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/535019c82c72c1602176fbaf4aa5640f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Description of components. This is a more detailed file that is responsible for assembling the components. What is noteworthy here? We introduce a load balancer component.<\/p>\n<p><\/p>\n<p>This is a ready-made Docker image, in which nginx and an application that listens to the Docker socket are launched. Dynamically, as containers are turned on and off, the nginx config is regenerated. We distribute the interaction with the components across third-level domain names.<\/p>\n<p><\/p>\n<p>For the development environment, we use the .dev domain \u2014 api.informer.dev. Applications with the .dev domain are accessible on the developer's local machine.<\/p>\n<p><\/p>\n<p>Then the configs are transferred to each project, and all projects are launched simultaneously.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/bc1e11e6f1fd1c837d2c8087d04145c4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Graphically depicted, the client is our browser or some tool we use to make requests to the load balancer.<\/p>\n<p><\/p>\n<p>The load balancer determines which container to address based on the domain name.<\/p>\n<p><\/p>\n<p>This could be nginx serving JS for the admin panel. It could be nginx that provides the API or static files delivered by nginx in the form of image uploads.<\/p>\n<p><\/p>\n<p>The diagram shows that the containers are combined into a virtual network and hidden behind a proxy.<\/p>\n<p><\/p>\n<p>On the developer's machine, you can access the container knowing the IP, but we generally do not apply this. There is hardly ever a need for direct access.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/2908c31fd9d3c79f6189bdbeeb6d028f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>What example should I look at to dockerize my application? In my opinion, a good example is the official Docker image for MySQL.<\/p>\n<p><\/p>\n<p>It is quite complex. There are many versions. However, its functionality covers numerous needs that may arise during further development. If you spend time understanding how everything interacts, I think you'll have no issues with independent implementation.<\/p>\n<p><\/p>\n<p>On hub.docker.com, there are usually links to github.com, where you can find the raw data needed to create an image yourself.<\/p>\n<p><\/p>\n<p>Further in this repository, there is a script called docker-endpoint.sh, which is responsible for the initial setup and further handling of the application startup.<\/p>\n<p><\/p>\n<p>Also, this example provides the option for configuration using environment variables. By defining the environment variable when launching a single container or via docker-compose, you can say that we need to set an empty password for docker for root on MySQL or something of our choice.<\/p>\n<p><\/p>\n<p>There is an option to create a random password. We state that we require a user, need to set a password for that user, and need to create a database.<\/p>\n<p><\/p>\n<p>In our projects, we standardized the Dockerfile that handles initialization. We adjusted it to our needs to simply extend the user rights that the application uses. This subsequently allowed us to easily create databases from the application console. In Ruby applications, there are commands for creating, modifying, and deleting databases.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4a9b56e21819d28dd39753134d23fee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This is an example of how a specific version of MySQL looks on github.com. You can open the Dockerfile and see how the installation proceeds.<\/p>\n<p><\/p>\n<p>The docker-endpoint.sh script is responsible for the entry point. During initial initialization, certain preparation actions are required, and all these actions are outlined in the initialization script.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/4153fa46526f3d802931dd1db48b08cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Let's move on to the second part.<\/p>\n<p><\/p>\n<p>For storing source codes, we switched to GitLab. It's a powerful system that features a visual interface.<\/p>\n<p><\/p>\n<p>One of the components of GitLab is GitLab CI. It allows you to describe a sequence of commands that will subsequently be used to organize the code delivery system or trigger automated testing.<\/p>\n<p><\/p>\n<p>Report on GitLab CI 2 <noindex><a rel=\"nofollow\" href=\"https:\/\/goo.gl\/uohKjI\">https:\/\/goo.gl\/uohKjI<\/a><\/noindex> \u2014 a report from the Ruby Russia club \u2014 it's quite detailed and may interest you.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/5d26d20500f34eeb9768e6c3d91bbefa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Now we will look at what is required to activate GitLab CI. To start GitLab CI, we just need to place the .gitlab-ci.yml file in the root of the project.<\/p>\n<p><\/p>\n<p>Here, we describe what we want to execute in a sequence of states like testing and deployment.<\/p>\n<p><\/p>\n<p>We execute scripts that directly invoke the docker-compose build of our application. This is an example of the backend.<\/p>\n<p><\/p>\n<p>Next, we specify that it is necessary to run migrations for database changes and execute tests.<\/p>\n<p><\/p>\n<p>If the scripts execute correctly and do not return an error code, then the system moves on to the second stage of deployment.<\/p>\n<p><\/p>\n<p>The deployment stage is currently implemented for staging. We have not organized zero-downtime restart.<\/p>\n<p><\/p>\n<p>We forcibly stop all containers and then bring all containers back up, built in the first stage during testing.<\/p>\n<p><\/p>\n<p>We run database migrations for the current variable environment that were written by developers.<\/p>\n<p><\/p>\n<p>There is a note that this should only be applied to the master branch.<\/p>\n<p><\/p>\n<p>Changes in other branches are not executed.<\/p>\n<p><\/p>\n<p>There is the possibility to organize releases by branches.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/8a5b880bd9a0d4bab0b276568e2ba413.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>To further organize this, we need to install GitLab Runner.<\/p>\n<p><\/p>\n<p>This utility is written in Golang. It is a single file as is common in the Golang world, requiring no dependencies.<\/p>\n<p><\/p>\n<p>Upon starting, we register the GitLab Runner.<\/p>\n<p><\/p>\n<p>We obtain the key in the web interface of GitLab.<\/p>\n<p><\/p>\n<p>Then we call the initialization command in the command line.<\/p>\n<p><\/p>\n<p>We configure the GitLab Runner in interactive mode (Shell, Docker, VirtualBox, SSH).<\/p>\n<p><\/p>\n<p>The code on the GitLab Runner will execute with each commit depending on the settings in .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/37c8ac7499f0cfba2eb07a84083e44e1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This is how it visually looks in GitLab's web interface. After connecting GitLab CI, we see a flag that indicates the current status of the build.<\/p>\n<p><\/p>\n<p>We can see that a commit was made 4 minutes ago, which passed all tests without issues.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/33ac79c45f0651a5f8bde75092c4ac44.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>We can take a closer look at the builds. Here we see that two states have already been passed: testing and staging deployment.<\/p>\n<p><\/p>\n<p>If we click on a specific build, there will be a console output of the commands that were executed during the process according to .gitlab-ci.yml.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d26dd28708c7fc8a409af4be4d48f502.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This is how the history of our product looks. We can see that there were successful attempts. When the tests fail, it does not proceed to the next step, and the code on staging does not get updated.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/ee7bd81dc283c7af0c194779d4230780.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>What issues were we addressing on staging when we implemented Docker? Our system consists of components, and we needed to restart only those components that were updated in the repository, rather than the entire system.<\/p>\n<p><\/p>\n<p>To do this, we had to separate everything into individual folders.<\/p>\n<p><\/p>\n<p>After doing this, we encountered a problem where Docker-compose creates a separate network space for each folder, and the components cannot see each other.<\/p>\n<p><\/p>\n<p>To work around this, we created a network in Docker manually. In Docker-compose, we specified to use this network for the project.<\/p>\n<p><\/p>\n<p>Thus, each component that starts with this network can see components in other parts of the system.<\/p>\n<p><\/p>\n<p>The next issue is separating staging between multiple projects.<\/p>\n<p><\/p>\n<p>To ensure everything looks neat and closely resembles production, it's good to use port 80 or 443, which are widely used in the web.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/f863cf1833271bf132455a70ad7d2706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>How did we solve this? We assigned one GitLab Runner to all major projects.<\/p>\n<p><\/p>\n<p>GitLab allows us to run several distributed GitLab Runners that will simply take all tasks in a random order.<\/p>\n<p><\/p>\n<p>To avoid chaos, we limited our group of projects to one GitLab Runner, which handles the load just fine.<\/p>\n<p><\/p>\n<p>We moved nginx-proxy to a separate startup script, where we specified the networks of all projects. <\/p>\n<p><\/p>\n<p>Our project has one network, while the load balancer has multiple networks by project names. It can proxy further based on domain names.<\/p>\n<p><\/p>\n<p>Requests come to us via the domain on port 80 and are routed to a group of containers that serve this domain.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/25c769f029b272a17fd06bbe60cf0ea0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>What other issues arose? By default, all containers run as the root user. This root is not the same as the system host root.<\/p>\n<p><\/p>\n<p>However, if you log into the container, you will be root, and any file created in that container will have root permissions.<\/p>\n<p><\/p>\n<p>If a developer logs into the container and executes commands that create files, then logs out, they will have a file in their working directory that they do not have access to.<\/p>\n<p><\/p>\n<p>How can this be resolved? Users can be added who will operate within the container.<\/p>\n<p><\/p>\n<p>What problems arose when we added a user?<\/p>\n<p><\/p>\n<p>When creating a user, we often have mismatches between group ID (UID) and user ID (GID).<\/p>\n<p><\/p>\n<p>To solve this problem, we use users with ID 1000 inside the container.<\/p>\n<p><\/p>\n<p>In our case, this coincided with the fact that almost all developers use the Ubuntu OS. In Ubuntu, the first user has ID 1000.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/d4911374e4953fdcd807c387efeafec3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>What are our plans?<\/p>\n<p><\/p>\n<p>Re-read the Docker documentation. The project is actively evolving, and the documentation is changing. Information that was obtained two to three months ago is gradually becoming outdated. <\/p>\n<p><\/p>\n<p>Some of the issues we resolved have likely already been addressed by the standard tools.<\/p>\n<p><\/p>\n<p>I really want to move on and directly transition to orchestration.<\/p>\n<p><\/p>\n<p>One example is Docker Swarm, which is built into Docker and available out of the box. We want to run something in production based on Docker Swarm technology.<\/p>\n<p><\/p>\n<p>Generating containers complicates working with logs. Currently, the logs are isolated. They are scattered across containers. One of the tasks is to provide easy access to logs via a web interface.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Development and testing process with Docker and Gitlab CI\" src=\"\/wp-content\/uploads\/2019\/04\/cff8c72dd8af7de1fa0227601bd1d69c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Source: <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.1.1 - 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\/en\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\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\/en\/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\udd47Development and Testing Process with Docker and GitLab CI | ProHoster","description":"I suggest reviewing the transcript of Alexander Sigachev's presentation from Inventos titled 'Development and Testing Process with Docker + GitLab CI.' This is for those who are just starting to implement the development process.","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/protsess-razrabotki-i-testirovaniya-s-docker-i-gitlab-ci","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","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\/en\/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\/en\/wp-json\/wp\/v2\/posts\/32526","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=32526"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/32526\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/24326"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=32526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=32526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=32526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}