Skip to content

Instantly share code, notes, and snippets.

@myh-st
Last active May 16, 2023 09:07
Show Gist options
  • Save myh-st/47fbce4d9a85d6a9e64add9da4ce7d53 to your computer and use it in GitHub Desktop.
Save myh-st/47fbce4d9a85d6a9e64add9da4ce7d53 to your computer and use it in GitHub Desktop.
Sample CI/CD Glue CDK

เขียน aws cdk ในการเชื่อมต่อ git codcommit เพื่อทำ ci/cd aws glue ประมาณ 3 jobs

Genie

ตัวอย่างการใช้ AWS CDK สำหรับการเชื่อมต่อ Git CodeCommit และสร้าง CI/CD pipeline สำหรับ AWS Glue ที่ประกอบด้วย 3 jobs:

  1. สร้างไฟล์ cdk.json ที่รากของโปรเจคของคุณ (ถ้ายังไม่มี) และตั้งค่าคำสั่งเริ่มต้นสำหรับ CDK:
{
  "app": "npx ts-node --prefer-ts-exts infra/app.ts"
}
  1. สร้างไดเรกทอรี infra และสร้างไฟล์ infra/app.ts ดังนี้:
import * as cdk from 'aws-cdk-lib';
import { PipelineStack } from './pipeline-stack';

const app = new cdk.App();
new PipelineStack(app, 'PipelineStack');
  1. สร้างไฟล์ infra/pipeline-stack.ts ที่มีโค้ดต่อไปนี้:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as codecommit from 'aws-cdk-lib/aws-codecommit';
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline';
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions';
import * as glue from 'aws-cdk-lib/aws-glue';
import * as iam from 'aws-cdk-lib/aws-iam';

export class PipelineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create a CodeCommit repository
    const repo = new codecommit.Repository(this, 'GlueJobsRepo', {
      repositoryName: 'glue-jobs-repo',
    });

    // Create a CodePipeline
    const pipeline = new codepipeline.Pipeline(this, 'GlueJobsPipeline', {
      pipelineName: 'glue-jobs-pipeline',
    });

    // Add source stage to the pipeline
    const sourceOutput = new codepipeline.Artifact();
    const sourceAction = new codepipeline_actions.CodeCommitSourceAction({
      actionName: 'CodeCommit_Source',
      repository: repo,
      output: sourceOutput,
    });
    pipeline.addStage({
      stageName: 'Source',
      actions: [sourceAction],
    });

    // Add deploy stage to the pipeline
    const deployAction = new codepipeline_actions.AwsLambdaInvokeAction({
      actionName: 'Deploy_Glue_Jobs',
      lambda: this.createDeploymentFunction(),
      userParameters: {
        glueJobNames: ['job1', 'job2', 'job3'],
      },
    });
    pipeline.addStage({
      stageName: 'Deploy',
      actions: [deployAction],
    });
  }

  private createDeploymentFunction(): lambda.Function {
    // Create an IAM role for the Lambda function
    const role = new iam.Role(this, 'DeploymentFunctionRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
    });
    role.addToPolicy(
      new iam.PolicyStatement({
        actions: [
          'glue:CreateJob',
          'glue:UpdateJob',
          'glue:DeleteJob',
          'glue:GetJob',
        ],
        resources: ['*'],
      })
    );

    // Create the Lambda function
    return new lambda.Function(this, 'DeploymentFunction', {
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, 'deployment-function')),
      role: role,
    });
  }
}
  1. สร้างไดเรกทอรี infra/deployment-function และสร้างไฟล์ infra/deployment-function/index.js ที่มีโค้ดต่อไปนี้:
const AWS = require('aws-sdk');
const glue = new AWS.Glue();

exports.handler = async (event) => {
  const jobNames = event.userParameters.glueJobNames;

  for (const jobName of jobNames) {
    await createOrUpdateGlueJob(jobName);
  }
};

async function createOrUpdateGlueJob(jobName) {
  try {
    await glue.getJob({ JobName: jobName }).promise();
    console.log(`Updating Glue job: ${jobName}`);
    // Update the job with new settings
  } catch (error) {
    if (error.code === 'EntityNotFoundException') {
      console.log(`Creating Glue job: ${jobName}`);
      // Create the job with initial settings
    } else {
      throw error;
    }
  }
}
  1. ติดตั้ง dependencies ของ CDK และ TypeScript ด้วยคำสั่ง:
npm install aws-cdk-lib constructs typescript ts-node
  1. สั่งให้ CDK สร้าง stack ด้วยคำสั่ง:
npx cdk deploy

โค้ดด้านบนจะสร้าง CodeCommit repository และ CodePipeline ที่มี 2 stages: Source และ Deploy. ในขั้นตอนการ Deploy, Lambda function จะถูกเรียกใช้เพื่อสร้างหรืออัปเดต Glue jobs ตามที่ระบุใน userParameters. คุณสามารถปรับแต่งโค้ดใน createOrUpdateGlueJob เพื่อกำหนดค่าต่าง ๆ สำหรับ Glue jobs ของคุณ.

You

แล้ว code ของแต่ละ job ที่มี .py นั้นต้องเก็บไว้ที่ไหน

Genie

คุณสามารถเก็บไฟล์ .py ของแต่ละ job ในโฟลเดอร์ที่อยู่ใน CodeCommit repository ที่เราสร้างขึ้นก่อนหน้านี้ (ชื่อว่า glue-jobs-repo) โดยสามารถจัดระเบียบโค้ดของ Glue jobs ตามโครงสร้างโฟลเดอร์ที่คุณต้องการ ตัวอย่างเช่น:

glue-jobs-repo/
├── job1/
│   └── job1.py
├── job2/
│   └── job2.py
└── job3/
    └── job3.py

เมื่อคุณมีโค้ดของ Glue jobs ใน repository แล้ว คุณต้องปรับแต่ง Lambda function ใน infra/deployment-function/index.js เพื่ออ่านไฟล์ .py และส่งค่าให้กับ Glue jobs ที่ถูกสร้างหรืออัปเดต คุณสามารถทำได้ดังนี้:

  1. ใน infra/pipeline-stack.ts, ปรับแต่ง userParameters ใน AwsLambdaInvokeAction โดยเพิ่มพารามิเตอร์ jobScriptPaths:
const deployAction = new codepipeline_actions.AwsLambdaInvokeAction({
  actionName: 'Deploy_Glue_Jobs',
  lambda: this.createDeploymentFunction(),
  userParameters: {
    glueJobNames: ['job1', 'job2', 'job3'],
    jobScriptPaths: ['job1/job1.py', 'job2/job2.py', 'job3/job3.py'], // Add this line
  },
});
  1. ใน infra/deployment-function/index.js, ปรับแต่งฟังก์ชัน handler ให้รับพารามิเตอร์ jobScriptPaths และส่งค่าให้กับฟังก์ชัน createOrUpdateGlueJob:
exports.handler = async (event) => {
  const jobNames = event.userParameters.glueJobNames;
  const jobScriptPaths = event.userParameters.jobScriptPaths;

  for (let i = 0; i < jobNames.length; i++) {
    await createOrUpdateGlueJob(jobNames[i], jobScriptPaths[i]);
  }
};
  1. ปรับแต่งฟังก์ชัน createOrUpdateGlueJob ใน infra/deployment-function/index.js เพื่อรับพารามิเตอร์ jobScriptPath และใช้ค่านี้เมื่อสร้างหรืออัปเดต Glue job:
async function createOrUpdateGlueJob(jobName, jobScriptPath) {
  // ... (existing code)

  // Replace the existing try block with the following:
  try {
    await glue.getJob({ JobName: jobName }).promise();
    console.log(`Updating Glue job: ${jobName}`);
    // Update the job with new settings and script path
    await glue.updateJob({
      JobName: jobName,
      JobUpdate: {
        Command: {
          ScriptLocation: `s3://your-bucket-name/${jobScriptPath}`,
        },
      },
    }).promise();
  } catch (error) {
    if (error.code === 'EntityNotFoundException') {
      console.log(`Creating Glue job: ${jobName}`);
      // Create the job with initial settings and script path
      await glue.createJob({
        Name: jobName,
        Role: 'your-glue-job-role-arn',
        Command: {
          Name: 'glueetl',
          ScriptLocation: `s3://your-bucket-name/${jobScriptPath}`,
        },
      }).promise();
    } else {
      throw error;
    }
  }
}

อย่าลืมแทนที่ 'your-bucket-name' และ 'your-glue-job-role-arn' ด้วยชื่อของ S3 bucket และ ARN ของ IAM role ที่ใช้กับ Glue jobs ของคุณ

เมื่อคุณทำตามขั้นตอนด้านบนแล้ว โค้ดของ Glue jobs จะถูกอ่านจากไฟล์ .py ใน CodeCommit repository และส่งค่าให้กับ Glue jobs ที่ถูกสร้างหรืออัปเดต

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment