在本地调试python 3.7 AWS lambda

时间:2019-06-14 09:21:42

标签: python amazon-web-services aws-lambda

我使用运行时Python 3.7使用main.py和入口点handler函数为AWS Lambda创建了一个zip文件。

使用3.7.3 python将zip文件打包在EC2上的Amazon Linux映像中。

我在AWS lambda上运行时遇到一些错误,因此决定是否可以在本地运行该功能。

我的main.py在下面:

import datetime
import logging
import os
import re
import subprocess

import boto3
import certbot.main
import raven

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def list_files(folder_path):
  onlyfiles = [f for f in listdir(folder_path) if path.isfile(path.join(folder_path, f))]
  logger.info('## path')
  logger.info(onlyfiles)


def read_and_delete_file(path):
  with open(path, 'r') as file:
    contents = file.read()
  os.remove(path)
  return contents


def provision_cert(email, domains):
  certbot.main.main([
    'certonly',                             # Obtain a cert but don't install it
    '-n',                                   # Run in non-interactive mode
    '--agree-tos',                          # Agree to the terms of service,
    '--email', email,                       # Email
    '--dns-route53',                        # Use dns challenge with route53
    '-d', domains,                          # Domains to provision certs for
    # Override directory paths so script doesn't have to be run as root
    '--config-dir', '/tmp/config-dir/',
    '--work-dir', '/tmp/work-dir/',
    '--logs-dir', '/tmp/logs-dir/',
  ])

  first_domain = domains.split(',')[0]
  first_domain_cert_folder = re.sub('\*\.', '', first_domain)
  path = '/tmp/config-dir/live/' + first_domain_cert_folder + '/'
  logger.info('## path')
  logger.info(path)

  list_files(path)

  return {
    'certificate': read_and_delete_file(path + 'cert.pem'),
    'private_key': read_and_delete_file(path + 'privkey.pem'),
    'certificate_chain': read_and_delete_file(path + 'fullchain.pem')
  }

def should_provision(domains):
  existing_cert = find_existing_cert(domains)
  if existing_cert:
    now = datetime.datetime.now(datetime.timezone.utc)
    not_after = existing_cert['Certificate']['NotAfter']
    return (not_after - now).days <= 30
  else:
    return True

def find_existing_cert(domains):
  domains = frozenset(domains.split(','))

  client = boto3.client('acm')
  paginator = client.get_paginator('list_certificates')
  iterator = paginator.paginate(PaginationConfig={'MaxItems':1000})

  for page in iterator:
    for cert in page['CertificateSummaryList']:
      cert = client.describe_certificate(CertificateArn=cert['CertificateArn'])
      sans = frozenset(cert['Certificate']['SubjectAlternativeNames'])
      if sans.issubset(domains):
        return cert

  return None

def notify_via_sns(topic_arn, domains, certificate):
  process = subprocess.Popen(['openssl', 'x509', '-noout', '-text'],
    stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf8')
  stdout, stderr = process.communicate(certificate)

  client = boto3.client('sns')
  client.publish(TopicArn=topic_arn,
    Subject='Issued new LetsEncrypt certificate',
    Message='Issued new certificates for domains: ' + domains + '\n\n' + stdout,
  )

def upload_cert_to_acm(cert, domains):
  existing_cert = find_existing_cert(domains)
  certificate_arn = existing_cert['Certificate']['CertificateArn'] if existing_cert else None

  client = boto3.client('acm')
  acm_response = client.import_certificate(
    CertificateArn=certificate_arn,
    Certificate=cert['certificate'],
    PrivateKey=cert['private_key'],
    CertificateChain=cert['certificate_chain']
  )

  return None if certificate_arn else acm_response['CertificateArn']

def handler(event, context):
  try:
    domains = os.environ['LETSENCRYPT_DOMAINS']
    if should_provision(domains):
      cert = provision_cert(os.environ['LETSENCRYPT_EMAIL'], domains)
      upload_cert_to_acm(cert, domains)
      notify_via_sns(os.environ['NOTIFICATION_SNS_ARN'], domains, cert['certificate'])
  except:
    client = raven.Client(os.environ['SENTRY_DSN'], transport=raven.transport.http.HTTPTransport)
    client.captureException()
    raise

zip文件约为20mb。我在AWS上发现resources关于本地调试的信息,但是坦率地说,我对如何入门感到迷茫。

尽管我对Python相当满意,但总体上我对AWS和Lambda不太熟悉。

我正在使用macOS操作系统,并使用Visual Studio代码作为编辑器。如果可以的话,我可以在Mac上创建virtualenv。

如何在本地macbook pro上调试lambda?

1 个答案:

答案 0 :(得分:1)

您已经发现,可以使用AWS SAM(与Docker)在本地进行调试。

下面是逐步入门指南:

先决条件

  1. 安装Docker:https://docs.docker.com/install/
  2. 安装AWS CLI:https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html
  3. 安装AWS SAM CLI:https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
  4. 安装pipenv:https://docs.pipenv.org/en/latest/install/

创建示例项目

可帮助您了解结构并在本地进行调试-然后,您可以用示例代码代替自己的代码。

sam init --runtime python3.7

添加依赖项

pipenv shell
pipenv install package-names

在本地运行和调试

pipenv lock -r > requirements.txt
sam build --manifest requirements.txt
sam local invoke HelloWorldFunction --event event.json

部署到AWS Lambda

根据需要制作一个新存储桶,以在其中存储功能代码:

aws s3 mb s3://bucket-name

创建并运行.sh脚本:

#!/bin/bash

pipenv lock -r > requirements.txt && sam build --manifest requirements.txt

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket bucket-name

sam deploy \
    --template-file packaged.yaml \
    --stack-name name-of-lambda-stack \
    --capabilities CAPABILITY_IAM \
    --region us-east-1

替换:

  • bucket-name,带有用于存储功能代码的S3存储桶的名称
  • name-of-lambda-stack,其名称为要部署到的AWS Lambda Stack
  • us-east-1和另一个region(如果需要)

您的功能现在已部署到AWS Lambda。