Complete Setup Guide
Follow these steps to set up your CI/CD pipeline for either AWS or Google Cloud.
AWS Setup Instructions
Create an AWS Account. If you don't have one, sign up at aws.amazon.com.
Create an IAM User. Go to IAM service → Users → Add user. Enable programmatic access and attach these policies:
AmazonEC2ContainerRegistryFullAccess,AmazonECS_FullAccess.Create ECR Repository. Go to ECR → Create repository → Name it (e.g., "my-app").
Create ECS Cluster. Go to ECS → Clusters → Create cluster → Select "Networking only" → Name it (e.g., "my-cluster").
Set Up GitLab Variables. In your GitLab project, go to Settings → CI/CD → Variables and add:
AWS_ACCESS_KEY_ID– From your IAM userAWS_SECRET_ACCESS_KEY– From your IAM userAWS_DEFAULT_REGION– e.g., "us-east-1"AWS_ACCOUNT_ID– Your 12-digit AWS account IDECR_REPO_NAME– Name of your ECR repositoryECS_CLUSTER_NAME– Name of your ECS clusterECS_SERVICE_NAME– Will be created automaticallyECS_TASK_DEFINITION– Will be created automatically
Quick AWS CLI Setup (Alternative)
If you have AWS CLI installed, you can run these commands:
# Configure AWS CLI
aws configure
# Create ECR repository
aws ecr create-repository --repository-name my-app
# Create ECS cluster
aws ecs create-cluster --cluster-name my-cluster
Google Cloud Setup Instructions
Create a Google Cloud Account. If you don't have one, sign up at cloud.google.com.
Create a Project. Go to Console → Select or create project → Name it (e.g., "my-ci-cd-project").
Enable APIs. Enable these APIs: Artifact Registry API, Cloud Run API.
Create Service Account. Go to IAM & Admin → Service Accounts → Create Service Account. Add these roles: Artifact Registry Writer, Cloud Run Admin. Then create and download a JSON key.
Create Artifact Registry. Go to Artifact Registry → Create Repository → Name it (e.g., "my-repo").
Set Up GitLab Variables. In your GitLab project, go to Settings → CI/CD → Variables and add:
GOOGLE_SERVICE_ACCOUNT_KEY– Base64 encoded content of your JSON key fileGOOGLE_SERVICE_ACCOUNT_EMAIL– Email of your service accountGOOGLE_PROJECT_ID– Your Google Cloud project IDGOOGLE_DEFAULT_REGION– e.g., "us-central1"AR_REPO_NAME– Name of your Artifact Registry repository
Encode Service Account Key
To encode your service account key for GitLab:
# On Linux/Mac
base64 -i key-file.json
# On Windows (PowerShell)
[Convert]::ToBase64String([IO.File]::ReadAllBytes("key-file.json"))
Separate Pipeline Configurations
Choose your cloud provider and use the appropriate pipeline configuration.
AWS GitLab CI/CD Pipeline
Use this pipeline if you're deploying to AWS ECS.
.gitlab-ci.yml (AWS Version)
image: docker:latest
services:
- docker:dind
stages:
- build
- test
- deploy
variables:
APP_NAME: "my-webapp"
# AWS TEMPLATE
.aws-job-template: &aws-job-anchor
variables:
ECR_URI: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${ECR_REPO_NAME}
before_script:
# Install AWS CLI and dependencies
- apk add --no-cache python3 py3-pip gettext jq
- pip3 install awscli
- export PATH=$PATH:/usr/local/bin
- aws --version
# Configure AWS credentials
- aws configure set aws_access_key_id "$AWS_ACCESS_KEY_ID"
- aws configure set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY"
- aws configure set default.region "$AWS_DEFAULT_REGION"
# Login to AWS ECR
- echo "Logging into AWS ECR..."
- aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URI
# DOCKER BUILD SCRIPTS
.docker-build-scripts: &docker-build-scripts
- docker build -t ${IMAGE_URI}:$CI_COMMIT_SHORT_SHA -f ./Dockerfile .
- docker push ${IMAGE_URI}:$CI_COMMIT_SHORT_SHA
- docker build -t ${IMAGE_URI}:latest -f ./Dockerfile .
- docker push ${IMAGE_URI}:latest
# -------------------------------
# BUILD STAGE
# -------------------------------
aws-build-job:
<<: *aws-job-anchor
stage: build
script:
- export IMAGE_URI=$ECR_URI
- *docker-build-scripts
only:
- main
- develop
# -------------------------------
# TEST STAGE
# -------------------------------
# Python pytest
python-test:
image: python:3.9
stage: test
script:
- pip install -r requirements.txt
- pip install pytest pytest-cov
- python -m pytest tests/ --cov=app --cov-report=xml
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
only:
- main
- develop
- merge_requests
# Integration test with Docker Compose
integration-test:
stage: test
script:
- echo "Running app using docker-compose for integration test..."
- docker-compose up --build -d
- echo "Waiting for containers to initialize..."
- sleep 10
- docker-compose logs
- echo "Running integration tests..."
# Add your integration test commands here
after_script:
- echo "Cleaning up..."
- docker-compose down
only:
- main
- develop
# -------------------------------
# DEPLOY STAGE
# -------------------------------
# AWS Deploy
aws-deploy-job:
<<: *aws-job-anchor
stage: deploy
environment: production
script:
# Create task definition from template
- envsubst < aws-task-definition.json > task-definition.json
- cat task-definition.json
# Register task definition and update service
- aws ecs register-task-definition --cli-input-json file://task-definition.json
- aws ecs update-service --cluster $ECS_CLUSTER_NAME --service $ECS_SERVICE_NAME --task-definition $ECS_TASK_DEFINITION
dependencies:
- aws-build-job
only:
- main
Google Cloud GitLab CI/CD Pipeline
Use this pipeline if you're deploying to Google Cloud Run.
.gitlab-ci.yml (Google Cloud Version)
image: docker:latest
services:
- docker:dind
stages:
- build
- test
- deploy
variables:
APP_NAME: "my-webapp"
# GOOGLE TEMPLATE
.google-job-template: &google-job-anchor
variables:
AR_URI: $GOOGLE_DEFAULT_REGION-docker.pkg.dev/$GOOGLE_PROJECT_ID/$AR_REPO_NAME/$APP_NAME
before_script:
# Install Google Cloud SDK
- apk add --no-cache tar curl python3 coreutils jq
- curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
- tar -xf google-cloud-cli-linux-x86_64.tar.gz
- ./google-cloud-sdk/install.sh --quiet
- export PATH=$PATH:$(pwd)/google-cloud-sdk/bin
# Authenticate with Google Cloud
- echo $GOOGLE_SERVICE_ACCOUNT_KEY | base64 -d > /tmp/key.json
- gcloud auth activate-service-account $GOOGLE_SERVICE_ACCOUNT_EMAIL --key-file=/tmp/key.json
- gcloud config set project $GOOGLE_PROJECT_ID
# Login to Google Artifact Registry
- gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://$GOOGLE_DEFAULT_REGION-docker.pkg.dev
# DOCKER BUILD SCRIPTS
.docker-build-scripts: &docker-build-scripts
- docker build -t ${IMAGE_URI}:$CI_COMMIT_SHORT_SHA -f ./Dockerfile .
- docker push ${IMAGE_URI}:$CI_COMMIT_SHORT_SHA
- docker build -t ${IMAGE_URI}:latest -f ./Dockerfile .
- docker push ${IMAGE_URI}:latest
# -------------------------------
# BUILD STAGE
# -------------------------------
google-build-job:
<<: *google-job-anchor
stage: build
script:
- export IMAGE_URI=$AR_URI
- *docker-build-scripts
only:
- main
- develop
# -------------------------------
# TEST STAGE
# -------------------------------
# Python pytest
python-test:
image: python:3.9
stage: test
script:
- pip install -r requirements.txt
- pip install pytest pytest-cov
- python -m pytest tests/ --cov=app --cov-report=xml
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
only:
- main
- develop
- merge_requests
# Integration test with Docker Compose
integration-test:
stage: test
script:
- echo "Running app using docker-compose for integration test..."
- docker-compose up --build -d
- echo "Waiting for containers to initialize..."
- sleep 10
- docker-compose logs
- echo "Running integration tests..."
# Add your integration test commands here
after_script:
- echo "Cleaning up..."
- docker-compose down
only:
- main
- develop
# -------------------------------
# DEPLOY STAGE
# -------------------------------
# Google Deploy
google-deploy-job:
<<: *google-job-anchor
stage: deploy
environment: production
script:
- gcloud run deploy $APP_NAME
--image=$AR_URI:latest
--platform=managed
--region=$GOOGLE_DEFAULT_REGION
--allow-unauthenticated
--set-env-vars=ENVIRONMENT=production
dependencies:
- google-build-job
only:
- main
Project Structure
Your project should have this basic structure:
your-project/
├── .gitlab-ci.yml # Choose AWS or Google version
├── Dockerfile # Your application Dockerfile
├── docker-compose.yml # For integration tests
├── aws-task-definition.json # AWS ECS task definition (for AWS only)
├── requirements.txt # Python dependencies (if using Python)
├── app/ # Your application code
│ └── ...
├── tests/ # Your tests
│ ├── test_app.py # Python tests
│ └── ...
└── YourBlazorApp.sln # Blazor solution file (if using Blazor)
Testing Examples
Here are complete testing examples for both Python/pytest and Blazor/bUnit.
Python Testing with pytest
Create
requirements.txtflask==2.3.3 pytest==7.4.2 pytest-cov==4.1.0 requests==2.31.0Create a Simple Flask App
# app/__init__.py from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return {'message': 'Hello, World!'} @app.route('/health') def health(): return {'status': 'healthy'} if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)Create pytest Tests
# tests/test_app.py import pytest from app import app @pytest.fixture def client(): with app.test_client() as client: yield client def test_hello_route(client): response = client.get('/') assert response.status_code == 200 assert response.json == {'message': 'Hello, World!'} def test_health_route(client): response = client.get('/health') assert response.status_code == 200 assert response.json == {'status': 'healthy'} def test_nonexistent_route(client): response = client.get('/nonexistent') assert response.status_code == 404Create Dockerfile
FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 5000 CMD ["python", "-m", "app"]
Blazor Testing with bUnit
Create Blazor Project. Create a new Blazor Server or WebAssembly project.
Add Test Project
dotnet new blazorserver -o MyBlazorApp cd MyBlazorApp dotnet new xunit -o MyBlazorApp.Tests cd MyBlazorApp.Tests dotnet add package bunit dotnet add package coverlet.collector dotnet add reference ../MyBlazorApp/MyBlazorApp.csprojCreate bUnit Tests
// Tests/HomePageTests.cs using Bunit; using Xunit; using MyBlazorApp.Pages; namespace MyBlazorApp.Tests { public class HomePageTests : TestContext { [Fact] public void HomePage_RendersCorrectly() { // Arrange var cut = RenderComponent<Home>(); // Assert cut.MarkupMatches("<h1>Hello, world!</h1>"); } [Fact] public void Counter_StartsAtZero() { // Arrange var cut = RenderComponent<Counter>(); // Assert cut.Find("p").MarkupMatches("<p>Current count: 0</p>"); } [Fact] public void Counter_IncrementsWhenClicked() { // Arrange var cut = RenderComponent<Counter>(); // Act cut.Find("button").Click(); // Assert cut.Find("p").MarkupMatches("<p>Current count: 1</p>"); } } }Create Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyBlazorApp/MyBlazorApp.csproj", "MyBlazorApp/"] RUN dotnet restore "MyBlazorApp/MyBlazorApp.csproj" COPY . . WORKDIR "/src/MyBlazorApp" RUN dotnet build "MyBlazorApp.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyBlazorApp.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyBlazorApp.dll"]
Deployment Configuration
Configure your deployment for either AWS or Google Cloud.
AWS ECS Task Definition
Create this file for AWS ECS deployment.
aws-task-definition.json
{
"family": "devopsAppTask",
"containerDefinitions": [
{
"name": "webapp",
"image": "${ECR_URI}:latest",
"cpu": 0,
"portMappings": [
{
"name": "webapp-80-tcp",
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp",
"appProtocol": "http"
}
],
"essential": true,
"environment": [
{ "name": "ENVIRONMENT", "value": "production" },
{ "name": "PORT", "value": "80" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/devopsAppTask",
"awslogs-create-group": "true",
"awslogs-region": "${AWS_DEFAULT_REGION}",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"executionRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole",
"networkMode": "awsvpc",
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "512",
"memory": "1024",
"runtimePlatform": {
"cpuArchitecture": "X86_64",
"operatingSystemFamily": "LINUX"
}
}
Create ECS Task Execution Role
You need to create an ECS task execution role in AWS IAM:
- Go to IAM → Roles → Create role.
- Select "AWS service" and choose "Elastic Container Service" → "Elastic Container Service Task".
- Attach the policy
AmazonECSTaskExecutionRolePolicy. - Name the role "ecsTaskExecutionRole".
Google Cloud Run Configuration
For Google Cloud, the deployment is handled directly in the pipeline. Make sure you have:
- Artifact Registry repository created
- Cloud Run API enabled
- Service account with appropriate permissions
Manual Cloud Run Deployment (if needed)
You can manually deploy to Cloud Run using gcloud:
# Build and push image manually
docker build -t us-central1-docker.pkg.dev/my-project/my-repo/my-app:latest .
docker push us-central1-docker.pkg.dev/my-project/my-repo/my-app:latest
# Deploy to Cloud Run
gcloud run deploy my-app \
--image us-central1-docker.pkg.dev/my-project/my-repo/my-app:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated
Docker Compose for Integration Tests
Create this file for running integration tests.
docker-compose.yml
version: '3.8'
services:
webapp:
build: .
ports:
- "5000:5000"
environment:
- ENVIRONMENT=test
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
tests:
build: .
command: python -m pytest tests/ -v
depends_on:
- webapp
environment:
- TEST_URL=http://webapp:5000
Next Steps & Troubleshooting
Common Issues & Solutions
Pipeline fails with "no basic auth credentials"
This means Docker can't authenticate with the container registry. Check that your AWS or Google Cloud credentials are correctly set in GitLab CI/CD variables.
ECS service not found
Make sure your ECS cluster and service names match exactly what you set in GitLab variables. The service will be created on first deployment.
Permission denied errors
Check that your IAM user (AWS) or service account (Google) has the necessary permissions. The guide includes the required roles.
Monitoring Your Pipeline
- Check pipeline status in GitLab → CI/CD → Pipelines
- View job logs by clicking on each job
- Set up notifications for failed pipelines
🚀 Ready to Deploy!
You now have a complete CI/CD pipeline that can build, test, and deploy your application to either AWS or Google Cloud. Push your code to GitLab and watch the magic happen!