LoginSignup
12
14

More than 5 years have passed since last update.

AnsibleでAWS Lambda(python)をdeployしてみる

Last updated at Posted at 2016-11-13

なぜansible?

ansible2.2からaws lambdaがデプロイできるようになったので試してみました

良かったこと

  • 自分がansibleを普段使っていたので学習コスト低かった.
  • host: localhost, connection: localにするとローカルで動かすスクリプトとして使える.
  • lambdaに同梱する 追加パッケージの用意, zipで固めるなどもansibleで記述できる.
  • テンプレート使っていろいろ処理・変数を取り回せる
    • vaultで秘匿情報とか埋め込める
  • ansibleのモジュールを使うとaws cliを叩くより扱いやすい.

辛かったこと

  • まだansible付属のmoduleだけじゃ無理なことが多い
    • 足りない部分はaws cliを叩くことに
    • 幸運なことにaws cliのレスポンスがjsonなのでansibleで加工しやすい
  • 各モジュールの返り値(registerで受け取れる物) の仕様が一定じゃないので辛い時がある.

とりあえず試しにansibleで毎日12時にawsの料金をslackに通知するlambdaをデプロイしてみます.
(ここではlambdaのコードには触れてないです. githubには入れてあります)

実験環境:ansible==2.2.0.0
コード:https://github.com/kikusu/lambda-aws-billing-to-slack

zipでコードを固める

自分はLamdaに追加パッケージを入れる場合は.gitignoreし易いようにsite-packageを切ってそこにれています.

├ site-packages/
│ └ (追加pkg)
├ lambda_function.py
└ requirements.txt

pipで必要なpkgを取ってきた後全部zipに圧縮します.

pip install -r requirements.txt -t site-package
zip -ur lambda.zip *

ansibleで書くとこんな感じ

- name: install required pkg
  pip:
    requirements: "requirements.txt"
    extra_args: "-t site-packages"
    chdir: "{{ source_dir }}"

- name: create lambda.zip
  shell: zip -ur lambda.zip *
  register: create_lambda_zip
  changed_when: create_lambda_zip.rc != 12
  failed_when: create_lambda_zip.rc not in [0, 12]
  args:
    chdir: "{{ source_dir }}"

zipに-uつけているのは更新がない時zipが変わらないようにしてlambdaのversionupのデプロイが走らないようにするためです.

Lambda用にRoleを作る

ansibleのiam, iam_policyを使ってlambdaを実行できるroleを作ります.

- name: create role
  iam:
    iam_type: role
    name: "{{ role_name }}"
    state: present
    trust_policy:
      Version: '2012-10-17'
      Statement:
      - Action: sts:AssumeRole
        Effect: Allow
        Principal:
          Service: lambda.amazonaws.com
      - Action: sts:AssumeRole
        Effect: Allow
        Principal:
          Service: events.amazonaws.com

- name: attatch policy
  iam_policy:
    iam_type: role
    iam_name: "{{ role_name }}"
    state: present
    policy_name: "{{ policy_name }}"
    policy_json:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
          Resource: '*'
        - Effect: Allow
          Action:
            - cloudwatch:GetMetricStatistics
          Resource: '*'
        - Effect: Allow
          Action:
            - s3:GetObject
          Resource: '*'
- name: get role arn
  command: "aws iam get-role --role-name {{ role_name }}"
  changed_when: false
  register: role

- set_fact:
    role_arn: "{{ (role.stdout|from_json).Role.Arn }}"

各statementにlambdaを実行するために必要な権限を追加しておいてください.

roleを作ったあとroleのarnをawsコマンドで取得します.レスポンスがjsonなので簡単にエスケープすることができます.
v2.2.0.0現在, role作成時のみiam moduleからARNが取得できる状態になっており2回以降はモジュールからARNが取得できないのでawsコマンドで取得するようにします.

LambdaをDeployする

lambdaモジュールを使ってデプロイします. 簡単ですね.

- name: create lambda function
  lambda:
    name: "{{ lambda_name }}"
    zip_file: "{{ source_dir }}/lambda.zip"
    handler: lambda_function.lambda_handler
    runtime: python2.7
    role: "{{ role_arn }}"
    timeout: 5

- name: get lambda arn
  command: "aws lambda get-function --function-name {{ lambda_name }}"
  changed_when: false
  register: lambda

- set_fact:
      lambda_arn: "{{ (lambda.stdout|from_json).Configuration.FunctionArn }}"

lambdaモジュールはlambda関数に変更が無かろうがARNを取得することができます.
ただしv2.2.0.0現在,その取得できるARNの値が変更の有無で違います.

# 変更無
"function_arn": "arn:aws:lambda:*******:********:function:AWSBillingToSlack",

# 変更有
"function_arn": "arn:aws:lambda:*******:********:function:AWSBillingToSlack:2",

変更があった場合はARNの最後にバージョンの数字がついてきます.orz
changedかどうかでarnの値を整形する方法もあると思いますが 一旦lambdaの方もawsコマンドを叩いてARNを取得するようにしました.

定時実行の設定をする

後はcloudwatch eventでcronイベントをセットしてlambda実行権限を付与すれば終わりです.
cronの時間設定はUTCなのでJSTと違うことに注意です.
add-permissionはモジュールがなさそうなのでawsコマンドを使って実行します.

- name: create cloudwatch event
  cloudwatchevent_rule:
    name: "{{ lambda_name }}"
    schedule_expression: cron(0 3 * * ? *)
    description: "{{ lambda_name }} cron"
    role_arn: "{{ role_arn }}"
    targets:
      - id: "{{ lambda_name }}"
        arn: "{{ lambda_arn }}"
  register: event

- name: aws lambda add-permission
  command: >-
    aws lambda add-permission
    --function-name {{ lambda_name }}
    --statement-id 'cron'
    --action "lambda:InvokeFunction"
    --principal events.amazonaws.com
    --source-arn {{ event.rule.arn }}
  register: add_permission
  changed_when: "{{ 'provided already exists' not in add_permission.stderr }}"
  failed_when: "{{ add_permission.rc != 0 }} and {{ 'provided already exists' not in add_permission.stderr }}"

やってみて

  • デプロイ前に事前ビルド(zipに固めるところまで)できるのは良い
  • テンプレートで role名やarnを引き回せるのは楽
  • 各モジュールのレスポンスの方針が一定だと嬉しい (変更がある場合でもない場合でもARNを返すとか)
  • シェルスクリプトより保守しやすい
12
14
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
12
14