GitLab CI/CD Integration and Deployment Orchestration for Amazon EC2

The orchestration of software delivery from a version control system to a live production environment requires a robust bridge between the Continuous Integration (CI) pipeline and the underlying compute infrastructure. In the context of GitLab and Amazon Web Services (AWS), this bridge is established through a combination of GitLab CI/CD pipelines and Amazon Elastic Compute Cloud (EC2). The synergy between these two platforms allows organizations to transform raw code into running applications through automated workflows that handle everything from infrastructure provisioning via CloudFormation to the final deployment of application artifacts. This process is governed by the .gitlab-ci.yml configuration file, which acts as the blueprint for the entire pipeline, and the GitLab Runner, which serves as the execution engine that carries out the specified jobs.

The integration of GitLab CI/CD with EC2 is not a monolithic process but rather a multi-layered architecture. It begins with the secure authentication of the GitLab environment to the AWS ecosystem, followed by the establishment of a compute resource (the Runner) to execute the logic, and concludes with the deployment of the actual application payload to an EC2 instance. Whether utilizing the high-level abstraction of Auto DevOps or the granular control of Infrastructure-as-Code (IaC) via CloudFormation, the goal remains the same: to achieve a repeatable, consistent, and scalable deployment cycle that minimizes manual intervention and eliminates the risks associated with human error during the release process.

AWS Authentication and Security Configuration

Before any deployment action can be triggered, a secure trust relationship must be established between GitLab and AWS. This is achieved by providing the GitLab CI/CD environment with the necessary permissions to interact with the AWS API.

The primary method for this authentication involves the creation of an Identity and Access Management (IAM) user within the AWS account. This user must be granted the specific permissions required for the tasks at hand, such as creating CloudFormation stacks or pushing files to S3 buckets. Once the IAM user is created, the administrator must generate a set of security credentials, specifically the Access Key ID and the Secret Access Key.

These credentials must never be hard-coded into the .gitlab-ci.yml file, as doing so would expose them to anyone with access to the repository. Instead, they are stored as protected CI/CD variables within the GitLab project settings.

The following table outlines the mandatory variables required for the authentication process:

Variable Name Description Purpose
AWS_ACCESS_KEY_ID The unique identifier for the IAM user Authenticates the identity of the requestor to AWS
AWS_SECRET_ACCESS_KEY The secret key associated with the IAM user Provides the cryptographic proof of identity
AWS_DEFAULT_REGION The AWS region code (e.g., us-east-1) Directs the deployment to the specific geographic data center

For advanced security requirements, GitLab supports the use of ID tokens and OpenID Connect (OIDC). This method is significantly more secure than using static CI/CD variables because it eliminates the need to store long-term secrets in GitLab, utilizing short-lived tokens instead. However, it is important to note that OIDC is not compatible with the specific guided templates provided for basic EC2 deployment and requires a custom configuration.

GitLab Runner Infrastructure and Management

The GitLab Runner is the application that actually executes the jobs defined in the .gitlab-ci.yml file. While GitLab provides hosted runners, enterprises often deploy their own runners on Amazon EC2 to gain more control over the environment and to reduce costs.

The manual setup of a runner is often a time-consuming process involving the provisioning of virtual machines, software installation, and manual registration. To solve this, an Infrastructure-as-Code (IaC) approach is utilized, primarily through AWS CloudFormation.

Automated Deployment via CloudFormation

By using IaC, the entire GitLab Runner architecture is described in a template, allowing it to be deployed quickly and consistently across multiple AWS accounts. The process follows a specific operational flow:

  1. The user executes a deployment script that references a parameterized CloudFormation template.
  2. A properties file is used to define the specific infrastructure configurations and the target environment.
  3. The deploy script invokes the CloudFormation CreateStack API to instantiate the runner environment.
  4. An EC2 Auto Scaling group is created, ensuring that the desired number of instances is always available to handle pipeline workloads.

The Role of the Launch Template and IAM

Each EC2 instance in the runner fleet is launched using a launch template. This template ensures that every runner is a mirror image of the others, providing consistency across the fleet. Attached to these instances is a specific IAM role that contains the permissions necessary for the runner to execute pipeline jobs, such as interacting with other AWS services or accessing the GitLab Container Registry.

Instance Lifecycle and Graceful Termination

To manage costs and optimize performance, the solution utilizes autoscaling. To prevent the abrupt termination of a running job when an instance is scaled down, a lifecycle hook is attached to the autoscaling group. This hook ensures that the instance undergoes a graceful termination process, allowing the current pipeline job to complete before the resource is destroyed.

Configuration through cfn-init

During the boot process of the EC2 instance, CloudFormation employs a helper script known as cfn-init. This script automates the final stages of the runner setup:

  • Installation: cfn-init installs the GitLab Runner software directly onto the EC2 instance.
  • Executor Configuration: The runner is configured as a Docker executor. This allows it to pull a pre-defined Docker image from the GitLab Container Registry, ensuring that each build runs in an isolated container with all necessary software pre-installed.
  • Registration: The script registers the runner to the specific GitLab projects defined in the properties file, linking the compute power of the EC2 instance to the project's pipeline.

Deployment Strategies for Application Payloads

