Setting Up CI/CD on AWS

Welcome to this step-by-step guide for setting up Continuous Integration and Continuous Deployment (CI/CD) pipelines on AWS Elastic Beanstalk. Whether you're a beginner or just looking for a straightforward walkthrough, this guide will help you implement CI/CD using two popular methods: GitHub Actions and AWS CodePipeline.

๐Ÿ•ฎ Content provided by DeployPRO - the easiest way to deploy apps on AWS, Azure and Digital Ocean

โœ… Option #1: GitHub Actions, AWS ECR, and Elastic Beanstalk

Step 1: GitHub Repository Setup

  1. Create a GitHub Repository:
    • Navigate to GitHub.
    • Click on the "+" in the top right corner and select "New repository."
    • Name your repository and follow the instructions to create it.
  2. Clone the Repository:
    • Open a terminal and run this command
    • git clone https://github.com/your-username/your-repository.git && cd your-repository

Step 2: GitHub Actions Configuration

  1. Create GitHub Actions Workflow:
    • Inside your repository, create a .github/workflows directory.
    • Create a YAML file (e.g., main.yml).
  2. Add GitHub Actions YAML Script:
    • Copy and paste the following script into main.yml
    • Replace placeholders like AWS_ACCESS_KEY,AWS_SECRET_KEY,AWS_REGION, and AWS_ACCOUNT_ID with your actual values.
name: Build and Deployment

on:
  push:
    branches:
      - main
      - dev
      - staging
      - test

jobs:
  build-image:
    name: Build Image
    runs-on: ubuntu-latest
    outputs:
      ENVIRONMENT: ${{ steps.generate-env.outputs.ENVIRONMENT }}
      APP_NAME: ${{ steps.repo-name.outputs.repository-name }}
      IMAGE_TAG_NGINX: nginx-${{ steps.generate-env.outputs.ENVIRONMENT }}-${{ github.run_number }}
      IMAGE_TAG_APP: app-${{ steps.generate-env.outputs.ENVIRONMENT }}-${{ github.run_number }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - uses: FranzDiebold/github-env-vars-action@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Get repository name
        id: repo-name
        uses: MariachiBear/get-repo-name-action@v1.2.0
        with:
          string-case: "lowercase"

      - name: Generate Environment Name
        id: generate-env
        run: |
          if [ "${GITHUB_REF##*/}" = "main" ]; then echo "ENVIRONMENT=prod" >> $GITHUB_OUTPUT; fi
          if [ "${GITHUB_REF##*/}" = "dev" ]; then echo "ENVIRONMENT=dev" >> $GITHUB_OUTPUT; fi
          if [ "${GITHUB_REF##*/}" = "staging" ]; then echo "ENVIRONMENT=staging" >> $GITHUB_OUTPUT; fi
          if [ "${GITHUB_REF##*/}" = "test" ]; then echo "ENVIRONMENT=test" >> $GITHUB_OUTPUT; fi

      - name: Build and push the image Application to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ steps.repo-name.outputs.repository-name }}
          IMAGE_TAG: app-${{ steps.generate-env.outputs.ENVIRONMENT }}-${{ github.run_number }}
          # ENVFILE: ${{ secrets.ENVFILE }}
        run: |
          echo "Build the application docker image"
          echo ${{ secrets.ENVFILE }} >> .env
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          echo "Pushing the image to ECR..."
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Build and push the image Nginx to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ steps.repo-name.outputs.repository-name }}
          IMAGE_TAG: nginx-${{ steps.generate-env.outputs.ENVIRONMENT }}-${{ github.run_number }}
        run: |
          echo "Build the nginx docker image"
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG ./nginx
          echo "Pushing the image to ECR..."
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          
  deployment:
    name: Deploy Application
    runs-on: ubuntu-latest
    needs: [build-image]

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Generate deployment package
        run: |
          cp docker-compose-template.yml docker-compose.editing.yml
          sed -i 's/AWS_ACCOUNT_ID/'"${{ secrets.AWS_ACCOUNT_ID }}"'/g' docker-compose.editing.yml
          sed -i 's/IMAGE_TAG_APP/'"${{ needs.build-image.outputs.IMAGE_TAG_APP }}"'/g' docker-compose.editing.yml
          sed -i 's/IMAGE_TAG_NGINX/'"${{ needs.build-image.outputs.IMAGE_TAG_NGINX }}"'/g' docker-compose.editing.yml
          sed -i 's/AWS_REGION/'"${{ secrets.AWS_REGION }}"'/g' docker-compose.editing.yml
          sed -i 's/IMAGE_NAME/'"${{ needs.build-image.outputs.APP_NAME }}"'/g' docker-compose.editing.yml
          mv docker-compose.editing.yml docker-compose.yml
          cat docker-compose.yml
          zip -r deploy.zip ./docker-compose.yml

      - name: Deploy Docker images to EB
        uses: einaregilsson/beanstalk-deploy@v20
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY }}
          aws_secret_key: ${{ secrets.AWS_SECRET_KEY }}
          application_name: ${{ needs.build-image.outputs.APP_NAME }}
          environment_name: ${{ needs.build-image.outputs.APP_NAME }}-${{ needs.build-image.outputs.ENVIRONMENT }}
          region: ${{ secrets.AWS_REGION }}
          version_label: ${{ github.sha }}
          deployment_package: deploy.zip

