Skip to content

Commit e18651b

Browse files
committed
feat: containerize openai_api and push to AWS ECR
1 parent 1773b5c commit e18651b

16 files changed

+481
-0
lines changed

api/kubernetes/ecr.tf

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#------------------------------------------------------------------------------
2+
# written by: Lawrence McDaniel
3+
# https://lawrencemcdaniel.com/
4+
#
5+
# date: feb-2024
6+
#
7+
# usage: build and upload a Docker image to AWS Elastic Container Registry (ECR)
8+
#------------------------------------------------------------------------------
9+
locals {
10+
ecr_repo = "chat_api"
11+
ecr_source_directory = "${path.module}../python/openai_api/"
12+
13+
ecr_build_path = "${path.module}/ecr_build"
14+
ecr_build_script = "${local.ecr_build_path}/build.sh"
15+
}
16+
17+
resource "aws_ecr_repository" "chat_api" {
18+
name = local.ecr_repo
19+
image_tag_mutability = "IMMUTABLE"
20+
21+
image_scanning_configuration {
22+
scan_on_push = true
23+
}
24+
tags = var.tags
25+
26+
}
27+
28+
###############################################################################
29+
# Python package
30+
###############################################################################
31+
resource "null_resource" "chat_api" {
32+
triggers = {
33+
always_redeploy = timestamp()
34+
}
35+
36+
provisioner "local-exec" {
37+
interpreter = ["/bin/bash"]
38+
command = local.ecr_build_script
39+
40+
environment = {
41+
BUILD_PATH = local.ecr_build_path
42+
CONTAINER_NAME = local.ecr_repo
43+
AWS_REGION = var.aws_region
44+
AWS_ACCOUNT_ID = var.aws_account_id
45+
}
46+
}
47+
48+
depends_on = [aws_ecr_repository.chat_api]
49+
}

api/kubernetes/ecr_build/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.zip
2+
venv
3+
archive

api/kubernetes/ecr_build/Dockerfile

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Use an AWS Lambda Python runtime as the base image
2+
# https://hub.docker.com/r/amazon/aws-lambda-python
3+
# ------------------------------------------------------
4+
FROM --platform=linux/amd64 python:3.11-buster
5+
6+
WORKDIR /app
7+
8+
COPY openai_api .
9+
COPY requirements.txt .
10+
11+
RUN apt-get update && apt-get install -y zip
12+
RUN pip install -r requirements.txt
13+
14+
CMD ["python", "service_controller.py"]
15+
16+
EXPOSE 8000
17+
18+
ENV DEBUG_MODE=False
19+
ARG OPENAI_API_KEY
20+
ARG PINECONE_API_KEY
21+
ARG PINECONE_ENVIRONMENT
22+
ARG GOOGLE_MAPS_API_KEY

api/kubernetes/ecr_build/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# AWS Lambda Layer for OpenAI/Langchain Lambdas
2+
3+
This layer contains the combined pip requirements for both lambda_langchain and lambda_openai_v2.

api/kubernetes/ecr_build/build.sh

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
#------------------------------------------------------------------------------
3+
# written by: Lawrence McDaniel
4+
# https://lawrencemcdaniel.com/
5+
#
6+
# date: nov-2023
7+
#
8+
# usage: Lambda Python packaging tool.
9+
# Called by Terraform "null_resource". Copies python
10+
# module(s) plus any requirements to a dedicated folder so that
11+
# it can be archived to a zip file for upload to
12+
# AWS Lambda by Terraform.
13+
#------------------------------------------------------------------------------
14+
cd $BUILD_PATH
15+
16+
pwd
17+
cp -R ../../python/openai_api/ ./openai_api
18+
19+
# Step 1: Build your Docker image
20+
docker build -t $CONTAINER_NAME .
21+
22+
# Step 2: Authenticate Docker to your Amazon ECR registry
23+
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
24+
25+
# Step 3: Tag your Docker image
26+
docker tag chat_api:latest $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/chat_api:latest
27+
28+
# Step 4: Push your Docker image
29+
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/chat_api:latest
30+
31+
rm -r ./openai_api
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -----------------------------------------------------------------------------
2+
# written by: Lawrence McDaniel
3+
# https://lawrencemcdaniel.com
4+
#
5+
# usage: Python requirements for AWS Lambda functions. Create a virtual
6+
# environment in the root of this repository named `venv`. Terraform
7+
# modules will look for and include these requirements in the zip
8+
# packages for each Python-based Lambda function.
9+
# -----------------------------------------------------------------------------
10+
11+
# misc
12+
# ------------
13+
boto3==1.34.25
14+
botocore==1.34.29
15+
requests==2.31.0
16+
17+
# Lambda layer: openai
18+
# ------------
19+
openai==1.10.0
20+
pyyaml==6.0.1
21+
22+
# Lambda layer: common
23+
# ------------
24+
python-dotenv==1.0.1
25+
pydantic==2.5.3
26+
pydantic-settings==2.1.0
27+
python-hcl2==4.3.2
28+
29+
# Lambda layer: langchain
30+
# ------------
31+
langchain==0.1.1
32+
langchain-openai==0.0.5
33+
34+
# Lambda layer: nlp
35+
# ------------
36+
python-Levenshtein==0.23.0
37+
nltk==3.8.1
38+
textblob==0.17.1
39+
40+
# weather function
41+
googlemaps==4.10.0
42+
openmeteo-requests==1.1.0
43+
requests-cache==1.1.1
44+
retry-requests==2.0.0

