From 99aa09a620979f1db3fc9a0120aee2d21ea318c6 Mon Sep 17 00:00:00 2001 From: carmen-chau <80921817+carmen-chau@users.noreply.github.com> Date: Sun, 10 Nov 2024 10:38:13 -0500 Subject: [PATCH] added docker files --- deployment/Dockerfile | 4 + deployment/docker-compose.ci.yml | 8 ++ deployment/docker-compose.prod.yml | 37 ++++++++ deployment/docker-stack-wait.sh | 148 +++++++++++++++++++++++++++++ deployment/entrypoint.sh | 18 +++- 5 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 deployment/docker-compose.ci.yml create mode 100644 deployment/docker-compose.prod.yml create mode 100644 deployment/docker-stack-wait.sh diff --git a/deployment/Dockerfile b/deployment/Dockerfile index ce5eedc..def1217 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -7,6 +7,10 @@ WORKDIR /usr/src/hackathon_site ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 +# Set time zone +RUN cp /usr/share/zoneinfo/Canada/Eastern /etc/localtime \ + && echo "America/Toronto" > /etc/timezone + # Install dependencies RUN apt-get update && apt-get install -y postgresql-client diff --git a/deployment/docker-compose.ci.yml b/deployment/docker-compose.ci.yml new file mode 100644 index 0000000..ff0a07a --- /dev/null +++ b/deployment/docker-compose.ci.yml @@ -0,0 +1,8 @@ +version: "3.8" + +services: + django: + build: + context: .. + dockerfile: ./deployment/Dockerfile + image: ${REGISTRY}/${IMAGE_NAME}/django:${GITHUB_SHA_SHORT} diff --git a/deployment/docker-compose.prod.yml b/deployment/docker-compose.prod.yml new file mode 100644 index 0000000..ff0e280 --- /dev/null +++ b/deployment/docker-compose.prod.yml @@ -0,0 +1,37 @@ +version: "3.8" + +services: + django: + image: ${REGISTRY}/${IMAGE_NAME}/django:${GITHUB_SHA_SHORT} + command: gunicorn hackathon_site.wsgi:application --bind 0.0.0.0:8000 --workers 5 --capture-output --access-logfile - --error-logfile - + ports: + - "8000:8000" + env_file: .env + volumes: + - /var/www/${IMAGE_NAME}/media/:/var/www/media/ + deploy: + replicas: 1 + update_config: + failure_action: rollback + order: start-first + restart_policy: + condition: on-failure + networks: + - aws-hacks-2024 + redis: + image: redis:6-alpine + ports: + - "6379:6379" + deploy: + replicas: 1 + update_config: + failure_action: rollback + order: start-first + restart_policy: + condition: on-failure + networks: + - aws-hacks-2024 + +networks: + aws-hacks-2024: + driver: overlay diff --git a/deployment/docker-stack-wait.sh b/deployment/docker-stack-wait.sh new file mode 100644 index 0000000..c7f00a3 --- /dev/null +++ b/deployment/docker-stack-wait.sh @@ -0,0 +1,148 @@ +#!/bin/sh + +# By: Brandon Mitchell +# License: MIT +# Source repo: https://github.com/sudo-bmitch/docker-stack-wait + +set -e +trap "{ exit 1; }" TERM INT +opt_h=0 +opt_r=0 +opt_s=5 +opt_t=3600 +start_epoc=$(date +%s) + +usage() { + echo "$(basename $0) [opts] stack_name" + echo " -f filter: only wait for services matching filter, may be passed multiple" + echo " times, see docker stack services for the filter syntax" + echo " -h: this help message" + echo " -n name: only wait for specific service names, overrides any filters," + echo " may be passed multiple times, do not include the stack name prefix" + echo " -r: treat a rollback as successful" + echo " -s sec: frequency to poll service state (default $opt_s sec)" + echo " -t sec: timeout to stop waiting" + [ "$opt_h" = "1" ] && exit 0 || exit 1 +} +check_timeout() { + # timeout when a timeout is defined and we will exceed the timeout after the + # next sleep completes + if [ "$opt_t" -gt 0 ]; then + cur_epoc=$(date +%s) + cutoff_epoc=$(expr ${start_epoc} + $opt_t - $opt_s) + if [ "$cur_epoc" -gt "$cutoff_epoc" ]; then + echo "Error: Timeout exceeded" + exit 1 + fi + fi +} +get_service_ids() { + if [ -n "$opt_n" ]; then + service_list="" + for name in $opt_n; do + service_list="${service_list:+${service_list} }${stack_name}_${name}" + done + docker service inspect --format '{{.ID}}' ${service_list} + else + docker stack services ${opt_f} -q "${stack_name}" + fi +} +service_state() { + # output the state when it changes from the last state for the service + service=$1 + # strip any invalid chars from service name for caching state + service_safe=$(echo "$service" | sed 's/[^A-Za-z0-9_]/_/g') + state=$2 + if eval [ \"\$cache_${service_safe}\" != \"\$state\" ]; then + echo "Service $service state: $state" + eval cache_${service_safe}=\"\$state\" + fi +} + +while getopts 'f:hn:rs:t:' opt; do + case $opt in + f) opt_f="${opt_f:+${opt_f} }-f $OPTARG";; + h) opt_h=1;; + n) opt_n="${opt_n:+${opt_n} } $OPTARG";; + r) opt_r=1;; + s) opt_s="$OPTARG";; + t) opt_t="$OPTARG";; + esac +done +shift $(expr $OPTIND - 1) + +if [ $# -ne 1 -o "$opt_h" = "1" -o "$opt_s" -le "0" ]; then + usage +fi + +stack_name=$1 + +# 0 = running, 1 = success, 2 = error +stack_done=0 +while [ "$stack_done" != "1" ]; do + stack_done=1 + # run get_service_ids outside of the for loop to catch errors + service_ids=$(get_service_ids) + for service_id in ${service_ids}; do + service_done=1 + service=$(docker service inspect --format '{{.Spec.Name}}' "$service_id") + + # hardcode a "new" state when UpdateStatus is not defined + state=$(docker service inspect -f '{{if .UpdateStatus}}{{.UpdateStatus.State}}{{else}}new{{end}}' "$service_id") + + # check for failed update states + case "$state" in + paused|rollback_paused) + service_done=2 + ;; + rollback_*) + if [ "$opt_r" = "0" ]; then + service_done=2 + fi + ;; + esac + + # identify/report current state + if [ "$service_done" != "2" ]; then + replicas=$(docker service ls --format '{{.Replicas}}' --filter "id=$service_id" | cut -d' ' -f1) + current=$(echo "$replicas" | cut -d/ -f1) + target=$(echo "$replicas" | cut -d/ -f2) + if [ "$current" != "$target" ]; then + # actively replicating service + service_done=0 + state="replicating $replicas" + fi + fi + service_state "$service" "$state" + + # check for states that indicate an update is done + if [ "$service_done" = "1" ]; then + case "$state" in + new|completed|rollback_completed) + service_done=1 + ;; + *) + # any other state is unknown, not necessarily finished + service_done=0 + ;; + esac + fi + + # update stack done state + if [ "$service_done" = "2" ]; then + # error condition + stack_done=2 + elif [ "$service_done" = "0" -a "$stack_done" = "1" ]; then + # only go to an updating state if not in an error state + stack_done=0 + fi + done + if [ "$stack_done" = "2" ]; then + echo "Error: This deployment will not complete" + exit 1 + fi + if [ "$stack_done" != "1" ]; then + check_timeout + sleep "${opt_s}" + fi +done diff --git a/deployment/entrypoint.sh b/deployment/entrypoint.sh index 581d8d5..704de48 100644 --- a/deployment/entrypoint.sh +++ b/deployment/entrypoint.sh @@ -2,10 +2,20 @@ echo "Waiting for the database..." -while ! pg_isready -d $DB_NAME -h $DB_HOST -p $DB_PORT -U $DB_USER; do +TIMEOUT=120 +ATTEMPT=0 +command="pg_isready -d $DB_NAME -h $DB_HOST -p $DB_PORT -U $DB_USER" + +until [ "$ATTEMPT" -eq "$TIMEOUT" ] || eval $command; do + (( ATTEMPT++ )) sleep 1 done -echo "Database connection made" - -exec "$@" +if [ "$ATTEMPT" -lt "$TIMEOUT" ] +then + echo "Database connection made" + exec "$@" +else + echo "Failed to connect to database" + exit 1 +fi