Skip to content

Module 7 : CI/CD avec CodePipeline & CodeBuild

Durée estimée : 15 minutes

Objectifs du Module

À la fin de ce module, vous serez capable de :

  • Configurer CodeCommit ou intégrer GitHub/GitLab
  • Créer des projets CodeBuild pour build et tests
  • Orchestrer des pipelines avec CodePipeline
  • Déployer automatiquement sur ECS/EKS
  • Implémenter des stratégies de déploiement (Blue/Green, Canary)

1. Vue d'Ensemble CI/CD AWS

graph LR
    subgraph "Source"
        CC["📦 CodeCommit"]
        GH["🐙 GitHub"]
        GL["🦊 GitLab"]
    end

    subgraph "Build"
        CB["🔨 CodeBuild"]
    end

    subgraph "Test"
        CBT["🧪 CodeBuild<br/>(Tests)"]
    end

    subgraph "Deploy"
        CD["🚀 CodeDeploy"]
        ECS["📦 ECS"]
        EKS["☸️ EKS"]
        LAMBDA["⚡ Lambda"]
    end

    subgraph "Orchestration"
        CP["🎯 CodePipeline"]
    end

    CC --> CP
    GH --> CP
    GL --> CP
    CP --> CB
    CB --> CBT
    CBT --> CD
    CD --> ECS
    CD --> EKS
    CD --> LAMBDA

    style CP fill:#ff9900,color:#000
    style CB fill:#1a73e8,color:#fff

2. CodeBuild

2.1 Configuration buildspec.yml

# buildspec.yml
version: 0.2

env:
  variables:
    DOCKER_BUILDKIT: "1"
  secrets-manager:
    DOCKER_HUB_TOKEN: "dockerhub:token"
  parameter-store:
    NPM_TOKEN: "/myapp/npm-token"

phases:
  install:
    runtime-versions:
      docker: 20
      nodejs: 18
    commands:
      - echo "Installing dependencies..."
      - npm ci

  pre_build:
    commands:
      - echo "Logging into ECR..."
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}

  build:
    commands:
      - echo "Running tests..."
      - npm test
      - echo "Building Docker image..."
      - docker build -t $ECR_REGISTRY/$IMAGE_REPO:$IMAGE_TAG .
      - docker tag $ECR_REGISTRY/$IMAGE_REPO:$IMAGE_TAG $ECR_REGISTRY/$IMAGE_REPO:latest

  post_build:
    commands:
      - echo "Pushing Docker image..."
      - docker push $ECR_REGISTRY/$IMAGE_REPO:$IMAGE_TAG
      - docker push $ECR_REGISTRY/$IMAGE_REPO:latest
      - echo "Writing image definitions file..."
      - printf '[{"name":"app","imageUri":"%s"}]' $ECR_REGISTRY/$IMAGE_REPO:$IMAGE_TAG > imagedefinitions.json

artifacts:
  files:
    - imagedefinitions.json
    - appspec.yml
    - taskdef.json

reports:
  coverage:
    files:
      - "coverage/clover.xml"
    file-format: CLOVERXML

cache:
  paths:
    - node_modules/**/*
    - /root/.npm/**/*

2.2 Créer un Projet CodeBuild

# Créer le rôle IAM
cat > codebuild-trust.json << 'EOF'
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {"Service": "codebuild.amazonaws.com"},
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF

aws iam create-role \
    --role-name CodeBuildServiceRole \
    --assume-role-policy-document file://codebuild-trust.json

# Attacher les policies nécessaires
aws iam attach-role-policy \
    --role-name CodeBuildServiceRole \
    --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser

# Créer le projet
aws codebuild create-project \
    --name my-app-build \
    --source '{
        "type": "GITHUB",
        "location": "https://github.com/myorg/myapp.git",
        "gitCloneDepth": 1,
        "buildspec": "buildspec.yml"
    }' \
    --artifacts '{
        "type": "S3",
        "location": "my-codebuild-artifacts",
        "packaging": "ZIP"
    }' \
    --environment '{
        "type": "LINUX_CONTAINER",
        "image": "aws/codebuild/amazonlinux2-x86_64-standard:5.0",
        "computeType": "BUILD_GENERAL1_MEDIUM",
        "privilegedMode": true,
        "environmentVariables": [
            {"name": "ECR_REGISTRY", "value": "123456789012.dkr.ecr.eu-west-1.amazonaws.com"},
            {"name": "IMAGE_REPO", "value": "my-app"}
        ]
    }' \
    --service-role arn:aws:iam::123456789012:role/CodeBuildServiceRole \
    --cache '{"type": "S3", "location": "my-codebuild-cache/cache"}' \
    --logs-config '{
        "cloudWatchLogs": {
            "status": "ENABLED",
            "groupName": "/aws/codebuild/my-app-build"
        }
    }'

3. CodePipeline

3.1 Pipeline Complet

# Créer le rôle IAM
aws iam create-role \
    --role-name CodePipelineServiceRole \
    --assume-role-policy-document '{
        "Version": "2012-10-17",
        "Statement": [{"Effect": "Allow", "Principal": {"Service": "codepipeline.amazonaws.com"}, "Action": "sts:AssumeRole"}]
    }'

# Créer le pipeline
aws codepipeline create-pipeline --pipeline '{
    "name": "my-app-pipeline",
    "roleArn": "arn:aws:iam::123456789012:role/CodePipelineServiceRole",
    "artifactStore": {
        "type": "S3",
        "location": "my-pipeline-artifacts"
    },
    "stages": [
        {
            "name": "Source",
            "actions": [{
                "name": "GitHub",
                "actionTypeId": {
                    "category": "Source",
                    "owner": "AWS",
                    "provider": "CodeStarSourceConnection",
                    "version": "1"
                },
                "configuration": {
                    "ConnectionArn": "arn:aws:codestar-connections:eu-west-1:123456789012:connection/xxx",
                    "FullRepositoryId": "myorg/myapp",
                    "BranchName": "main",
                    "OutputArtifactFormat": "CODE_ZIP"
                },
                "outputArtifacts": [{"name": "SourceOutput"}]
            }]
        },
        {
            "name": "Build",
            "actions": [{
                "name": "BuildAndTest",
                "actionTypeId": {
                    "category": "Build",
                    "owner": "AWS",
                    "provider": "CodeBuild",
                    "version": "1"
                },
                "configuration": {
                    "ProjectName": "my-app-build"
                },
                "inputArtifacts": [{"name": "SourceOutput"}],
                "outputArtifacts": [{"name": "BuildOutput"}]
            }]
        },
        {
            "name": "Deploy-Staging",
            "actions": [{
                "name": "DeployToStaging",
                "actionTypeId": {
                    "category": "Deploy",
                    "owner": "AWS",
                    "provider": "ECS",
                    "version": "1"
                },
                "configuration": {
                    "ClusterName": "staging-cluster",
                    "ServiceName": "my-app-staging",
                    "FileName": "imagedefinitions.json"
                },
                "inputArtifacts": [{"name": "BuildOutput"}]
            }]
        },
        {
            "name": "Approval",
            "actions": [{
                "name": "ManualApproval",
                "actionTypeId": {
                    "category": "Approval",
                    "owner": "AWS",
                    "provider": "Manual",
                    "version": "1"
                },
                "configuration": {
                    "NotificationArn": "arn:aws:sns:eu-west-1:123456789012:pipeline-approvals",
                    "CustomData": "Please review staging deployment before production"
                }
            }]
        },
        {
            "name": "Deploy-Production",
            "actions": [{
                "name": "DeployToProduction",
                "actionTypeId": {
                    "category": "Deploy",
                    "owner": "AWS",
                    "provider": "CodeDeployToECS",
                    "version": "1"
                },
                "configuration": {
                    "ApplicationName": "my-app-deploy",
                    "DeploymentGroupName": "my-app-production",
                    "TaskDefinitionTemplateArtifact": "BuildOutput",
                    "TaskDefinitionTemplatePath": "taskdef.json",
                    "AppSpecTemplateArtifact": "BuildOutput",
                    "AppSpecTemplatePath": "appspec.yml"
                },
                "inputArtifacts": [{"name": "BuildOutput"}]
            }]
        }
    ]
}'

