cup helps you deploy apps and services on remote servers with high-availability rolling and parallel deploys. It works alongside up to do so.
up
requires an Upfile, but maintaining a bug-free Upfile on a large project
is difficult. To solve this problem, cup automatically generates Upfiles for
you, removing the tedious and error-prone boilerplate. cup
generates scripts
suitable for fast deploys in production environments. Even large projects
deploy in barely any more time than it takes to rsync the directories.
Nothing needs to be installed on the remote host except ssh and rsync (but you can customize those if you really want).
Every project using cup
must define a manifest file, which is a json file
with the name of your service, such as my_web_app.json
. Although this one is
called my_web_app.json
, yours can be named anything. Here's an example:
$ cat my_web_app.json
{
"files": {
"deploy/": {},
"pf.conf": {
"path": "/etc/pf.conf",
"mod": "600",
"own": "root:root"
},
},
"stop": [
"sudo systemctl stop \"my_app 2\""
],
"start": [
"echo $MY_SECRET_ENV > env.ini",
"sudo systemctl start \"my_app 2\"",
"sleep 5 && $check_health"
],
"default": {
"ssh": "ssh -J $jump",
"rsync": "rsync -chazP -e 'ssh -J $jump'",
"remote": "/home/$user/my_app",
"user": "_daemon"
},
"vars": {
"jump": "jump@10.0.0.4",
"check_health": "curl -s --max-time 1 http://$server:80/health"
}
}
There's several sections in the example above:
app
then the
deploy folder will be synced recursively to /home/app/my_web_app/deploy
.
You can override this default by specifying path
, mod
, and/or own
.up
will complete after syncing
all files. up
will always run stop, then start. You can omit stop if
unneeded. Notice that $check_health
is defined in the vars
section.cup
will pick smart defaults.
Consider this an escape hatch if you use a jumpbox or have some other
non-standard setup.Note that you can also pass in environment variables such as $ENV
above,
which is great for secrets. Vars not declared in the "vars" section will be
left as-is, meaning that up
will do the substitution for you.
To use cup with our above arrangement, you'd usually call:
$ cup -f my_web_app.json | up -f -
cup
will read my_web_app.json
to generate an Upfile, which is piped to
up
and executed. In the above case, my_web_app
would be deployed to
10.0.0.1
, and if all steps succeeded, it would move on to 10.0.0.2
.
The generated Upfile rsyncs all files in a single command, changes file
ownership and permissions as needed, and then runs the deploy steps as a single
script, minimizing ssh
and rsync
connections to deploy as quickly as
possible.
You can configure up
per usual. In the following example we do a dry run
before before deploying 2 servers per batch in parallel:
$ cup -f my_web_app.json | up -d -f -
$ cup -f my_web_app.json | up -n 2 -f -
Note the trailing -
is required after -f
, as it instructs up
to read from
stdin.
Typically cup
and up
will be called from a deploy script, which ensures
processes such as compiling and cleanup happen only once per deploy, even if
deploying to many servers. An example best-practice deploy script is below.
#!/usr/bin/env sh
set -efu
name=my-service
tmpdir=/tmp/$name
# compile our binary
make
# unlock secrets which we can include in our build
shh login
mkdir -p $tmpdir
shh get -n my_web_app/production/env > $tmpdir/$name.env
shh get -n my_web_app/production/sql_client_key > $tmpdir/sql_key.pem
shh get -n my_web_app/production/sql_password > $tmpdir/sql_pass
# do a rolling, zero-downtime deploy on the appropriate servers
cup -f $name.json > $tmpdir/Upfile
up -c $name -f $tmpdir/Upfile
# clean up build artifacts
rm -r $tmpdir
make clean
$UP_USER
$UP_USER
/home/$user/$manifest/
.
This will be used when a file's remote is either not specified or isn't an
absolute path. Note that $manifest
will be the name of the manifest.json
file, without the extension. For instance if we're using my_app.json
as our
manifest file, the default folder for files will /home/$user/my_app
.ssh $user@$server $command
. $command
will be replaced automatically.rsync -chazP --del --chmod=700 $files $user@$server:$manifest/
.
$files
will be replaced automatically.sudo cp -R
sudo chown -R
sudo chmod -R
sudo mkdir -p
Since cup
minimizes round-trips by combining all commands on the server into
a single ssh call, you'll get an error by default that's difficult to debug,
since it highlights a single line doing many different things, and any of those
generated cp
or chmod
or chown
s could have failed.
To address this, cup
includes a debug flag -v
which will create a slower
but debuggable Upfile. Each ssh
statement will be executed on its own line
and ssh connection. When anything goes wrong, you'll see the exact command that
caused it.
Your workflow will usually involve run/modify/run the manifest file with cup -v
, and once everything works, remove the debug flag and enjoy deploys that
are as fast as possible.