Step 3: Adding Secrets to Your GitHub Repository

  1. Create AWS Access Key and Secret Key:
    • Go to the AWS Management Console.
    • Navigate to "IAM" (Identity and Access Management).
    • In the left sidebar, click on "Users" and then "Add user."
    • Give your user a name, check "Programmatic access," and click "Next."
    • Attach the AWSElasticBeanstalk, AmazonEC2ContainerRegistryFullAccess, AmazonEC2FullAccess, AmazonRDSFullAccess, AWSCodePipeline_FullAccess, IAMFullAccess, IAMUserChangePassword, and SecretsManagerReadWrite policies.
    • Review and create the user. Save the Access Key ID and Secret Access Key.
  2. Add AWS Credentials as Secrets:
    • In your GitHub repository, go to "Settings" -> "Secrets and Variables" -> "Actions" -> "New repository secret."
    • Add AWS_ACCESS_KEY, AWS_SECRET_KEY, AWS_REGION, and AWS_ACCOUNT_ID any other necessary secrets.
    • Provide the corresponding values for each secret.
    • Click "Add secret" to save.

Step 4: AWS Elastic Beanstalk Configuration

  1. Go to the AWS Management Console
    1. Navigate to the AWS Management Console.
    2. Sign in to your AWS account.
  2. Click "Create a New Environment"
    1. In the AWS Management Console, locate the "Elastic Beanstalk" service.
    2. Click on "Create a new environment."
  3. Configure Environment
    1. Select "Web server environment" as your environment type.
    2. Enter a unique and meaningful "Application name."
    3. Choose a distinctive "Environment name."
  4. Configure Service Access
    • In the "Service role" section, create a new service role or use an existing one.
    • If creating a new role, Elastic Beanstalk will guide you through the process.
  5. Optional - Set Up Networking, Database, and Tags
    1. Navigate to the "Configure more options" section.
    2. Configure networking, including VPC, subnets, and security groups.
    3. Set up a database if required, choosing options like RDS.
    4. Add tags for better organization.
  6. Optional - Configure Instance Traffic and Scaling
    1. Navigate to the "Capacity" section.
    2. Configure instance type, number of instances, and scaling options based on your requirements.
  7. Optional - Configure Updates, Monitoring, and Logging
    1. Navigate to the "Configuration" section.
    2. Configure application updates, monitoring options, and logging settings as needed.
  8. Review
    1. Click "Create environment" after reviewing all your configurations.
    2. Wait for Elastic Beanstalk to provision resources and deploy the sample application.

Step 5: Create an Elastic Container Registry (ECR)

  1. Create an ECR Repository:
  2. Note the ECR repository URI (e.g., your-aws-account-id.dkr.ecr.your-aws-region.amazonaws.com/your-ecr-repository).

Step 6: Testing the CI/CD Pipeline

  1. Commit and Push Changes:
    • Create a new file, make changes, or push an existing file to the main branch.
    • Commit your changes to the GitHub repository.
    • Push the changes to the main branch.
  2. Monitor GitHub Actions Workflow:
    • Go to the "Actions" tab in your GitHub repository to monitor the workflow.
    • Ensure each step executes successfully.
  3. Verify Deployment on AWS Elastic Beanstalk:
    • Visit the Elastic Beanstalk environment in the AWS Management Console.
    • Verify that the new version is deployed successfully.

โœ… Option #2: CodeBuild, ECR, CodePipeline, and Elastic Beanstalk

Step 1: GitHub Repository Setup

  1. Create a GitHub Repository:
    • Navigate to GitHub.
    • Click on the "+" in the top right corner and select "New repository."
    • Name your repository and follow the instructions to create it.
  2. Clone the Repository:
    • Open a terminal and run this command
    • git clone https://github.com/your-username/your-repository.git && cd your-repository

Step 2: AWS CodeBuild Configuration

  1. Navigate to AWS CodeBuild:
  2. Define Build Specifications:
    • In your GitHub repository, create a buildspec.yml
    • Set environment variable in CodeBuild with this AWS_REGION, AWS_ACCOUNT_ID, IMAGE_TAG, and IMAGE_REPO_NAME.
    • Set environment to Ubuntu, and managed image.
    • Choose a compatible runtime version.
    • In the "Buildspec" section, select "Use a buildspec file" and specify the location of your buildspec.yml.