api/kubernetes/k8s.tf

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#--------------------------------------------------------------
2+
# Deploy containerized application to an existing Kubernetes cluster
3+
#--------------------------------------------------------------
4+
5+
provider "kubernetes" {
6+
config_path = "~/.kube/config"
7+
}
8+
9+
# resource "kubernetes_manifest" "deployment" {
10+
# manifest = yamldecode(data.template_file.deployment.rendered)
11+
# }
12+
13+
# 1. namespace
14+
# 2. service
15+
# 3. horizontal scaling policy
16+
# 4. vertical scaling policy
17+
# 5. certificate
18+
# 6. ingress
19+
# 7. route53 dns record

api/kubernetes/terraform.tf

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#------------------------------------------------------------------------------
2+
# written by: Lawrence McDaniel
3+
# https://lawrencemcdaniel.com/
4+
#
5+
# date: July-2023
6+
#
7+
# usage: Terraform configuration
8+
#------------------------------------------------------------------------------
9+
10+
terraform {
11+
required_version = "~> 1.5"
12+
backend "s3" {
13+
bucket = "090511222473-tfstate-openai"
14+
key = "chat_api/terraform.tfstate"
15+
region = "us-east-1"
16+
dynamodb_table = "090511222473-tfstate-lock-openai"
17+
profile = "lawrence"
18+
encrypt = false
19+
}
20+
21+
required_providers {
22+
aws = {
23+
source = "hashicorp/aws"
24+
version = "~> 5.35"
25+
}
26+
null = {
27+
source = "hashicorp/null"
28+
version = "~> 3.2"
29+
}
30+
kubernetes = {
31+
source = "hashicorp/kubernetes"
32+
version = "~> 2.25"
33+
}
34+
}
35+
}

api/kubernetes/terraform.tfvars

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#------------------------------------------------------------------------------
2+
# written by: Lawrence McDaniel
3+
# https://lawrencemcdaniel.com/
4+
#
5+
# date: sep-2023
6+
#
7+
# usage: override default variable values
8+
#------------------------------------------------------------------------------
9+
10+
###############################################################################
11+
# AWS CLI parameters
12+
###############################################################################
13+
aws_account_id = "090511222473"
14+
tags = {
15+
"terraform" = "true",
16+
"project" = "chatGPT microservice"
17+
"contact" = "Lawrence McDaniel - https://lawrencemcdaniel.com/"
18+
}
19+
aws_region = "us-east-2"
20+
aws_profile = "lawrence"
21+
22+
###############################################################################
23+
# OpenAI API parameters
24+
###############################################################################
25+
openai_endpoint_image_n = 4
26+
openai_endpoint_image_size = "1024x768"
27+
28+
29+
###############################################################################
30+
# CloudWatch logging parameters
31+
###############################################################################
32+
logging_level = "INFO"
33+
34+
35+
###############################################################################
36+
# APIGateway parameters
37+
###############################################################################
38+
root_domain = "lawrencemcdaniel.com"
39+
shared_resource_identifier = "openai"
40+
stage = "v1"

api/kubernetes/variables.tf

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#------------------------------------------------------------------------------
2+
# written by: Lawrence McDaniel
3+
# https://lawrencemcdaniel.com/
4+
#
5+
# date: sep-2023
6+
#
7+
# usage: all Terraform variable declarations
8+
#------------------------------------------------------------------------------
9+
variable "shared_resource_identifier" {
10+
description = "A common identifier/prefix for resources created for this demo"
11+
type = string
12+
default = "openai"
13+
}
14+
15+
variable "aws_account_id" {
16+
description = "12-digit AWS account number"
17+
type = string
18+
}
19+
variable "aws_region" {
20+
description = "A valid AWS data center region code"
21+
type = string
22+
default = "us-east-1"
23+
}
24+
variable "aws_profile" {
25+
description = "a valid AWS CLI profile located in $HOME/.aws/credentials"
26+
type = string
27+
default = "default"
28+
}
29+
30+
variable "debug_mode" {
31+
type = bool
32+
default = false
33+
}
34+
variable "tags" {
35+
description = "A map of tags to add to all resources. Tags added to launch configuration or templates override these values."
36+
type = map(string)
37+
default = {}
38+
}
39+
40+
###############################################################################
41+
# OpenAI API parameters
42+
###############################################################################
43+
variable "openai_endpoint_image_n" {
44+
description = "FIX NOTE: what is this?"
45+
type = number
46+
default = 4
47+
}
48+
variable "openai_endpoint_image_size" {
49+
description = "Image output dimensions in pixels"
50+
type = string
51+
default = "1024x768"
52+
}
53+
54+
55+
variable "root_domain" {
56+
description = "a valid Internet domain name which you directly control using AWS Route53 in this account"
57+
type = string
58+
default = ""
59+
}
60+
61+
variable "stage" {
62+
description = "Examples: dev, staging, prod, v0, v1, etc."
63+
type = string
64+
default = "v1"
65+
}
66+
67+
variable "logging_level" {
68+
type = string
69+
default = "INFO"
70+
}

