Skip to content

Commit

Permalink
Merge pull request #1170 from briankariuki/deploy/kamal
Browse files Browse the repository at this point in the history
add kamal config, closes #1169, closes #1173
  • Loading branch information
wintermeyer authored Dec 3, 2024
2 parents d951c7c + 8f574e3 commit bbc1bf1
Show file tree
Hide file tree
Showing 30 changed files with 631 additions and 7 deletions.
49 changes: 49 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
############
# Database
############

POSTGRES_HOST=localhost
POSTGRES_USER=postgres
POSTGRES_PASSWORD=""
POSTGRES_PORT=5432

############
# Dev Container Environement Variables
############
DEV_SERVER_IP=127.0.0.1
DEV_SECRET_KEY_BASE=k/B2/h3nrn+XIwYaLZcc7cIAKfUihj9gLwm/IrtzNhx6daOLRax2xAzDCRoiHCNuH1iPOlMhp9aD91HJ
DEV_DATABASE_URL=ecto://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/animina_dev
DEV_PHX_HOST=dev.animina.de
DEV_PORT=4300
DEV_FILE_VOLUME_PATH=/etc/animina/dev/
DEV_CONTAINER_IMAGE="animina/animina-dev"

############
# Prod Container Environement Variables
############
PROD_SERVER_IP=127.0.0.1
PROD_SECRET_KEY_BASE=l8m2Yg9DmUzyC/onSZuzd2yR0UZbPTw3a8fRwMLDZqyhF1TuPdPys5pv/7rAo8xN12ZtuOW46kVEWBpy
PROD_DATABASE_URL=ecto://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/animina
PROD_PHX_HOST=animina.de
PROD_PORT=4310
PROD_FILE_VOLUME_PATH=/etc/animina/prod/
PROD_CONTAINER_IMAGE="animina/animina-prod"

############
# Demo Container Environement Variables
############
DEMO_SERVER_IP=127.0.0.1
DEMO_SECRET_KEY_BASE=l8m2Yg9DmUzyC/onSZuzd2yR0UZbPTw3a8fRwMLDZqyhF1TuPdPys5pv/7rAo8xN12ZtuOW46kVEWBpy
DEMO_DATABASE_URL=ecto://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/animina_demo
DEMO_PHX_HOST=demo.animina.de
DEMO_PORT=4330
DEMO_FILE_VOLUME_PATH=/etc/animina/demo/
DEMO_CONTAINER_IMAGE="animina/animina-demo"

############
# Shared Environement Variables
############
DISABLE_ML_FEATURES="true"
KAMAL_REGISTRY_USERNAME="animina"
KAMAL_REGISTRY_PASSWORD=""
REMOTE_BUILDER_SSH_URL="ssh://root@$DEV_SERVER_IP"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ npm-debug.log

.env.test
.env.dev
.env

3 changes: 3 additions & 0 deletions .kamal/hooks/docker-setup.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

echo "Docker set up on $KAMAL_HOSTS..."
14 changes: 14 additions & 0 deletions .kamal/hooks/post-deploy.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

# A sample post-deploy hook
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME

echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
3 changes: 3 additions & 0 deletions .kamal/hooks/post-proxy-reboot.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
51 changes: 51 additions & 0 deletions .kamal/hooks/pre-build.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

# A sample pre-build hook
#
# Checks:
# 1. We have a clean checkout
# 2. A remote is configured
# 3. The branch has been pushed to the remote
# 4. The version we are deploying matches the remote
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)

if [ -n "$(git status --porcelain)" ]; then
echo "Git checkout is not clean, aborting..." >&2
git status --porcelain >&2
exit 1
fi

first_remote=$(git remote)

if [ -z "$first_remote" ]; then
echo "No git remote set, aborting..." >&2
exit 1
fi

current_branch=$(git branch --show-current)

if [ -z "$current_branch" ]; then
echo "Not on a git branch, aborting..." >&2
exit 1
fi

remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)

if [ -z "$remote_head" ]; then
echo "Branch not pushed to remote, aborting..." >&2
exit 1
fi

if [ "$KAMAL_VERSION" != "$remote_head" ]; then
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
exit 1
fi

exit 0
47 changes: 47 additions & 0 deletions .kamal/hooks/pre-connect.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env ruby

# A sample pre-connect check
#
# Warms DNS before connecting to hosts in parallel
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME

hosts = ENV["KAMAL_HOSTS"].split(",")
results = nil
max = 3

elapsed = Benchmark.realtime do
results = hosts.map do |host|
Thread.new do
tries = 1

begin
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
rescue SocketError
if tries < max
puts "Retrying DNS warmup: #{host}"
tries += 1
sleep rand
retry
else
puts "DNS warmup failed: #{host}"
host
end
end

tries
end
end.map(&:value)
end

retries = results.sum - hosts.size
nopes = results.count { |r| r == max }

puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
109 changes: 109 additions & 0 deletions .kamal/hooks/pre-deploy.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env ruby

