Terraform
Pipeline de build

Código para a pipeline de build

Definição do pipeline

modelbuild_ci_pipeline.tf
resource "aws_codepipeline" "sm_ci_pipeline" {
  name     = "modelbuild-pipeline"
  role_arn = aws_iam_role.tf_mlops_role.arn
  tags = {
    Environment = var.env
  }

  artifact_store {
    location = aws_s3_bucket.artifacts_bucket.bucket
    type     = "S3"
  }

  stage {
    name = "Source"
    action {
      category = "Source"
      configuration = {
        RepositoryName         = var.build_repository_name
        BranchName             = var.repository_branch
        PollForSourceChanges   = true
      }

      input_artifacts = []
      name            = "Source"
      output_artifacts = [
        "SourceArtifact",
      ]
      owner     = "AWS"
      provider  = "CodeCommit"
      run_order = 1
      version   = "1"
    }
  }

  stage {
    name = "Build"

    action {
      category = "Build"
      configuration = {
        "ProjectName" = "tf-mlops-modelbuild"
      }
      input_artifacts = [
        "SourceArtifact",
      ]
      name = "Build"
      output_artifacts = [
        "BuildArtifact",
      ]
      owner     = "AWS"
      provider  = "CodeBuild"
      run_order = 1
      version   = "1"
    }
  }

}

Aqui definimos todos os aspectos de nossa pipeline, começando pela etapa de Source, definimos que a fonte vem de um repositório AWS CodeCommit, e colocamos seu nome, a branch a ser utilizada e o PollForSourceChanges serve para a pipeline buscar por mudanças no código fonte e rodar a pipeline quando detectar mudanças. Em seguida, temos a fase de build, que aqui colocamos o nome do projeto, porém vamos criar ela no próximo arquivo. Por fim, essa etapa não possui fase de deploy.

Definição do CodeBuild

modelbuild_buildspec.yml
version: 0.2
 
phases:
  install:
    runtime-versions:
      python: 3.8
    commands:
      - pip install --upgrade --force-reinstall . awscli
      # Tire a linha abaixo quando for utilizar outros dados, esses são só para exemplo de pipeline.
      - aws s3 cp s3://sagemaker-sample-files/datasets/tabular/synthetic/churn.txt s3://${ARTIFACT_BUCKET}/sagemaker/DEMO-xgboost-churn/data/RawData.csv 
  build:
    commands:
      - export PYTHONUNBUFFERED=TRUE
      - export SAGEMAKER_PROJECT_NAME_ID="${SAGEMAKER_PROJECT_NAME}-${SAGEMAKER_PROJECT_ID}" 
      - |
        run-pipeline --module-name pipelines.customer_churn.pipeline \
          --role-arn ${SAGEMAKER_PIPELINE_ROLE_ARN} \
          --tags "[{\"Key\":\"sagemaker:project-name\", \"Value\":\"${SAGEMAKER_PROJECT_NAME}\"}, {\"Key\":\"sagemaker:project-id\", \"Value\":\"${SAGEMAKER_PROJECT_ID}\"}]" \
          --kwargs "{\"region\":\"${AWS_REGION}\",\"role\":\"${SAGEMAKER_PIPELINE_ROLE_ARN}\",\"default_bucket\":\"${ARTIFACT_BUCKET}\",\"pipeline_name\":\"${SAGEMAKER_PROJECT_NAME_ID}\",\"model_package_group_name\":\"${SAGEMAKER_PROJECT_NAME_ID}\",\"base_job_prefix\":\"${SAGEMAKER_PROJECT_NAME_ID}\"}"
      - echo "Create/Update of the SageMaker Pipeline and execution completed."

Neste arquivo yml, definimos todas as etapas que o build deve executar, desde a instalação de dependências, até a execução da pipeline do sagemaker (opens in a new tab).

modelbuild_codebuild.tf
data "template_file" "buildspec" {
  template = file("modelbuild_buildspec.yml")
  vars = {
    env = var.env
    SAGEMAKER_PROJECT_NAME=var.project_name
    SAGEMAKER_PROJECT_ID=var.project_id
    ARTIFACT_BUCKET=var.artifacts_bucket_name
    SAGEMAKER_PIPELINE_ROLE_ARN=aws_iam_role.tf_mlops_role.arn
    AWS_REGION=var.region
    SAGEMAKER_PROJECT_NAME_ID="${var.project_name}-${var.project_id}"
  }
}

resource "aws_codebuild_project" "tf_mlops_modelbuild" {
  badge_enabled  = false
  build_timeout  = 60
  name           = "tf-mlops-modelbuild"
  queued_timeout = 480
  service_role   = aws_iam_role.tf_mlops_role.arn
  tags = {
    Environment = var.env
  }

  artifacts {
    encryption_disabled    = false
    name                   = "tf-mlops-modelbuild-${var.env}"
    override_artifact_name = false
    packaging              = "NONE"
    type                   = "CODEPIPELINE"
  }

  environment {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/amazonlinux2-x86_64-standard:2.0"
    image_pull_credentials_type = "CODEBUILD"
    privileged_mode             = false
    type                        = "LINUX_CONTAINER"
    environment_variable {
      name  = "environment"
      type  = "PLAINTEXT"
      value = var.env
    }
    environment_variable {
      name  = "SAGEMAKER_PROJECT_NAME"
      type  = "PLAINTEXT"
      value = var.project_name
    }
    environment_variable {
      name  = "SAGEMAKER_PROJECT_ID"
      type  = "PLAINTEXT"
      value = var.project_id
    }
    environment_variable {
      name  = "ARTIFACT_BUCKET"
      type  = "PLAINTEXT"
      value = var.artifacts_bucket_name
    }
    environment_variable {
      name  = "SAGEMAKER_PIPELINE_ROLE_ARN"
      type  = "PLAINTEXT"
      value = aws_iam_role.tf_mlops_role.arn
    }
    environment_variable {
      name  = "AWS_REGION"
      type  = "PLAINTEXT"
      value = var.region
    }
  }

  logs_config {
    cloudwatch_logs {
      status = "ENABLED"
    }

    s3_logs {
      encryption_disabled = false
      status              = "DISABLED"
    }
  }

  source {
    buildspec           = data.template_file.buildspec.rendered
    git_clone_depth     = 0
    insecure_ssl        = false
    report_build_status = false
    type                = "CODEPIPELINE"
  }
}

Por fim, aqui definimos a etapa de build, onde no inicio do arquivo todas as variáveis do .yml são preenchidas por variáveis do terraform. Em seguida são configurados todos os aspectos do build, como o tipo de instância utilizada, a imagem utilizada na instância, todas as variáveis de ambiente utilizadas na pipeline, configura também os logs no cloudwatch e no desabilita eles no S3 e coloca o CODEPIPELINE como a fonte do código para o build.