3.2 Blue/Green Deployment avec ECS

# appspec.yml pour ECS Blue/Green
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: "app"
          ContainerPort: 80
        PlatformVersion: "LATEST"

Hooks:
  - BeforeInstall: "LambdaFunctionToValidateBeforeInstall"
  - AfterInstall: "LambdaFunctionToValidateAfterInstall"
  - AfterAllowTestTraffic: "LambdaFunctionToValidateAfterTestTrafficStarts"
  - BeforeAllowTraffic: "LambdaFunctionToValidateBeforeAllowingProductionTraffic"
  - AfterAllowTraffic: "LambdaFunctionToValidateAfterAllowingProductionTraffic"
// taskdef.json
{
    "family": "my-app",
    "networkMode": "awsvpc",
    "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
    "taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
    "containerDefinitions": [
        {
            "name": "app",
            "image": "<IMAGE1_NAME>",
            "portMappings": [
                {"containerPort": 80, "protocol": "tcp"}
            ],
            "essential": true,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/my-app",
                    "awslogs-region": "eu-west-1",
                    "awslogs-stream-prefix": "ecs"
                }
            }
        }
    ],
    "requiresCompatibilities": ["FARGATE"],
    "cpu": "256",
    "memory": "512"
}

4. Déploiement sur EKS

4.1 Pipeline EKS avec kubectl

# buildspec-eks.yml
version: 0.2

phases:
  install:
    commands:
      - curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
      - chmod +x kubectl && mv kubectl /usr/local/bin/

  pre_build:
    commands:
      - aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_DEFAULT_REGION
      - aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_REGISTRY

  build:
    commands:
      - docker build -t $ECR_REGISTRY/$IMAGE_REPO:$CODEBUILD_RESOLVED_SOURCE_VERSION .
      - docker push $ECR_REGISTRY/$IMAGE_REPO:$CODEBUILD_RESOLVED_SOURCE_VERSION

  post_build:
    commands:
      - sed -i "s|IMAGE_TAG|$CODEBUILD_RESOLVED_SOURCE_VERSION|g" k8s/deployment.yaml
      - kubectl apply -f k8s/
      - kubectl rollout status deployment/my-app -n production --timeout=5m

4.2 ArgoCD Integration

# Application ArgoCD
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp-gitops.git
    targetRevision: HEAD
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

5. Exercices Pratiques

Exercice 1 : Pipeline CI/CD Complet

Objectif

Créer un pipeline qui build, teste et déploie une application sur ECS.

Solution
# Voir sections précédentes pour le code complet
# Étapes clés :
# 1. Créer le projet CodeBuild avec buildspec.yml
# 2. Configurer la connexion GitHub/CodeCommit
# 3. Créer le pipeline CodePipeline avec tous les stages
# 4. Configurer CodeDeploy pour Blue/Green sur ECS
# 5. Ajouter des notifications SNS pour les approbations

6. Résumé

Service Description Use Case
CodeCommit Git repository managé Source control AWS-native
CodeBuild Service de build CI - Build, test, package
CodeDeploy Déploiement automatisé Blue/Green, Canary
CodePipeline Orchestration CI/CD Pipeline end-to-end
CodeArtifact Repository d'artefacts npm, PyPI, Maven

Précédent Suivant
← Module 6 : TP Final Module 8 : Serverless →

← Module 6 : TP Final - Infrastructure ... Module 8 : Serverless - Lambda & Fargate →

Retour au Programme