Why should I rent a gitlab runner?

How to Improve the Performance of Your GitLab Runners

Guénolé Delanoë
·
August 19, 2023

Continuous Integration and Continuous Delivery (CI/CD) pipelines are the backbone of modern software development, enabling teams to automate testing, building, and deploying applications. GitLab, a widely-used open-source platform, incorporates CI/CD functionality, with GitLab runners executing these crucial jobs. However, slow GitLab runners can significantly hamper the performance of your CI/CD pipeline, causing delays in deployments, reduced productivity, and potential revenue losses.

For instance, a well-known fintech company recently faced severe delays in their CI/CD processes due to underpowered runners. This led to a 30% drop in developer productivity, as engineers spent more time waiting for builds to complete than writing code. This bottleneck not only delayed feature releases but also impacted customer satisfaction and the company's competitive edge.

If you're struggling with slow GitLab runners, you're not alone. Many users face this issue due to various reasons, such as CPU limitations, low memory, or network pressure. This is why we created our own high performance cloud-based gitlab runners company.

To get a clearer understanding of why your runners might be slow, check out our article on the topic. We discuss typical causes and offer pointers for enhancing performance.

Optimize My GitLab CI/CD

Parallelizing Jobs

Before Optimization:

In many CI/CD pipelines, jobs are often executed sequentially, which can lead to significant delays, especially if some tasks are not dependent on each other.

stages:
 - build
 - test
 - deploy

build:
 stage: build
 script:
   - echo "Building the project..."
   - ./build.sh

test:
 stage: test
 script:
   - echo "Running tests..."
   - ./test.sh

deploy:
 stage: deploy
 script:
   - echo "Deploying the project..."
   - ./deploy.sh

To maximize efficiency, jobs that do not depend on each other can be run concurrently by using parallel jobs within the same stage.

stages:
 - build
 - test
 - deploy

build:
 stage: build
 script:
   - echo "Building the project..."
   - ./build.sh

unit_test:
 stage: test
 script:
   - echo "Running unit tests..."
   - ./unit_test.sh

integration_test:
 stage: test
 script:
   - echo "Running integration tests..."
   - ./integration_test.sh

deploy:
 stage: deploy
 script:
   - echo "Deploying the project..."
   - ./deploy.sh

In the optimized version, the test stage contains both unit_test and integration_test jobs. By configuring these as separate jobs within the same stage, GitLab CI/CD can execute them in parallel if there are sufficient runner resources. This approach reduces the overall pipeline execution time, making the CI/CD process more efficient and speeding up the delivery of new features and fixes.

Prebuilding Dependencies

Each pipeline run might involve rebuilding Docker images and downloading dependencies, which is redundant and time-consuming.

stages:
 - build
 - test
 - deploy

build:
 stage: build
 script:
   - docker build -t my-app .
   - docker run my-app npm install

test:
 stage: test
 script:
   - docker run my-app npm test

deploy:
 stage: deploy
 script:
   - docker run my-app npm run deploy

Prebuild Docker images and cache dependencies to avoid redundant tasks.

stages:
 - prebuild
 - build
 - test
 - deploy

prebuild:
 stage: prebuild
 script:
   - docker build -t my-app .
   - docker create --name my-app-container my-app
   - docker cp package.json my-app-container:/app/
   - docker cp package-lock.json my-app-container:/app/
   - docker start my-app-container
   - docker exec my-app-container npm install
   - docker commit my-app-container my-app-with-deps
   - docker rm my-app-container

build:
 stage: build
 script:
   - docker run my-app-with-deps npm run build

test:
 stage: test
 script:
   - docker run my-app-with-deps npm test

deploy:
 stage: deploy
 script:
   - docker run my-app-with-deps npm run deploy

In the optimized version, the prebuild stage handles building the Docker image and installing dependencies. The resulting image is reused in subsequent stages, significantly reducing the time spent on redundant tasks.

Optimizing Task Execution

Tasks such as building Docker images or downloading dependencies are executed within the pipeline, which can slow down the overall process.

