CircleCI

CI/CDCircleCICloudDockerAutomationDevOpsParallel ExecutionPerformance

CI/CD Tool

CircleCI

Overview

CircleCI is a cloud-first CI/CD platform. It features fast execution, Docker support, parallel processing, and excellent developer experience, with over 50% of customers achieving elite DORA benchmarks.

Details

CircleCI is a cloud-native CI/CD (Continuous Integration/Continuous Delivery) platform founded in 2011. It integrates with GitHub, GitHub Enterprise, and Bitbucket to automatically create builds when new code lines are committed. It leverages containers to run multiple builds and deployments in parallel, achieving high performance and efficiency. In 2024, Centralized Configuration, Templates, and Overrides were introduced, enabling platform teams to manage pipelines from central templates while maintaining development team autonomy. The provision of Mac M4 Pro resources brings Apple's most advanced silicon directly to CI/CD pipelines, delivering cutting-edge performance with optimized CPU and memory configurations. It supports both cloud-managed options and execution on private infrastructure, providing flexible workflow control through dynamic configuration and Pre/Post steps.

Pros and Cons

Pros

  • High Performance: Over 50% of customers achieve elite DORA benchmarks
  • Parallel Execution: Simultaneous multiple builds through container technology
  • Cloud Native: Managed service without infrastructure management
  • Excellent Developer Experience: Intuitive UI/UX and easy setup
  • Rich Integrations: Support for GitHub, Bitbucket, major cloud services
  • Flexible Execution Environments: Linux, macOS, Windows, ARM support
  • Centralized Configuration: Balance governance and autonomy through template management
  • Latest Hardware: Access to cutting-edge resources like Mac M4 Pro

Cons

  • Pricing Structure: High costs due to pay-as-you-go pricing for large-scale usage
  • Vendor Lock-in: Risk of dependency on CircleCI-specific features
  • Self-hosting Limitations: Completely cloud-dependent, no on-premises option
  • Customization Constraints: Lower configuration freedom compared to Jenkins
  • Execution Time Limits: Monthly execution time constraints by plan
  • Debugging Difficulty: Challenging local environment reproduction
  • Dependency Management: Impact from external service outages

Key Links

Code Examples

Basic Configuration

# .circleci/config.yml
version: 2.1

jobs:
  build:
    docker:
      - image: cimg/base:2024.12
    resource_class: xlarge
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: |
            sudo apt-get update
            sudo apt-get install -y nodejs npm
      - run:
          name: Build application
          command: |
            npm install
            npm run build
      - persist_to_workspace:
          root: .
          paths:
            - dist

  test:
    docker:
      - image: cimg/node:18.20
    steps:
      - checkout
      - restore_cache:
          keys:
            - node-deps-{{ checksum "package-lock.json" }}
      - run:
          name: Install dependencies
          command: npm ci
      - save_cache:
          key: node-deps-{{ checksum "package-lock.json" }}
          paths:
            - node_modules
      - run:
          name: Run tests
          command: npm test
      - store_test_results:
          path: test-results
      - store_artifacts:
          path: coverage

workflows:
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build

Docker Integration and Parallel Execution

version: 2.1

executors:
  node-executor:
    docker:
      - image: cimg/node:18.20
        environment:
          NODE_ENV: test
  
  docker-executor:
    docker:
      - image: cimg/base:2024.12
    resource_class: large

jobs:
  install_dependencies:
    executor: node-executor
    steps:
      - checkout
      - restore_cache:
          keys:
            - deps-{{ checksum "package-lock.json" }}
            - deps-
      - run:
          name: Install dependencies
          command: npm ci
      - save_cache:
          key: deps-{{ checksum "package-lock.json" }}
          paths:
            - node_modules
      - persist_to_workspace:
          root: .
          paths:
            - node_modules

  lint:
    executor: node-executor
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: ESLint
          command: npm run lint
      - run:
          name: Prettier check
          command: npm run format:check

  unit_tests:
    executor: node-executor
    parallelism: 4
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Run unit tests
          command: |
            TESTFILES=$(circleci tests glob "test/**/*.test.js" | circleci tests split --split-by=timings)
            npm test $TESTFILES
      - store_test_results:
          path: test-results

  build_docker:
    executor: docker-executor
    steps:
      - checkout
      - attach_workspace:
          at: .
      - setup_remote_docker:
          version: 20.10.18
          docker_layer_caching: true
      - run:
          name: Build Docker image
          command: |
            docker build -t myapp:${CIRCLE_SHA1} .
            docker tag myapp:${CIRCLE_SHA1} myapp:latest
      - run:
          name: Push to registry
          command: |
            echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
            docker push myapp:${CIRCLE_SHA1}
            docker push myapp:latest

