Publish image for 05-assistive-chatbot #73
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: "Build and push Docker image" | |
run-name: "Publish image for ${{inputs.dockerfile_folder}}" | |
on: | |
workflow_dispatch: | |
inputs: | |
dockerfile_folder: | |
description: 'Folder containing Dockerfile to build' | |
required: true | |
type: choice | |
options: | |
- '05-assistive-chatbot' | |
- '02-household-queries' | |
subdomain: | |
description: 'Subdomain of navalabs.co' | |
type: choice | |
required: true | |
default: 'chat' | |
options: | |
- 'chat' | |
- 'chatbot' | |
- 'chatbdt' | |
- 'chat-bdt' | |
- 'bdtbot' | |
- 'bdt-bot' | |
- 'bdt-chat' | |
- 'bdt-chatbot' | |
- 'chatbot-prototype' | |
- 'chat.zone' | |
service_name: | |
description: 'Name of target service. Leave blank if unsure' | |
type: choice | |
default: '' | |
options: | |
- '' | |
- 'container-service-3' | |
- 'container-service-2' | |
- 'chatbot-chainlit-svc' | |
- 'secure-chatbot-svc' | |
build_image: | |
description: "Build and push image" | |
required: true | |
type: boolean | |
default: 'true' | |
deploy_image: | |
description: "Deploy image" | |
required: true | |
type: boolean | |
default: 'false' | |
# image_tag: | |
# description: 'Tag/Version of the image to push' | |
# required: true | |
# type: string | |
# default: '0.06' | |
create_new_svc: | |
description: "Create new Lightsail service" | |
required: true | |
type: boolean | |
default: 'false' | |
delete_images: | |
description: 'Delete previous images associated with service' | |
required: true | |
type: boolean | |
default: 'false' | |
env: | |
AWS_REGION: us-east-1 | |
IMAGE_NAME: localimage | |
jobs: | |
publish-image: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Check inputs | |
id: check_inputs | |
run: | | |
service_name="${{ inputs.service_name }}-svc" | |
# if [ "${service_name}" = "" ]; then | |
# case "${{ inputs.dockerfile_folder }}" in | |
# '02-household-queries') service_name='secure-chatbot-svc';; | |
# '05-assistive-chatbot') service_name='chatbot-chainlit-svc';; | |
# *) exit 1;; | |
# esac | |
# fi | |
echo "service_name=$service_name" >> $GITHUB_OUTPUT | |
if [ ${{ inputs.create_new_svc }} == 'false' ]; then | |
echo "Since not creating new service, check if service '$service_name' exists" | |
aws lightsail get-container-services --service-name "$service_name" | |
fi | |
image_tag="${{ inputs.image_tag }}" | |
if [ "${image_tag}" = "" ]; then | |
case "${service_name}" in | |
# The image_tag is specific to the `*-svc` service | |
'secure-chatbot-svc') image_tag='0.01';; | |
'chatbot-chainlit-svc') image_tag='chatbot-chainlit';; | |
container-service-*) image_tag='not-used';; | |
*) echo "Unknown service_name: '${service_name}'"; exit 3;; | |
esac | |
fi | |
echo "image_tag=$image_tag" >> $GITHUB_OUTPUT | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
aws-region: ${{ env.AWS_REGION }} | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
mask-aws-account-id: true | |
# TODO: secure credentials: https://github.com/aws-actions/amazon-ecr-login?tab=readme-ov-file#ecr-private | |
# https://github.com/docker/login-action?tab=readme-ov-file#aws-elastic-container-registry-ecr | |
# https://medium.com/@lukhee/automating-aws-lightsail-deployments-with-github-actions-53c73c9a1c1f | |
- name: "Upgrade AWS CLI version and setup lightsailctl" | |
run: | | |
# aws --version | |
# curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" | |
# unzip awscliv2.zip | |
# sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update | |
# which aws | |
aws --version | |
sudo curl "https://s3.us-west-2.amazonaws.com/lightsailctl/latest/linux-amd64/lightsailctl" -o "/usr/local/bin/lightsailctl" | |
sudo chmod +x /usr/local/bin/lightsailctl | |
aws lightsail push-container-image help | |
- name: "Create new Lightsail container service" | |
if: inputs.create_new_svc | |
env: | |
FULL_DOMAIN: ${{ inputs.subdomain }}.navalabs.co | |
SERVICE_NAME: ${{ steps.check_inputs.outputs.service_name }} | |
run: | | |
# check if service already exists | |
if aws lightsail get-container-services --service-name "$SERVICE_NAME"; then | |
echo "Already exists!" | |
else | |
# `micro` power is needed for it's memory capacity; 60%+ memory is needed for the vector DB | |
aws lightsail create-container-service --service-name $SERVICE_NAME --power micro --scale 1 --public-domain-names navalabs-cert=$FULL_DOMAIN | |
# TODO: wait for service to be "READY" before updating container service | |
while true; do | |
sleep 15 | |
SVC_STATE=$(aws lightsail get-container-services --service-name "$SERVICE_NAME" | jq -r '.containerServices[0].state') | |
echo "service state: $SVC_STATE" | |
if [ "$SVC_STATE" == "READY" ]; then | |
break | |
fi | |
done | |
SVC_URL=$(aws lightsail get-container-services --service-name "$SERVICE_NAME" | jq -r '.containerServices[0].url') | |
# Remove 'https://' prefix | |
TARGET_DOMAIN=${SVC_URL#https://} | |
# Remove '/' suffix | |
TARGET_DOMAIN=${TARGET_DOMAIN%/} | |
# If domain entry exists, delete it | |
OLD_TARGET=$(aws lightsail get-domain --domain-name navalabs.co | jq -r ".domain.domainEntries[] | select( .name == \"$FULL_DOMAIN\" ) | .target") | |
if [ "$OLD_TARGET" ] ; then | |
echo "Deleting existing '$FULL_DOMAIN' entry with target '$OLD_TARGET'" | |
aws lightsail delete-domain-entry --domain-name navalabs.co --domain-entry "type=A,isAlias=true,name=$FULL_DOMAIN,target=$OLD_TARGET" | |
fi | |
# Create DNS assignment by adding a domain entry | |
aws lightsail create-domain-entry --domain-name navalabs.co --domain-entry "type=A,isAlias=true,name=$FULL_DOMAIN,target=$TARGET_DOMAIN" | |
fi | |
- name: "Delete previous container images" | |
if: inputs.delete_images | |
env: | |
SERVICE_NAME: ${{ steps.check_inputs.outputs.service_name }} | |
run: | | |
AWS_IMAGES=$(aws lightsail get-container-images --region "$AWS_REGION" --service-name "$SERVICE_NAME" --output text) | |
IMAGE_NAMES=$(echo $AWS_IMAGES | grep -Eo ':"$SERVICE_NAME"\.${{ inputs.image-name }}\.[0-9]+') | |
echo $IMAGE_NAMES | |
FIRST=0 | |
while read LINE; do | |
if [ "$FIRST" -ne 0 ]; then | |
aws lightsail delete-container-image --region "$AWS_REGION" --service-name "$SERVICE_NAME" --image $LINE; | |
fi | |
FIRST=1; | |
done <<< $IMAGE_NAMES | |
- name: "Login to Amazon ECR" | |
id: login-ecr | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
mask-password: true | |
- name: "Checkout source code" | |
if: inputs.build_image | |
uses: actions/checkout@v4 | |
- name: "Build image" | |
if: inputs.build_image | |
run: | | |
cd ${{ inputs.dockerfile_folder }} | |
# TODO: make this more easily editable and secure | |
# The DOT_ENV_FILE_CONTENTS contains LITERAL_API_KEY, OPENAI_API_KEY, RETRIEVE_K, LLM_MODEL_NAME, SUMMARIZER_LLM_MODEL_NAME | |
echo "${{secrets.DOT_ENV_FILE_CONTENTS}}" > .env | |
echo "BUILD_DATE=$(date +%Y-%m-%d-%T)" >> .env | |
echo "GIT_SHA=${{ github.sha }}" >> .env | |
docker build -t "$IMAGE_NAME" --build-arg GURU_CARDS_URL="https://docs.google.com/uc?export=download&id=${{ secrets.GURU_CARDS_URL_ID }}" . | |
- name: "Publish image to Lightsail" | |
if: inputs.build_image | |
id: pub_image_to_ls | |
env: | |
ECR_PATH: ${{ steps.login-ecr.outputs.registry }}/${{ secrets.ECR_REPO }} | |
SERVICE_NAME: ${{ steps.check_inputs.outputs.service_name }} | |
IMAGE_TAG: ${{ steps.check_inputs.outputs.image_tag }} | |
# LABEL must match regex ^(?:[a-z0-9]{1,2}|[a-z0-9][a-z0-9-]+[a-z0-9])$ | |
LABEL: git-push | |
IMAGE_SHA_TAG: ${{ github.sha }} | |
run: | | |
echo "# Publishing image for $SERVICE_NAME" | |
aws lightsail push-container-image --region $AWS_REGION --service-name "$SERVICE_NAME" --label "$LABEL" --image "$IMAGE_NAME" | |
LS_DOCKER_IMAGE=$(aws lightsail get-container-images --service-name "$SERVICE_NAME" | jq -r .containerImages[0].image) | |
echo "Image name: '$LS_DOCKER_IMAGE'" | |
echo "LS_DOCKER_IMAGE=$LS_DOCKER_IMAGE" >> $GITHUB_ENV | |
- name: Deploy container on AWS Lightsail | |
if: inputs.deploy_image | |
env: | |
SERVICE_NAME: ${{ steps.check_inputs.outputs.service_name }} | |
run: | | |
TEMPLATE='{ | |
"serviceName": "$SERVICE_NAME", | |
"containers": { | |
"chatbot": { | |
"image": "$LS_DOCKER_IMAGE", | |
"command": [], | |
"environment": { | |
"ENV": "PROD", | |
"BUILD_DATE": "$BUILD_DATE" | |
}, | |
"ports": { | |
"8000": "HTTP" | |
} | |
} | |
}, | |
"publicEndpoint": { | |
"containerName": "chatbot", | |
"containerPort": 8000, | |
"healthCheck": { | |
"healthyThreshold": 2, | |
"unhealthyThreshold": 4, | |
"timeoutSeconds": 20, | |
"intervalSeconds": 60, | |
"path": "/healthcheck", | |
"successCodes": "200-499" | |
} | |
} | |
}' | |
echo "$TEMPLATE" | BUILD_DATE=$(date +%Y-%m-%d-%T%z) envsubst > config.json | |
cat config.json | |
aws lightsail create-container-service-deployment --cli-input-json file://config.json | |
# aws lightsail create-container-service-deployment --region ${{ inputs.aws-region }} --cli-input-json '${{ inputs.aws-lightsail-service-config }}' > /dev/null | |
# aws lightsail update-container-service --service-name "$SERVICE_NAME" --no-is-disabled | |
# TODO: Wait for deployment to complete | |
# TODO: warm up vector DB on startup | |
# - name: "Update AWS Service" | |
# if: inputs.deploy_image | |
# env: | |
# CLUSTER_NAME: genai-experiments | |
# run: | | |
# aws ecs update-service --force-new-deployment --cluster "$CLUSTER_NAME" --service "${{ steps.check_inputs.outputs.service_name }}" |