stages:
 - build
 - test
 - deploy

build:
 stage: build
 script:
   - docker build -t my-app .
   - docker run my-app npm install

test:
 stage: test
 script:
   - docker run my-app npm test

deploy:
 stage: deploy
 script:
   - docker run my-app npm run deploy

Move these tasks outside of the pipeline to streamline execution.

stages:
 - build
 - test
 - deploy

build:
 stage: build
 script:
   - docker build -t my-app:latest .

test:
 stage: test
 script:
   - docker run my-app:latest npm test

deploy:
 stage: deploy
 script:
   - docker run my-app:latest npm run deploy

By prebuilding Docker images and handling dependencies outside the main pipeline, the overall execution becomes more efficient. The build stage simply pulls and runs the prebuilt image, significantly speeding up the process.

Maximizing CPU Utilization

CI/CD pipelines often do not fully utilize the available CPU resources, leading to inefficiencies.

test:
 stage: test
 script:
   - echo "Running tests..."
   - ./run_tests.sh

Optimize the test suite to leverage multi-threading capabilities.

test:
 stage: test
 script:
   - echo "Running tests..."
   - ./run_tests.sh --parallel

By configuring the test suite to run in parallel, multiple tests can execute simultaneously, fully utilizing the available CPU cores. This reduces the overall test execution time and improves pipeline efficiency.

Understanding Where Your GitLab Runners Run

GitLab runners can either be managed by GitLab.com or self-hosted by the organization. Managed runners by GitLab.com typically run on virtual machine (VM) instances on platforms such as Google Cloud Platform (GCP), offering limited resources like 2 CPU cores and 2 GB of RAM. While these managed runners are convenient and often free, they may not suffice for resource-intensive CI/CD tasks, leading to performance bottlenecks and slow execution times.

Alternatively, self-hosted runners provide organizations with greater control over resource allocation and runner configurations. By managing their own infrastructure, teams can tailor resources to meet specific workload requirements. However, this approach necessitates additional setup and maintenance efforts.

Identifying the Causes of Slow Performance

Assessing Resource Utilization

To diagnose performance issues, consider setting up a new runner locally or on an on-premises server with ample resources. This controlled environment allows you to test performance outside of GitLab's managed infrastructure. Monitor the new runner's performance closely to identify whether slowdowns are due to inadequate CPU, RAM, or network resources.

Reviewing System Metrics

Analyze CPU utilization, memory usage, and network access metrics of the new runner. If these metrics do not indicate any issues, it may be time to optimize your GitLab CI/CD configuration itself.

Improve My GitLab Runner Performance

Advanced Caching Techniques

CI/CD jobs are often highly repetitive, with approximately 98% of the output from previous pipeline runs being reused. Implementing advanced caching techniques, such as S3 caching, Docker image caching, HTTP response caching, and build artifact caching, can significantly enhance performance.

For example, a large e-commerce company improved their pipeline speed by 40% simply by caching Docker images, reducing redundant tasks and streamlining their CI/CD process.

Ensuring Adequate System Resources

Address any resource limitations by adding more CPU cores, increasing RAM capacity, or improving network connectivity. This can alleviate bottlenecks and boost overall performance.

Conclusion

In conclusion, optimizing GitLab runners involves understanding where they run, diagnosing performance issues, and implementing advanced caching techniques and resource improvements. By parallelizing jobs, prebuilding dependencies, and maximizing CPU utilization, you can significantly enhance your CI/CD pipeline's performance, ensuring faster and more efficient software delivery.

We have then delved into conducting platform audits to identify system components and pinpoint bottlenecks. Subsequently, we have examined ways to improve system performance, particularly when faced with hardware limitations, emphasizing the importance of caching. Lastly, we have highlighted the crucial significance of enhancing pipeline architectures to optimize parallelism.

Discover the power of high-performance GitLab runners with Cloud-Runner. Explore our services and cut by 4 the time of your CI/CD pipelines today!

Untitled UI logotext
© 2024 Cloud-Runner. All rights reserved.