Jak wykonać proces CI/CD dla funkcji Lambda przy pomocy AWS CodePipline.
Napisaliśmy aplikację serverless. Źródła trzymamy oczywiście w repozytorium kodu, najprawdopodobniej w GitHub. Chemy wdrożyć ją w chmurze AWS. Chcemy też, aby od razu po wrzuceniu zmian do repozytorium kodu, nowa wersja aplikacji, po przejściu przez testy, pojawiła się „na produkcji”. Potrzebujemy więc sławnego CI/CD. Jak wykonać proces CI/CD dla funkcji Lambda przy pomocy AWS CodePipline? W sumie jest to dość proste. Pokażę w jaki sposób to zrobić.
Aplikacja
Nasza aplikacja jest bardzo prosta. Składa się z jednej funkcji Lambda oraz endpointa w usłudze APIGateway. Całość zdefiniowana jest oczywiście za pomocą Serverless Application Model. Definicja wygląda następująco:
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
Lambda:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: python3.7
Role:
Fn::GetAtt:
- LambdaExecutionRole
- Arn
Events:
GetEvent:
Type: Api
Properties:
Path: /
Method: get
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'ddlambdarole'
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Sama funkcja też jest bardzo prosta. Zwraca po prostu trochę tekstu do API:
import json
def handler(event, context):
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps(
{'message': 'Hello DevDiaries readers'}
),
}
Dodatkowo zaimplementujemy jeszcze testy jednostkowe, które będą przeprowadzane w procesie budowania aplikacji. Test sprawdzi po prostu, czy w zwracanym ciągu znaków jest zwrot DevDiaries:
import unittest
import index
class TestHandlerCase(unittest.TestCase):
def test_response(self):
print("testing response.")
result = index.handler(None, None)
print(result)
self.assertEqual(result['statusCode'], 200)
self.assertEqual(result['headers']['Content-Type'], 'application/json')
self.assertIn('DevDiaries', result['body'])
if __name__ == '__main__':
unittest.main()
Cały kod jest oczywiście dostępny na GitHub i możecie sami spróbować wdrożyć cały proces CI/CD.
Mamy więc nasz kod, czas uruchomić AWS i wdrożyć naszą aplikację.
Wdrażamy proces CI/CD
Aby wygodnie połączyć GitHuba z AWS skorzystamy z usługi AWS CodePipeline. Zanim jednak z niej skorzystamy musimy się przygotować. Potrzebny nam będzie bucket S3 na artefakty oraz rola dla usługi CloudFormation, która pozwoli jej na działanie.
Rola dla CloudFormation
W usłudze IAM przechodzimy do zakładki Roles i klikamy Create role. Jako trusted entity wybieramy CloudFormation. Jako Permissions dodajemy politykę AWSLambdaExecute. Kilkamy Next, wpisujemy jakąś nazwę dla roli. Gdy rola jest gotowa, otwieramy ją i w zakładce Permissions klikamy Add inline policy. Przechodzimy do zakładki JSON i wklejamy poniższy dokument:
{
"Statement": [
{
"Action": [
"apigateway:*",
"codedeploy:*",
"lambda:*",
"cloudformation:CreateChangeSet",
"iam:GetRole",
"iam:CreateRole",
"iam:DeleteRole",
"iam:PutRolePolicy",
"iam:AttachRolePolicy",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PassRole",
"s3:GetObjectVersion",
"s3:GetBucketVersioning"
],
"Resource": "*",
"Effect": "Allow"
}
],
"Version": "2012-10-17"
}
Zapisujemy zmiany, tworzymy bucket S3 na artefakty (to chyba każdy potrafi 😉 ) i możemy przejść do usługi AWS CodePipeline.
Konfigurujemy proces CI/CD
Po jej otwarciu musimy stworzyć nowy pipeline. Klikamy Create pipeline i na początek wpisujemy nazwę dla naszego procesu.
Klikamy Next i wybieramy skąd mają być pobierane źródła aplikacji. W naszym przypadku będzie to GitHub.
Zostawiamy resztę opcji bez zmian i klikamy Connect to GitHub. Autoryzujemy AWS do podłączenia się do naszego konta. Trzeba to zrobić osobno dla każdego regionu AWS.
Następnie wybieramy repozytorium i branch, który ma być śledzony.
Kolejny krok to utworzenie projektu w usłudze CodeBuild. Tam będzie kompilowana i testowana nasza aplikacja.
Klikamy Create project I w zależności od potrzeb wybieramy odpowiednie ustawienia platformy dla naszego builda.
Klikamy Continue to CodePipeline i rozpoczynamy proces konfiguracji deploymentu.
Nasza aplikacja będzie wdrożona za pomocą usługi CloudFormation, którą wybieramy spośród innych możliwości. Po ustawieniu pożądanego regionu jako akcję wybieramy Create or replace a changeset. Później to rozbudujemy.
Dalej kolejno definiujemy nazwy dla stacka oraz changeseta. Podajemy także gdzie znajduje się template dla naszej aplikacji. W naszym przypadku to BuildArtifact::outputtemplate.yaml.
Opcjonalnie, w zależności od potrzeb dodajemy Capabilities, czyli upoważniamy CloudFormation do tworzenia zasobów w usłudze IAM. Klikamy Next I Create pipeline.
Po chwili nasz pipeline pojawi się i zacznie pracę. Niestety musimy wykonać jeszcze dwie rzeczy, aby mógł przebiec pomyślnie.
Przede wszystkim musimy dodać uprawnienia dla usługi CodeBuild do zapisu plików w S3.
Przechodzimy do naszego projektu w CodeBuild i w zakładce Build details
odszukujemy rolę
klikamy w nią. Następnie dodajemy do niej politykę AmazonS3FullAccess. Teraz proces budowy aplikacji będzie miał uprawnienia do zapisu w naszym buckecie w usłudze S3. Sam bucket definiujemy w pliku buildspec.yaml jako zmienną:
- export S3_BUCKET=bucket_name
Wracamy do naszego pipeline i musimy dokończyć konfigurację procesu deploymentu. Do tej pory zdefiniowaliśmy proces jako tworzenie changeseta. Musimy dodać jeszcze proces, który ten changeset uruchomi i zniemi nasze środowisko w AWS. Przechodzimy do edycji Deploy
I dodajemy akcję. Klikamy Add action group I wypełniamy poszczególne pola jak poniżej. Używamy oczywiście odpowiednich nazw zarówno dla stacka jak i changeseta.
Zapisujemy zmiany i możemy wypróbować czy nasz proces CI/CD zadziała.
Po chwili (dłuższej lub krótszej) nasza aplikacja serverless będzie zainstalowana na usługach AWS. OD tej pory każda zmiana kodu w wybrany repozytorium i branchu będzie uruchamiała proces budowania i deploymentu naszej aplikacji.
Podsumowanie
Jak widać cała konfiguracja jest dość prosta. Warto z niej korzystać. Początkowy wkład pracy szybko zwróci się z nawiązką przy kolejnych zmianach w naszej aplikacji. Nic nie stoi na przeszkodzie, aby taki proces połączyć ze źródłami trzymanymi na przykład w BitBucket, a nawet w S3.