workflows:
  main:
    jobs:
      - install_dependencies
      - lint:
          requires:
            - install_dependencies
      - unit_tests:
          requires:
            - install_dependencies
      - build_docker:
          requires:
            - lint
            - unit_tests
          filters:
            branches:
              only: main

Multi-Environment Deployment

version: 2.1

orbs:
  aws-cli: circleci/[email protected]
  kubernetes: circleci/[email protected]

commands:
  deploy_to_environment:
    parameters:
      environment:
        type: string
      image_tag:
        type: string
    steps:
      - run:
          name: Deploy to << parameters.environment >>
          command: |
            helm upgrade --install myapp-<< parameters.environment >> ./helm/myapp \
              --namespace << parameters.environment >> \
              --set image.tag=<< parameters.image_tag >> \
              --set environment=<< parameters.environment >> \
              --wait --timeout=300s

jobs:
  build_and_push:
    docker:
      - image: cimg/base:2024.12
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build and push Docker image
          command: |
            docker build -t $AWS_ECR_REGISTRY/myapp:$CIRCLE_SHA1 .
            aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ECR_REGISTRY
            docker push $AWS_ECR_REGISTRY/myapp:$CIRCLE_SHA1

  deploy_staging:
    docker:
      - image: cimg/aws:2024.03
    steps:
      - checkout
      - kubernetes/install-kubectl
      - aws-cli/setup
      - run:
          name: Configure kubectl
          command: |
            aws eks update-kubeconfig --name staging-cluster --region $AWS_DEFAULT_REGION
      - deploy_to_environment:
          environment: staging
          image_tag: $CIRCLE_SHA1

  deploy_production:
    docker:
      - image: cimg/aws:2024.03
    steps:
      - checkout
      - kubernetes/install-kubectl
      - aws-cli/setup
      - run:
          name: Configure kubectl
          command: |
            aws eks update-kubeconfig --name production-cluster --region $AWS_DEFAULT_REGION
      - deploy_to_environment:
          environment: production
          image_tag: $CIRCLE_SHA1

workflows:
  deploy_pipeline:
    jobs:
      - build_and_push:
          context: aws-credentials
      - deploy_staging:
          requires:
            - build_and_push
          context: aws-credentials
          filters:
            branches:
              only: develop
      - hold_for_approval:
          type: approval
          requires:
            - build_and_push
          filters:
            branches:
              only: main
      - deploy_production:
          requires:
            - hold_for_approval
          context: aws-credentials
          filters:
            branches:
              only: main

Pre/Post Steps and Performance Optimization

version: 2.1

jobs:
  test_with_setup:
    docker:
      - image: cimg/node:18.20
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: npm ci
      - run:
          name: Run tests
          command: npm test

  deploy_with_monitoring:
    docker:
      - image: cimg/base:2024.12
    steps:
      - checkout
      - run:
          name: Deploy application
          command: ./deploy.sh

workflows:
  test_and_deploy:
    jobs:
      - test_with_setup:
          pre-steps:
            - run:
                name: Setup custom environment
                command: |
                  echo "export CUSTOM_VAR=value" >> $BASH_ENV
                  source $BASH_ENV
          post-steps:
            - run:
                name: Cleanup test environment
                command: ./cleanup.sh
            - store_artifacts:
                path: logs
      - deploy_with_monitoring:
          requires:
            - test_with_setup
          pre-steps:
            - run:
                name: Pre-deployment checks
                command: ./pre-deploy-check.sh
          post-steps:
            - run:
                name: Post-deployment monitoring
                command: ./monitor-deployment.sh

ARM Architecture and Multi-Platform

version: 2.1

jobs:
  build_x86:
    docker:
      - image: cimg/base:2024.12
    resource_class: large
    steps:
      - checkout
      - run:
          name: Build for x86_64
          command: |
            echo "Building for x86_64 architecture"
            make build ARCH=amd64

  build_arm:
    machine:
      image: ubuntu-2004:2024.01.2
    resource_class: arm.medium
    steps:
      - checkout
      - run:
          name: Check ARM architecture
          command: |
            uname -a
            echo "Hello, ARM!"
      - run:
          name: Build for ARM
          command: |
            echo "Building for ARM64 architecture"
            make build ARCH=arm64

  test_matrix:
    docker:
      - image: cimg/node:18.20
    parallelism: 8
    steps:
      - checkout
      - run:
          name: Matrix testing
          command: |
            case $CIRCLE_NODE_INDEX in
              0) npm run test:unit ;;
              1) npm run test:integration ;;
              2) npm run test:e2e ;;
              3) npm run test:security ;;
              4) npm run test:performance ;;
              5) npm run test:accessibility ;;
              6) npm run test:compatibility ;;
              7) npm run test:load ;;
            esac

workflows:
  multi_arch_build:
    jobs:
      - build_x86
      - build_arm
      - test_matrix:
          requires:
            - build_x86
            - build_arm