version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging into Amazon ECR...
      - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t app-$IMAGE_REPO_NAME:$IMAGE_TAG .
      - docker tag app-$IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:app-$IMAGE_TAG
      - docker build -t nginx-$IMAGE_REPO_NAME:$IMAGE_TAG ./nginx/
      - docker tag nginx-$IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:nginx-$IMAGE_TAG
      - cp docker-compose-template.yml docker-compose.editing.yml
      - sed -i 's/AWS_ACCOUNT_ID/'$AWS_ACCOUNT_ID'/g' docker-compose.editing.yml
      - sed -i 's/IMAGE_TAG_APP/'app-$IMAGE_TAG'/g' docker-compose.editing.yml
      - sed -i 's/IMAGE_TAG_NGINX/'nginx-$IMAGE_TAG'/g' docker-compose.editing.yml
      - sed -i 's/AWS_REGION/'$AWS_REGION'/g' docker-compose.editing.yml
      - sed -i 's/IMAGE_NAME/'$IMAGE_REPO_NAME'/g' docker-compose.editing.yml
      - mv docker-compose.editing.yml docker-compose.yml
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image app...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:app-$IMAGE_TAG
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:nginx-$IMAGE_TAG

artifacts:
  files:
    - docker-compose.yml

Step 3: AWS CodePipeline Configuration

  1. Navigate to AWS CodePipeline:
    • Go to AWS CodePipeline.
    • Click "Create pipeline."
    • Connect your GitHub repository as the source.
  2. Define Stages and Actions:
    • Configure stages (source, build, deploy).
    • For source, choose GitHub.
    • For build, use AWS CodeBuild and connect to your GitHub repository.
    • For deploy, select Elastic Beanstalk.
  3. Set Up Artifacts:
    • Define input and output artifacts for smooth pipeline flow.

Step 4: AWS Elastic Beanstalk Configuration

  1. Create an Elastic Beanstalk Environment:
    • Go to AWS Elastic Beanstalk.
    • Click "Create a new environment"
    • follow the setup wizard.
    • Choose a sample application or upload your code.

Step 5: Testing the CI/CD Pipeline

  1. Commit and Push Changes
    • Add or modify files in your repository.
    • Commit and push changes to the main branch.
  2. Monitor CodePipeline Execution
    • Visit the CodePipeline dashboard.
    • Monitor the pipeline stages for successful execution.
  3. Verify Deployment on AWS Elastic Beanstalk
    • Check your Elastic Beanstalk environment for the latest deployment.

โœ… Setup Load Balancer for Elastic Beanstalk

Step 1: Create an SSL Certificate in ACM

  1. Sign in to AWS Console
    1. Go to AWS Management Console.
    2. Sign in with your AWS account.
    3. In the AWS Management Console, search for "Certificate Manager" or find it in the "Security, Identity, & Compliance" section.
  2. Request a Certificate
    1. Click on the "Request a certificate" button.
    2. Enter the domain name (e.g., www.yourdomain.com) and click "Next."
    3. Choose a validation method (e.g., DNS validation) and follow the on-screen instructions to complete the validation.
    4. Wait for the certificate status to change to "Issued."

Step 2: Configure Elastic Beanstalk Environment

  1. Environment Configuration
    1. Open the environment and click `Configurations`
    2. In the "Configure more options" section, navigate to the "Load Balancer" settings.
    3. Make sure "Enable Load Balancer" is selected.
    4. Save the configuration.

Step 3: Configure SSL for Load Balancer

  1. Open Load Balancer Configuration
    1. After configuring the environment, go to the environment dashboard.
    2. Navigate to the "Configuration" tab.
  2. Add Listener
    1. Scroll down to the "Load Balancer" section and click "Modify."
    2. Add a listener for HTTPS (port 443).
    3. Select the ACM certificate you created.
  3. Update Environment
    1. Scroll down and click "Apply" to update the environment.

Step 4: Test the Setup

  1. Access Your Application
    1. Once the deployment is complete, access your application using the HTTPS URL (e.g., https://www.yourdomain.com).
    2. Ensure that your browser shows the padlock icon, indicating a secure connection.

โœ… GitHub Actions vs. CodePipeline

GitHub Actions:

Pros:

  • Seamless integration with GitHub repositories.
  • Quick setup and configuration using YAML files.
  • Free for public repositories.

Cons:

  • Limited parallelism for free accounts.
  • May require additional setup for complex workflows.

AWS CodePipeline:

Pros:

  • Native AWS integration for a comprehensive CI/CD experience.
  • Scalable and robust pipeline configuration.
  • Supports a wide range of AWS services.

Cons:

  • May involve more initial setup steps.
  • Costs are associated with usage, but a free tier is available.

โœ… Conclusion

In conclusion, both GitHub Actions and AWS CodePipeline offer powerful CI/CD capabilities. GitHub Actions excels in simplicity and GitHub integration, while AWS CodePipeline provides a more comprehensive, native AWS experience. The choice between them depends on your project's specific needs and your team's familiarity with each platform.


โœ… Resources

For questions and product requests, feel free to contact AppSeed via email or Discord:

  • ๐Ÿ‘‰ Accessย AppSeedย for more starters and support
  • ๐Ÿ‘‰ Take a look at DeployPRO, a simple solution to deploy apps on AWS, Digital Ocean, and Azure.