AWS Lambda Function is awesome and trust me if you are working on AWS, sooner or later you have to deal with AWS Lambda.
Due to the nature of being a PAAS service, we can't ignore the ways of Lambda deployment and it's test methods which is somehow more or less through the Lambda Function Console.
Off course there are ways to write code, test code and deploy code directly through IDEs, but keep in mind you still need an ACCESS Key and ACCESS Secret.
So what about the code base, how will we track the code changes done in Lambda Function itself.
In this post, we will cover the challenges with Lambda approaches for CICD and one of the my proposed solutions to address some part of it.
Let's know some of the challenges and their probable solutions.
- Lambda Deployment : We can use Terraform and CloudFormation for the same, then what's is the challenge ?
- CloudFormation :
- We can use Inline methods to put our Lambda Code under the Code block of ZipFile, but what about the 3rd party modules like panda, we can't use the code block under CloudFormation.
- One can still package those third party modules and other code together, but still one needs to upload those in S3 bucket and think of a way of handling changes before using it.
- Lambda Function Code base :
- We still need to have snapshots of our lambda function code to track the daily changes and to be later use in deployment pipeline.
- The moment when we talk about CICD, mostly pipeline uses git to get source code and then use it for further process like, checkout, build, release, deploy, test..
- Here the case is somehow different, due to nature of PAAS, we have to test our code's functionality in Lambda Console first, then it can be pushed to repository to save our source code.
- Yes, AWS SAM is yet another option of testing Lambda Function code within our local environment, but not in the case when Lambda is hosted in VPC and it uses other services to communicate.
- IAM Access Key Secrets or IAM Role attached to EC2 instance, from where the Jenkins job is triggered.
- GitHub Personal Access Token
- I assume, developer is initially testing his/her code's functionality on Lambda Console, Once Developer is okay, with his/her Lambda Function code, we will move to next step.
- SysAdmin/Developer can check-in his/her code directly from Lambda Function to GitHub repository using Jenkins Job.
- Jenkins Job has scripted pipeline attached to it, thus it will go through below stages.
- Stage : Check Out code to appropriate branch.
- Stage : Build Docker image from Docker File for Ansible.
- Stage : Run Ansible container from above created Docker image and run Ansible Playbook command to execute Ansible role and it's relative ansible tasks.
- Task 1 -
- Download Lambda Code from Lambda Console using Python Script, which is using boto3 module.
- Unzip the downloaded code into specific directory to track the changes as a file, else changes in zip file can't be tracked.
- Task 2 -
- Clone existing repository from git, replace the existing lambda source code with the newer one downloaded in above step.
- Git add, commit and push it into git repository.
Our Final Intention is to dump or lambda function code under src directory, that is lambda_folder/src
def gituser = env.GIT_USERNAME
def gituserpass = env.GIT_PASSWORD
def ACCESS_KEY = env.AWS_ACCESS_KEY
def KEY_ID = env.AWS_SECRET_ACCESS_KEY
def DEBUG_MODE = env.LOG_TYPE
node('master'){
try {
stage('Git Checkout'){
checkout scm
sh "git checkout lambda_deployer"
}
stage('build'){
sh "ls -ltr"
echo "Building docker image via dockerfile..."
sh "docker build -t ansible:2.10-$BUILD_ID ."
}
stage('deploy'){
echo "Infrastructure deployment started...."
wrap([$class: "MaskPasswordsBuildWrapper",
varPasswordPairs: [[password: gituserpass, var: gituserpass] ]]) {
sh "docker run --rm \
-e gituser=$gituser \
-e gituserpass=$gituserpass \
-e AWS_ACCESS_KEY_ID=$ACCESS_KEY \
-e AWS_SECRET_ACCESS_KEY=$KEY_ID \
-e AWS_DEFAULT_REGION='ap-south-1' \
ansible:2.10-$BUILD_ID ansible-playbook -$DEBUG_MODE --extra-vars 'env=dev1 git_username=${gituser} token=${gituserpass}' lambda_folder/root_lambda_project.yml"
}
}
}
catch (e){
echo "Error occurred - " + e.toString()
throw e
}
finally {
deleteDir()
sh 'docker rmi -f ansible:2.10-$BUILD_ID && echo "ansible:2.10-$BUILD_ID local image deleted."'
}
}
FROM python:3.7
RUN python3 -m pip install ansible==2.10 boto3 awscli
RUN rm -rf /usr/local/ansible/
copy lambda_folder /usr/local/ansible/lambda_folder
WORKDIR usr/local/ansible/
CMD ["ansible-playbook", "--version"]
---
- hosts: localhost
connection: local
gather_facts: False
roles:
- role-
---
region: us-east-1
function_name: ck-uat-LambdaAuthorizer
git_repo_name: aws-swa
git_repo_branch: lambda_deployer
""" Script to download individual Lambda Function and dump code in specified directory """ import os import sys from urllib.request import urlopen import zipfile from io import BytesIO import boto3 def get_lambda_functions_code_url(fn_name): client = boto3.client("lambda") functions_code_url = [] fn_code = client.get_function(FunctionName=fn_name)["Code"] fn_code["FunctionName"] = fn_name functions_code_url.append(fn_code) return functions_code_url def download_lambda_function_code(fn_name, fn_code_link, dir_path): function_path = os.path.join(dir_path, fn_name) if not os.path.exists(function_path): os.mkdir(function_path) with urlopen(fn_code_link) as lambda_extract: with zipfile.ZipFile(BytesIO(lambda_extract.read())) as zfile: zfile.extractall(function_path) if __name__ == "__main__": inp = sys.argv[1:] print("Destination folder {}".format(inp)) if inp and os.path.exists(inp[0]): dest = os.path.abspath(inp[0]) fc = get_lambda_functions_code_url(sys.argv[2]) for i, f in enumerate(fc): print("Downloading Lambda function {}".format(f["FunctionName"])) download_lambda_function_code(f["FunctionName"], f["Location"], dest) else: print("Destination folder doesn't exist")
---
- name: Read Variables
include_vars:
file: "role/vars/{{ env }}/main.yml"
- name: Download Lambda Function using Python script..
command:
argv:
- python3
- role/files/download_lambda.py
- src
- "{{ function_name }}"
---
- name: Git clone source repository..
command:
argv:
- git
- clone
- https://{{ git_username }}:{{ token }}@github.com/Jackuna/{{ git_repo_name }}.git
- -b
- "{{ git_repo_branch }}"
- name: Git add Lambda function source code to repo..
command: >
cp -r src {{ git_repo_name }}/lambda_folder
- name: Git add recent changes..
command: >
git add --all lambda_folder/src
args:
chdir: "{{ git_repo_name }}"
- name: Git Config username..
command: >
git config user.name {{ git_username }}
args:
chdir: "{{ git_repo_name }}"
- name: Git Config email..
command: >
git config user.email {{ git_username }}@cyberkeeda.com
args:
chdir: "{{ git_repo_name }}"
- name: Git commit recent changes..
command: >
git commit -m "Updated Latest code"
args:
chdir: "{{ git_repo_name }}"
- name: Git push recent changes..
command:
argv:
- git
- push
- https://{{ git_username }}:{{ token }}@github.com/Jackuna/{{ git_repo_name }}.git
- -u
- "{{ git_repo_branch }}"
args:
chdir: "{{ git_repo_name }}"
register: git_push_output
No comments:
Post a Comment