api/kubernetes/yaml/certificate-manager.yaml

Whitespace-only changes.

api/kubernetes/yaml/deployment.yaml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: openai-api
5+
labels:
6+
app: openai-api
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: openai-api
12+
template:
13+
metadata:
14+
labels:
15+
app: openai-api
16+
spec:
17+
containers:
18+
- name: openai-api
19+
image: "${var.aws_account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/${var.ecr_repo_name}:${var.ecr_repo_tag}"
20+
env:
21+
- name: ENV_VAR1
22+
value: "value1"
23+
- name: ENV_VAR2
24+
value: "value2"
25+
ports:
26+
- containerPort: 8080

api/kubernetes/yaml/horizontal-pod-autoscaling-policy.yaml

Whitespace-only changes.

api/kubernetes/yaml/ingress.yaml

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
annotations:
5+
cert-manager.io/cluster-issuer: api.lawrencemcdaniel.com
6+
kubectl.kubernetes.io/last-applied-configuration: |
7+
{"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"annotations":{"cert-manager.io/cluster-issuer":"api.lawrencemcdaniel.com","kubernetes.io/ingress.class":"nginx","nginx.ingress.kubernetes.io/affinity":"cookie","nginx.ingress.kubernetes.io/backend-protocol":"HTTP","nginx.ingress.kubernetes.io/force-ssl-redirect":"true","nginx.ingress.kubernetes.io/proxy-body-size":"0","nginx.ingress.kubernetes.io/proxy-buffer-size":"256k","nginx.ingress.kubernetes.io/proxy-buffers":"4 512k","nginx.ingress.kubernetes.io/proxy-busy-buffers-size":"512k","nginx.ingress.kubernetes.io/session-cookie-expires":"172800","nginx.ingress.kubernetes.io/session-cookie-max-age":"172800","nginx.ingress.kubernetes.io/session-cookie-name":"wordpress_sticky_session"},"name":"api.lawrencemcdaniel.com","namespace":"lawrencemcdaniel-api"},"spec":{"rules":[{"host":"api.lawrencemcdaniel.com","http":{"paths":[{"backend":{"service":{"name":"wordpress","port":{"number":80}}},"path":"/","pathType":"Prefix"}]}}],"tls":[{"hosts":["api.lawrencemcdaniel.com"],"secretName":"api.lawrencemcdaniel.com-tls"}]}}
8+
kubernetes.io/ingress.class: nginx
9+
nginx.ingress.kubernetes.io/affinity: cookie
10+
nginx.ingress.kubernetes.io/backend-protocol: HTTP
11+
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
12+
nginx.ingress.kubernetes.io/proxy-body-size: "0"
13+
nginx.ingress.kubernetes.io/proxy-buffer-size: 256k
14+
nginx.ingress.kubernetes.io/proxy-buffers: 4 512k
15+
nginx.ingress.kubernetes.io/proxy-busy-buffers-size: 512k
16+
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
17+
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
18+
nginx.ingress.kubernetes.io/session-cookie-name: wordpress_sticky_session
19+
creationTimestamp: "2023-08-22T03:08:08Z"
20+
generation: 1
21+
name: api.lawrencemcdaniel.com
22+
namespace: lawrencemcdaniel-api
23+
resourceVersion: "79637258"
24+
uid: 0170d971-3b48-46d5-9308-ba4b4a678634
25+
spec:
26+
rules:
27+
- host: api.lawrencemcdaniel.com
28+
http:
29+
paths:
30+
- backend:
31+
service:
32+
name: wordpress
33+
port:
34+
number: 80
35+
path: /
36+
pathType: Prefix
37+
tls:
38+
- hosts:
39+
- api.lawrencemcdaniel.com
40+
secretName: api.lawrencemcdaniel.com-tls
41+
status:
42+
loadBalancer:
43+
ingress:
44+
- hostname: a1db5dfcf202b4a63bdcd0f3c03e769f-769707598.us-east-2.elb.amazonaws.com

0 commit comments

Comments
 (0)