LoginSignup
27
24

More than 5 years have passed since last update.

AWSにて、S3のイベントをトリガにLambdaからJenkinsのジョブの実行を導入しました

Last updated at Posted at 2016-03-25

参考URL

これをやった経緯

  • 他システムとのデータ連携をCSVファイルを介して、データ連携する要件が発生
  • そのデータをS3のような「耐久性」、「可用性」、「安全性」の高いストレージを用意して受け渡す必要があった
  • 担当システムではバッチ処理をJenkinsで管理しており、今回もそれを利用したかった
  • 定期実行でも対応はできたが、それだとかっこ悪いなーと思ったので、S3にファイル配置されたときだけ自動実行したかった

手段検討

参考URL1つ目のS3のイベントをLambdaで拾って、hipchatに通知できるなら、Jenkinsのジョブもたたけるじゃないか!
ということで、下記のような仕組みを妄想w

  1. 他システムより、CSVファイルがS3にアップロードされる
  2. S3のイベントにて、オブジェクトがCreateされた際に、イベント発行
  3. 発行されたイベントをLambdaで受取り、Jenkinsのジョブをキック。ついでにHipchatにも通知する。

実現方法

  1. まずは、該当のCSVファイルを取込むバッチ処理を作ることだが、それは割愛します。
  2. 処理の流れの初めであるS3へのイベントの設定から行いたいが、手順は逆で、Lambdaのfunctionから作成します。
    Lambda Management Console.jpg

  3. 今回は言語は「Node.js」、コードはインラインで作成せず、ローカルで作成して、ZIPファイルでアップしました。
    また、Lambda用のロールの作成がちょっとてこずりました。そこについては後述しますが、今は一旦、「lambda_s3_exec_role」で作成してください。
    Lambda Management Console (3).jpg

  4. Create Functionを選択し、Lambda関数を作成してください。
    Lambda Management Console (2).jpg

  5. ここまできたら、後はLambdaで動かすコードを作成しましょう!下記のコードについては、、NodeJSが初めてのため、ソースが初心者コードになってます。。ご指摘、改善内容提案などいただけると嬉しいです!

node.js
console.log('Loading event');
var prodaws = require('aws-sdk');
var s3 = new prodaws.S3({apiVersion: '2006-03-01'});
var request = require('request');
var async = require('async');
var auth = "Basic " + new Buffer("XXXXXX:XXXXXX").toString("base64");

exports.handler = function(event, context) {
    // *************************************************************************
    // * Lambdaをno VPC以外で動かす場合には必要になりそう
    // * VPC指定だと、privateサブネット経由だと、NATインスタンスのGIPになるっぽい
    // *************************************************************************
/*
    var exec = require('child_process').exec;
    var cmd = "curl inet-ip.info";
    var child = exec(cmd, function(error, stdout, stderr) {
        if (!error) {
            // IP => CIDR
            var ipAddress = stdout.replace(/[\n\r]/g,"") + "/32";
            console.log('Lambda Global Ip: ' + ipAddress);
            var params = {
              GroupId: 'sg-XXXXXX',
              IpPermissions: [
                {
                  FromPort: 80,
                  ToPort: 80,
                  IpProtocol: 'tcp',
                  IpRanges: [
                    {
                      CidrIp: ipAddress
                    },
                  ]
                },
              ],
            };
            ec2.authorizeSecurityGroupIngress(params, function(err, data) {
                if (err){
                    console.log(err);
                } else {
                    console.log('Success Add Lambda Global Ip to jenkins security group! ');
                }
            });
        } else {
            console.log("error code: " + error.code + ", err: " + error);
            context.done(error,'lambda');
        }
    });
*/
    console.log('Received event:');
    console.log(JSON.stringify(event, null, '  '));
    // Get the object from the event and show its content type
    var bucket = event.Records[0].s3.bucket.name;
    console.log(bucket);
    var key = event.Records[0].s3.object.key;
    console.log(key);
    console.log(event.Records[0].s3.object);
    console.log('Sending hipchat:');
    async.waterfall([
        function download(callback){
            s3.getObject({Bucket:bucket, Key:key},
               function(err,data) {
                 if (err) {
                     console.log('error getting object ' + key + ' from bucket ' + bucket +
                        '. Make sure they exist and your bucket is in the same region as this function.');
                     context.done('error','error getting file'+err);
                 }
                 else {
                     console.log('CONTENT TYPE:',data.ContentType);
                     callback(null, 1);
                 }
               }
            );
        },
        function postjenkins(arg1, callback){
            console.log('Start kick jenkins jobs:');
            request.post({
                    url : 'http://jenkins.example.com/job/test-echo-job/build',
                    headers: { 'Authorization': auth },  // Basic認証が必要な場合は入れます。
                    form: { }
                },
                function (error, response, body) {
                    if (!error && response.statusCode == 200) {
                        console.log(body);
                    }
                    if (error) {
                        console.log(error);
                    }
                }
            );
            console.log('End kick jenkins jobs:');
            callback(null, 2);
        },        
        function posthipchat(arg1, callback){
            console.log('Start Sending hipchat:');
            request.post(
                'https://api.hipchat.com/v1/rooms/message?format=json&auth_token=XXXXXXXXXXX',
                { form: { 'room_id': 'XXXXXXX' , 'from': 'Lambda Script' , 'message': 'おら Lambda.よろしくね。 バッチを起動します' , 'message_format': 'text' } },
                function (error, response, body) {
                    if (!error && response.statusCode == 200) {
                        console.log(body);
                    }
                }
            );
            console.log('End Sending hipchat:');
            callback(null, 'done');
        }
    ],
        function(err){
            if(err){ console.log('Error:' + err); }
            console.log('all done....');
            context.done(null,'process all done....');
        }
    );
};
  1. ひたすらLambdaの画面にて、アップロード、テスト、アップロード、テスト・・・・・
  2. 動いたら、こんな感じでHipchatで反応があります!

caad - HipChat (1).jpg

  1. ここまで動かせたら、後は、S3のイベントでフックして実行されるように設定をするだけです。送信先を作成したLambda関数にして、イベントは「ObjectCreated」でいけます!

S3 Management Console.jpg

注意点

  • 今回は、LambdaがAWSのリソースへのアクセスとして、S3とVPCへアクセスをするため、双方がアクセス可能なロールを作成しています。それが、下記。このロールが必要なところに少しハマりました。。。
    IAM Management Console (1).jpg

  • また、上記のコード上でもコメントで記載してますが、今回はVPC内のNATインスタンス経由でJekinsやHipchatへアクセスすることで、Jenkinsのセキュリティグループへのアクセス許可を固定化できましたが、「No VPC」で利用する場合は、Lambdaが実行された際に、GlobalIPがわからないため、GlobalIPを取得し、さらにJenkinsのセキュリティグループでそれを許可させるという処理が必要になります!これもかなり悩みましたが、VPCへのアクセスにすることで、NATのGlobalIPになることがわかり、なんとか解決しました。

27
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
24