# A sample pre-deploy hook
#
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
#
# Fails unless the combined status is "success"
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_COMMAND
# KAMAL_SUBCOMMAND
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)

# Only check the build status for production deployments
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
exit 0
end

require "bundler/inline"

# true = install gems so this is fast on repeat invocations
gemfile(true, quiet: true) do
source "https://rubygems.org"

gem "octokit"
gem "faraday-retry"
end

MAX_ATTEMPTS = 72
ATTEMPTS_GAP = 10

def exit_with_error(message)
$stderr.puts message
exit 1
end

class GithubStatusChecks
attr_reader :remote_url, :git_sha, :github_client, :combined_status

def initialize
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
@git_sha = `git rev-parse HEAD`.strip
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
refresh!
end

def refresh!
@combined_status = github_client.combined_status(remote_url, git_sha)
end

def state
combined_status[:state]
end

def first_status_url
first_status = combined_status[:statuses].find { |status| status[:state] == state }
first_status && first_status[:target_url]
end

def complete_count
combined_status[:statuses].count { |status| status[:state] != "pending"}
end

def total_count
combined_status[:statuses].count
end

def current_status
if total_count > 0
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
else
"Build not started..."
end
end
end


$stdout.sync = true

puts "Checking build status..."
attempts = 0
checks = GithubStatusChecks.new

begin
loop do
case checks.state
when "success"
puts "Checks passed, see #{checks.first_status_url}"
exit 0
when "failure"
exit_with_error "Checks failed, see #{checks.first_status_url}"
when "pending"
attempts += 1
end

exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS

puts checks.current_status
sleep(ATTEMPTS_GAP)
checks.refresh!
end
rescue Octokit::NotFound
exit_with_error "Build status could not be found"
end
3 changes: 3 additions & 0 deletions .kamal/hooks/pre-proxy-reboot.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
2 changes: 2 additions & 0 deletions .kamal/secrets-common
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
KAMAL_REGISTRY_USERNAME=$KAMAL_REGISTRY_USERNAME
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
10 changes: 10 additions & 0 deletions .kamal/secrets.demo
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SERVER_IP=$DEMO_SERVER_IP
SECRET_KEY_BASE=$DEMO_SECRET_KEY_BASE
DATABASE_URL=$DEMO_DATABASE_URL
PHX_HOST=$DEMO_PHX_HOST
PORT=$DEMO_PORT
FILE_VOLUME_PATH=$DEMO_FILE_VOLUME_PATH
DISABLE_ML_FEATURES=$DISABLE_ML_FEATURES
KAMAL_REGISTRY_USERNAME=$KAMAL_REGISTRY_USERNAME
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
CONTAINER_IMAGE=$DEMO_CONTAINER_IMAGE
10 changes: 10 additions & 0 deletions .kamal/secrets.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SERVER_IP=$DEV_SERVER_IP
SECRET_KEY_BASE=$DEV_SECRET_KEY_BASE
DATABASE_URL=$DEV_DATABASE_URL
PHX_HOST=$DEV_PHX_HOST
PORT=$DEV_PORT
FILE_VOLUME_PATH=$DEV_FILE_VOLUME_PATH
DISABLE_ML_FEATURES=$DISABLE_ML_FEATURES
KAMAL_REGISTRY_USERNAME=$KAMAL_REGISTRY_USERNAME
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
CONTAINER_IMAGE=$DEV_CONTAINER_IMAGE
10 changes: 10 additions & 0 deletions .kamal/secrets.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SERVER_IP=$PROD_SERVER_IP
SECRET_KEY_BASE=$PROD_SECRET_KEY_BASE
DATABASE_URL=$PROD_DATABASE_URL
PHX_HOST=$PROD_PHX_HOST
PORT=$PROD_PORT
FILE_VOLUME_PATH=$PROD_FILE_VOLUME_PATH
DISABLE_ML_FEATURES=$DISABLE_ML_FEATURES
KAMAL_REGISTRY_USERNAME=$KAMAL_REGISTRY_USERNAME
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
CONTAINER_IMAGE=$PROD_CONTAINER_IMAGE
8 changes: 5 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
FROM ${BUILDER_IMAGE} as builder

# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git \
RUN apt-get update -y && apt-get install -y build-essential git curl \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*

#Install nodejs
Expand Down Expand Up @@ -55,7 +55,7 @@ COPY lib lib

COPY assets assets

RUN cd assets && yarn install
RUN cd assets && npm install

# compile assets
RUN mix assets.deploy
Expand All @@ -74,7 +74,7 @@ RUN mix release
FROM ${RUNNER_IMAGE}

RUN apt-get update -y && \
apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates \
apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates curl \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*

# Set the locale
Expand All @@ -100,4 +100,6 @@ USER nobody
# above and adding an entrypoint. See https://github.com/krallin/tini for details
# ENTRYPOINT ["/tini", "--"]

EXPOSE 4000

CMD ["sh", "-c", "/app/bin/migrate && exec /app/bin/server"]
Loading

0 comments on commit bbc1bf1

Please sign in to comment.