Once the infrastructure is in place and the runner is active, the actual application must be deployed to the target EC2 instances. GitLab provides several pathways to achieve this, depending on the level of automation required.

Utilizing Auto DevOps for EC2

Auto DevOps is designed to reduce the manual effort of writing complex CI/CD configurations. To deploy to EC2 using this method, the .gitlab-ci.yml file must reference the Auto-DevOps.gitlab-ci.yml template.

The configuration requires the definition of a build_artifact job within the build stage. The following example demonstrates the required structure:

```yaml
include:
- template: Auto-DevOps.gitlab-ci.yml

variables:
AUTODEVOPSPLATFORM_TARGET: EC2

build_artifact:
stage: build
script:
-
artifacts:
paths:
-
```

In this configuration, the AUTO_DEVOPS_PLATFORM_TARGET variable tells GitLab to route the deployment toward the EC2 environment. The build_artifact job is critical because it produces the file that will eventually be deployed to the server.

The AWS/CF-Provision-and-Deploy-EC2 Template

For users who need more control over their infrastructure, GitLab provides the AWS/CF-Provision-and-Deploy-EC2 template. This template automates the bridge between the build artifact and the live instance.

The pipeline execution follows a three-step sequence:

  • Infrastructure Provisioning: The pipeline uses the AWS CloudFormation API to create the stack, ensuring the EC2 instance and networking are ready.
  • Artifact Storage: The build job creates an application artifact, which is then pushed to an AWS S3 bucket for centralized storage.
  • Application Deployment: The content is retrieved from S3 and deployed onto the target EC2 instance.

To utilize this template, users must provide specific JSON configurations. The JSON for pushing the artifact to S3 must include:

  • applicationName: A string identifying the application.
  • source: The location where the build job originally created the application.
  • s3Location: The destination path in the S3 bucket, formatted as s3://your/bucket/project_built_file....

Template Dependencies and Constraints

When configuring the pipeline, it is critical to understand the hierarchy of GitLab's AWS templates to avoid pipeline failures.

The AWS/Deploy-ECS.gitlab-ci.yml template is a composite template that includes two other templates: Jobs/Build.gitlab-ci.yml and Jobs/Deploy/ECS.gitlab-ci.yml.

The following restrictions apply to these templates:

  • Independent Inclusion: Do not include Jobs/Build.gitlab-ci.yml or Jobs/Deploy/ECS.gitlab-ci.yml on their own. They are designed to function only as part of the main AWS/Deploy-ECS.gitlab-ci.yml template.
  • Volatility: These sub-templates are subject to unexpected moves or changes by GitLab.
  • Job Overriding: Users should not override the job names within these templates. Because the names may change in future updates, any custom override would cease to function, potentially breaking the deployment pipeline.

For those using ECS-specific deployments, the variable CI_AWS_ECS_WAIT_FOR_ROLLOUT_COMPLETE_DISABLED can be set to a non-empty value if the user wishes to disable the behavior where the pipeline waits for the rollout to complete.

Operational Maintenance and Cleanup

Maintaining a healthy deployment environment requires both monitoring and the ability to decommission resources to prevent unnecessary costs.

Troubleshooting and Log Analysis

When a deployment fails or a runner does not register correctly, the primary source of truth is the log files located on the EC2 instance. Specifically, administrators should connect to the runner EC2 instance and examine the files located at:

/var/log/cfn-*.log

These logs provide detailed output from the CloudFormation initialization process and the cfn-init script, revealing exactly where a configuration step may have failed.

Resource Decommissioning

To avoid incurring ongoing charges from AWS, it is essential to clean up the environment once the deployment or testing phase is complete. Because the architecture is built on CloudFormation, the most efficient way to delete all provisioned resources—including the EC2 instances, IAM roles, and networking components—is to delete the CloudFormation stack created during the deployment phase.

Comprehensive Analysis of the GitLab-to-EC2 Ecosystem

The integration of GitLab CI/CD with Amazon EC2 represents a convergence of software delivery and infrastructure management. By treating the GitLab Runner as a dynamic resource managed via IaC, organizations can move away from "snowflake" servers (manually configured, unique servers) toward an immutable infrastructure model.

The impact of this approach is significant. The use of Auto Scaling and lifecycle hooks ensures that the compute cost is directly proportional to the workload, preventing the waste of idle resources. Furthermore, the reliance on Docker executors within the runners ensures that the environment in which the code is built is identical to the environment in which it is tested, drastically reducing "it works on my machine" bugs.

The flexibility of the offering is evident in its accessibility, being available across Free, Premium, and Ultimate tiers, and supporting GitLab.com, Self-Managed, and Dedicated instances. This ensures that the path from a small hobby project to a massive enterprise deployment remains consistent.

The transition from manual deployments to the AWS/CF-Provision-and-Deploy-EC2 workflow fundamentally changes the role of the DevOps engineer. Instead of manually SSHing into servers to pull code, the engineer becomes an architect of the pipeline, defining the JSON specifications and the YAML logic that governs the automated flow of data. The result is a highly resilient system where the infrastructure is as versionable and trackable as the application code itself.

Sources

  1. Use Auto DevOps to deploy to EC2
  2. Deploy and Manage Gitlab Runners on Amazon EC2
  3. Deploy to AWS from GitLab CI/CD

Related Posts