How to automate CI/CD pipelines with Docker and AWS for faster releases
Automating CI/CD pipelines with Docker and AWS streamlines deployments, minimizes manual intervention, and ensures faster, more reliable releases. By integrating these tools, you can optimize resources, enhance scalability, and reduce operational overhead—allowing your team to focus on innovation rather than infrastructure management. This guide walks you through a step-by-step approach to setting up a robust CI/CD pipeline, ensuring efficiency, security, and cost-effectiveness in your cloud-based development workflow.
Starting with the Dockerfile
Before setting up the pipeline, let’s assume our Java microservice is structured such that the final artifact is a JAR file
. A simple Dockerfile might look like this:
FROM amazoncorretto:17
WORKDIR /app
COPY target/<your-app-jar>.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
amazoncorretto:17
: Uses Amazon’s Corretto (Java 17).WORKDIR /app
: Sets the working directory in the container.COPY
: Copies the built JAR into the container.EXPOSE 8080
: Exposes port 8080 (update if your app uses a different port).ENTRYPOINT
: Instructs Docker to run the JAR as the main process.
Pipeline objective and stages
Goal: Automate the process of building, testing, scanning, and deploying your application in AWS with Docker.
Our 5 pipeline stages
We’ll create an AWS CodePipeline with the following stages:
Source: Pulls the latest code from your repository.
Compile: Compiles the code to catch syntax and dependency issues.
Test: Runs unit or integration tests for early feedback.
Sonar: Scans your code for security issues, coverage, and code smells.
Build: Builds your Docker image, pushes it to Amazon ECR, and deploys to ECS.
Setting up the pipeline
- Create a new pipeline in the AWS CodePipeline console and name it (e.g., Bitlogic-Docker-Pipeline).
- Select an Artifact Store (e.g., S3) for pipeline artifacts.
- Add the 5 stages listed above (Source, Compile, Test, Sonar, Build) in CodePipeline.
At this point, each stage will be empty. Let’s configure them one by one.
1. Source stage
Purpose: Automatically pulls changes from your code repository (AWS CodeCommit, GitHub, or Bitbucket).
1.1. Configure your repository details in the Source stage.
1.2. Branch: Choose the branch (e.g., main
) that triggers the pipeline.
1.3. Permissions: Ensure CodePipeline can access the repo (e.g., via IAM roles or OAuth).
Result: Any commit to your designated branch will kick off the pipeline automatically.
2. Compile stage
Purpose: Compiles your Java application to identify any compilation or dependency errors early on.
2.1. Create a CodeBuild Project named “Compile.”
2.2. Add a buildspec (example below) that runs mvn compile
:
version: 0.2
phases:
install:
runtime-versions:
java: corretto17
build:
commands:
- echo "Compiling the application..."
- mvn compile
artifacts:
files:
- target/**/*.class
2.3. Attach this project to the Compile stage in CodePipeline.
2.4. Fail Fast: If the code can’t compile, the pipeline stops here—saving time and resources.
3. Test stage
Purpose: Runs unit and/or integration tests to provide early feedback on code functionality.
3.1. Create a CodeBuild project named “Test.”
3.2. Add a buildspec that runs mvn test
:
version: 0.2
phases:
build:
commands:
- echo "Running tests..."
- mvn test
artifacts:
files:
- target/surefire-reports/*.xml
3.3. Attach this project to the Test stage.
Result: If any tests fail, the pipeline halts, preventing broken code from moving forward.
4. Sonar stage
Purpose: Analyzes code for coverage, vulnerabilities, and code smells using Sonar.
4.1. Create a CodeBuild project named “Sonar.”
4.2. Reference a buildspec that runs the Sonar Maven Plugin:
version: 0.2
phases:
install:
runtime-versions:
java: corretto17
pre_build:
commands:
- echo "Starting Sonar analysis..."
build:
commands:
- mvn clean install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \
-Dsonar.host.url=https://sonarcloud.io \
-Dsonar.organization=<your-organization> \
-Dsonar.projectKey=<your-project-key> \
-Dsonar.token=$SONAR_TOKEN \
-Dsonar.qualitygate.wait=true \
-Dsonar.coverage.jacoco.xmlReportPaths=/target/site/jacoco-aggregate/jacoco.xml \
-DnvdApiKey=$dckey
artifacts:
files:
- target/*
- Quality Gate: If code fails Sonar’s specified thresholds, this step fails, blocking deployment.
4.3. Attach this project to the Sonar stage in CodePipeline.
5. Build stage (Docker Build & Deploy)
Purpose: Uses the Dockerfile shown at the beginning, pushes the resulting image to Amazon ECR, and updates your ECS service.
5.1. Create a CodeBuild Project named “Build.”
5.2. Use a buildspec similar to the following to build, push, and deploy:
version: 0.2
phases:
pre_build:
commands:
- echo "Logging in to ECR..."
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo "Building the Docker image..."
- docker build -t <repo-name>:latest .
- docker tag <repo-name>:latest <aws-account-id>.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/<repo-name>:latest
post_build:
commands:
- echo "Pushing image to ECR..."
- docker push <aws-account-id>.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/<repo-name>:latest
- echo "Deploying to ECS..."
- aws ecs update-service --cluster <your-ecs-cluster> --service <your-ecs-service> --force-new-deployment
artifacts:
files:
- Dockerfile
5.3. Link this project to the Build stage in CodePipeline.
Result:
- Docker image is pushed to ECR.
- ECS fetches the new image, updating the running service in near real-time.
Best practices at Bitlogic
Fail fast: Stop on errors in Compile or Test to save build resources and developer time.
Use version tags: Push both
latest
and a semantic version (e.g.,1.0.0
) for easier rollbacks and traceability.Store secrets securely: Keep Sonar tokens (
$SONAR_TOKEN
) in Parameter Store or CodeBuild’s environment variables.Automate rollbacks: Configure ECS to revert if the new deployment fails health checks.
Comprehensive logging: Aggregate logs from CodeBuild, ECS, and your application (e.g., CloudWatch Logs) for quick troubleshooting.
Wrapping up
By starting with a Dockerfile for your Java microservice and following these five stages—Source, Compile, Test, Sonar, and Build—you’ll:
- Catch errors early (compilation or test failures).
- Enforce code quality (Sonar).
- Streamline deployments to AWS ECS.
This approach ensures faster, more reliable releases, embodying DevOps best practices at Bitlogic. Customize each step (e.g., test commands, Docker configurations) to fit your project’s requirements, and you’ll have a robust pipeline that grows with your development team.
Ready to Automate? Let’s Talk!
If you want to improve efficiency and speed up software releases, we can help. Our expert DevOps engineers ensure seamless integration and deployment—without disrupting your operations.
📩 Contact us today for a free consultation!