2019-07-01 21:48:12 +02:00
#!/usr/bin/env bash
set -e
2020-01-03 11:17:35 +01:00
2020-01-03 20:06:33 +01:00
dc = "docker-compose --no-ansi"
dcr = " $dc run --rm "
2020-01-03 11:17:35 +01:00
# Thanks to https://unix.stackexchange.com/a/145654/108960
log_file = "sentry_install_log-`date +'%Y-%m-%d_%H-%M-%S'`.txt"
exec & > >( tee -a " $log_file " )
2019-07-01 21:48:12 +02:00
2019-07-17 21:13:23 +02:00
MIN_DOCKER_VERSION = '17.05.0'
2020-01-21 20:56:04 +01:00
MIN_COMPOSE_VERSION = '1.23.0'
2019-11-12 00:18:59 +01:00
MIN_RAM = 2400 # MB
SENTRY_CONFIG_PY = 'sentry/sentry.conf.py'
SENTRY_CONFIG_YML = 'sentry/config.yml'
2020-04-24 14:31:59 +02:00
RELAY_CONFIG_YML = 'relay/config.yml'
RELAY_CREDENTIALS_JSON = 'relay/credentials.json'
2019-11-12 00:18:59 +01:00
SENTRY_EXTRA_REQUIREMENTS = 'sentry/requirements.txt'
2019-07-01 21:48:12 +02:00
DID_CLEAN_UP = 0
# the cleanup function will be the exit point
cleanup ( ) {
if [ " $DID_CLEAN_UP " -eq 1 ] ; then
return 0;
fi
echo "Cleaning up..."
2020-01-03 20:06:33 +01:00
$dc stop & > /dev/null
2019-07-01 21:48:12 +02:00
DID_CLEAN_UP = 1
}
trap cleanup ERR INT TERM
echo "Checking minimum requirements..."
DOCKER_VERSION = $( docker version --format '{{.Server.Version}}' )
2020-01-03 20:06:33 +01:00
COMPOSE_VERSION = $( $dc --version | sed 's/docker-compose version \(.\{1,\}\),.*/\1/' )
2019-07-01 21:48:12 +02:00
RAM_AVAILABLE_IN_DOCKER = $( docker run --rm busybox free -m 2>/dev/null | awk '/Mem/ {print $2}' ) ;
2019-10-24 00:14:53 +02:00
# Compare dot-separated strings - function below is inspired by https://stackoverflow.com/a/37939589/808368
function ver ( ) { echo " $@ " | awk -F. '{ printf("%d%03d%03d", $1,$2,$3); }' ; }
2019-07-01 21:48:12 +02:00
2019-11-12 00:18:59 +01:00
# Thanks to https://stackoverflow.com/a/25123013/90297 for the quick `sed` pattern
function ensure_file_from_example {
if [ -f " $1 " ] ; then
echo " $1 already exists, skipped creation. "
else
echo " Creating $1 ... "
cp -n $( echo " $1 " | sed 's/\.[^.]*$/.example&/' ) " $1 "
fi
}
2019-07-01 21:48:12 +02:00
if [ $( ver $DOCKER_VERSION ) -lt $( ver $MIN_DOCKER_VERSION ) ] ; then
echo " FAIL: Expected minimum Docker version to be $MIN_DOCKER_VERSION but found $DOCKER_VERSION "
2020-02-03 19:01:12 +01:00
exit 1
2019-07-01 21:48:12 +02:00
fi
if [ $( ver $COMPOSE_VERSION ) -lt $( ver $MIN_COMPOSE_VERSION ) ] ; then
echo " FAIL: Expected minimum docker-compose version to be $MIN_COMPOSE_VERSION but found $COMPOSE_VERSION "
2020-02-03 19:01:12 +01:00
exit 1
2019-07-01 21:48:12 +02:00
fi
if [ " $RAM_AVAILABLE_IN_DOCKER " -lt " $MIN_RAM " ] ; then
echo " FAIL: Expected minimum RAM available to Docker to be $MIN_RAM MB but found $RAM_AVAILABLE_IN_DOCKER MB "
2020-02-03 19:01:12 +01:00
exit 1
2019-07-01 21:48:12 +02:00
fi
2020-04-27 19:54:59 +02:00
#SSE4.2 required by Clickhouse (https://clickhouse.yandex/docs/en/operations/requirements/)
2020-05-09 22:10:08 +02:00
# On KVM, cpuinfo could falsely not report SSE 4.2 support, so skip the check. https://github.com/ClickHouse/ClickHouse/issues/20#issuecomment-226849297
IS_KVM = $( docker run --rm busybox grep -c 'Common KVM processor' /proc/cpuinfo || :)
if ( ( $IS_KVM = = 0) ) ; then
SUPPORTS_SSE42 = $( docker run --rm busybox grep -c sse4_2 /proc/cpuinfo || :)
if ( ( $SUPPORTS_SSE42 = = 0) ) ; then
2020-01-29 14:29:38 +01:00
echo "FAIL: The CPU your machine is running on does not support the SSE 4.2 instruction set, which is required for one of the services Sentry uses (Clickhouse). See https://git.io/JvLDt for more info."
2020-02-03 19:01:12 +01:00
exit 1
2020-05-09 22:10:08 +02:00
fi
2020-01-29 14:29:38 +01:00
fi
2019-12-30 21:07:42 +01:00
# Clean up old stuff and ensure nothing is working while we install/update
2020-01-03 20:06:33 +01:00
# This is for older versions of on-premise:
$dc -p onpremise down --rmi local --remove-orphans
# This is for newer versions
$dc down --rmi local --remove-orphans
2019-11-12 00:18:59 +01:00
2019-07-01 21:48:12 +02:00
echo ""
echo "Creating volumes for persistent storage..."
echo " Created $( docker volume create --name= sentry-data) . "
echo " Created $( docker volume create --name= sentry-postgres) . "
2019-11-12 00:18:59 +01:00
echo " Created $( docker volume create --name= sentry-redis) . "
echo " Created $( docker volume create --name= sentry-zookeeper) . "
echo " Created $( docker volume create --name= sentry-kafka) . "
echo " Created $( docker volume create --name= sentry-clickhouse) . "
echo " Created $( docker volume create --name= sentry-symbolicator) . "
2019-07-01 21:48:12 +02:00
2019-12-09 19:33:02 +01:00
echo ""
ensure_file_from_example $SENTRY_CONFIG_PY
ensure_file_from_example $SENTRY_CONFIG_YML
ensure_file_from_example $SENTRY_EXTRA_REQUIREMENTS
2020-02-25 14:08:13 +01:00
if grep -xq "system.secret-key: '!!changeme!!'" $SENTRY_CONFIG_YML ; then
echo ""
echo "Generating secret key..."
# This is to escape the secret key to be used in sed below
# Note the need to set LC_ALL=C due to BSD tr and sed always trying to decode
# whatever is passed to them. Kudos to https://stackoverflow.com/a/23584470/90297
SECRET_KEY = $( export LC_ALL = C; head /dev/urandom | tr -dc "a-z0-9@#%^&*(-_=+)" | head -c 50 | sed -e 's/[\/&]/\\&/g' )
sed -i -e 's/^system.secret-key:.*$/system.secret-key: ' " ' $SECRET_KEY ' " '/' $SENTRY_CONFIG_YML
echo " Secret key written to $SENTRY_CONFIG_YML "
fi
2019-07-01 21:48:12 +02:00
feat: Instruct users to migrate TSDB (#430)
Tested this in a Ubuntu VM. The output of `date` is not too pretty but at least localized (so D/M vs M/D is not confusing)
## What is the TSDB migration?
We're effectively deprecating all TSDB backends but `sentry.tsdb.redissnuba.RedisSnubaTSDB`. We cannot reasonably support any other backend due to the fact that we would have to reimplement each of the backends in Relay, which is written in a different language. Also, like with deprecating mysql support, we don't really have the capacity to support things we do not use ourselves.
## Migration
`install.sh` should rewrite your configuration automatically and define a cutover date such that no data is lost. Before the cutover date, data is written to two backends at once, Redis and Snuba, and read from one, Redis. After the cutover date, event-related metrics will be read from Snuba which matches what we have on sentry.io.
## Manual migration guide for TSDB
In case `install.sh` is unable to migrate your files you will be given basic instructions on the console that essentially tell you to completely delete all TSDB config and paste the new, standard one. If for some reason you cannot say goodbye to your existing TSDB config, please create a new issue in this repo and cc @untitaker on it.
2020-05-12 12:02:40 +02:00
replace_tsdb( ) {
if (
[ -f " $SENTRY_CONFIG_PY " ] &&
! grep -xq 'SENTRY_TSDB = "sentry.tsdb.redissnuba.RedisSnubaTSDB"' " $SENTRY_CONFIG_PY "
) ; then
tsdb_settings = " SENTRY_TSDB = \"sentry.tsdb.redissnuba.RedisSnubaTSDB\"
# Automatic switchover 90 days after $(date). Can be removed afterwards.
SENTRY_TSDB_OPTIONS = { \" switchover_timestamp\" : $( date +%s) + ( 90 * 24 * 3600) } "
if grep -q 'SENTRY_TSDB_OPTIONS = ' " $SENTRY_CONFIG_PY " ; then
echo "Not attempting automatic TSDB migration due to presence of SENTRY_TSDB_OPTIONS"
else
echo "Attempting to automatically migrate to new TSDB"
# Escape newlines for sed
tsdb_settings = " ${ tsdb_settings // $'\n' / \\ n } "
cp " $SENTRY_CONFIG_PY " " $SENTRY_CONFIG_PY .bak "
sed -i -e " s/^SENTRY_TSDB = .* $/ ${ tsdb_settings } /g " " $SENTRY_CONFIG_PY " || true
if grep -xq 'SENTRY_TSDB = "sentry.tsdb.redissnuba.RedisSnubaTSDB"' " $SENTRY_CONFIG_PY " ; then
echo " Migrated TSDB to Snuba. Old configuration file backed up to $SENTRY_CONFIG_PY .bak "
return
fi
echo "Failed to automatically migrate TSDB. Reverting..."
mv " $SENTRY_CONFIG_PY .bak " " $SENTRY_CONFIG_PY "
echo " $SENTRY_CONFIG_PY restored from backup. "
fi
echo " WARN: Your Sentry configuration uses a legacy data store for time-series data. Remove the options SENTRY_TSDB and SENTRY_TSDB_OPTIONS from $SENTRY_CONFIG_PY and add: "
echo ""
echo " $tsdb_settings "
echo ""
echo "For more information please refer to https://github.com/getsentry/onpremise/pull/430"
fi
}
replace_tsdb
2020-04-24 14:13:38 +02:00
echo ""
echo "Fetching and updating Docker images..."
echo ""
# We tag locally built images with an '-onpremise-local' suffix. docker-compose pull tries to pull these too and
# shows a 404 error on the console which is confusing and unnecessary. To overcome this, we add the stderr>stdout
# redirection below and pass it through grep, ignoring all lines having this '-onpremise-local' suffix.
$dc pull -q --ignore-pull-failures 2>& 1 | grep -v -- -onpremise-local || true
docker pull ${ SENTRY_IMAGE :- getsentry /sentry : latest }
2019-07-01 21:48:12 +02:00
echo ""
echo "Building and tagging Docker images..."
echo ""
2019-11-12 00:18:59 +01:00
# Build the sentry onpremise image first as it is needed for the cron image
2020-01-03 20:06:33 +01:00
$dc build --force-rm web
2020-01-12 16:53:54 +01:00
$dc build --force-rm --parallel
2019-07-01 21:48:12 +02:00
echo ""
echo "Docker images built."
2020-01-03 20:52:22 +01:00
echo "Bootstrapping Snuba..."
2020-01-21 20:56:45 +01:00
# `bootstrap` is for fresh installs, and `migrate` is for existing installs
# Running them both for both cases is harmless so we blindly run them
$dcr snuba-api bootstrap --force
$dcr snuba-api migrate
2020-01-03 20:52:22 +01:00
echo ""
2019-12-09 19:33:02 +01:00
# Very naively check whether there's an existing sentry-postgres volume and the PG version in it
if [ [ $( docker volume ls -q --filter name = sentry-postgres) && $( docker run --rm -v sentry-postgres:/db busybox cat /db/PG_VERSION 2>/dev/null) = = "9.5" ] ] ; then
2019-12-30 21:07:42 +01:00
docker volume rm sentry-postgres-new || true
2019-12-09 19:33:02 +01:00
# If this is Postgres 9.5 data, start upgrading it to 9.6 in a new volume
docker run --rm \
-v sentry-postgres:/var/lib/postgresql/9.5/data \
-v sentry-postgres-new:/var/lib/postgresql/9.6/data \
tianon/postgres-upgrade:9.5-to-9.6
# Get rid of the old volume as we'll rename the new one to that
docker volume rm sentry-postgres
docker volume create --name sentry-postgres
# There's no rename volume in Docker so copy the contents from old to new name
# Also append the `host all all all trust` line as `tianon/postgres-upgrade:9.5-to-9.6`
# doesn't do that automatically.
docker run --rm -v sentry-postgres-new:/from -v sentry-postgres:/to alpine ash -c \
"cd /from ; cp -av . /to ; echo 'host all all all trust' >> /to/pg_hba.conf"
# Finally, remove the new old volume as we are all in sentry-postgres now
docker volume rm sentry-postgres-new
fi
2019-07-01 21:48:12 +02:00
echo ""
echo "Setting up database..."
if [ $CI ] ; then
2020-01-03 20:06:33 +01:00
$dcr web upgrade --noinput
2019-07-01 21:48:12 +02:00
echo ""
echo "Did not prompt for user creation due to non-interactive shell."
echo "Run the following command to create one yourself (recommended):"
echo ""
echo " docker-compose run --rm web createuser"
echo ""
else
2020-01-03 20:06:33 +01:00
$dcr web upgrade
2019-07-01 21:48:12 +02:00
fi
2020-01-03 20:06:33 +01:00
2019-12-30 21:07:42 +01:00
SENTRY_DATA_NEEDS_MIGRATION = $( docker run --rm -v sentry-data:/data alpine ash -c "[ ! -d '/data/files' ] && ls -A1x /data | wc -l || true" )
2019-12-09 19:33:02 +01:00
if [ " $SENTRY_DATA_NEEDS_MIGRATION " ] ; then
echo "Migrating file storage..."
2019-12-30 21:07:42 +01:00
# Use the web (Sentry) image so the file owners are kept as sentry:sentry
2020-01-09 20:55:20 +01:00
# The `\"` escape pattern is to make this compatible w/ Git Bash on Windows. See #329.
$dcr --entrypoint \" /bin/bash\" web -c \
2020-01-13 20:25:27 +01:00
"mkdir -p /tmp/files; mv /data/* /tmp/files/; mv /tmp/files /data/files; chown -R sentry:sentry /data"
2019-12-09 19:33:02 +01:00
fi
2020-04-24 14:31:59 +02:00
if [ ! -f " $RELAY_CREDENTIALS_JSON " ] ; then
2020-05-04 20:44:34 +02:00
echo ""
echo "Generating Relay credentials..."
# We need the ugly hack below as `relay generate credentials` tries to read the config and the credentials
# even with the `--stdout` and `--overwrite` flags and then errors out when the credentials file exists but
# not valid JSON. We hit this case as we redirect output to the same config folder, creating an empty
# credentials file before relay runs.
$dcr --no-deps -v $( pwd ) /$RELAY_CONFIG_YML :/tmp/config.yml relay --config /tmp credentials generate --stdout > " $RELAY_CREDENTIALS_JSON "
echo " Relay credentials written to $RELAY_CREDENTIALS_JSON "
fi
RELAY_CREDENTIALS = $( sed -n 's/^.*"public_key"[[:space:]]*:[[:space:]]*"\([a-zA-Z0-9_-]\{1,\}\)".*$/\1/p' " $RELAY_CREDENTIALS_JSON " )
if [ -z " $RELAY_CREDENTIALS " ] ; then
>& 2 echo " FAIL: Cannot read credentials back from $RELAY_CREDENTIALS_JSON . "
>& 2 echo " Please ensure this file is readable and contains valid credentials."
>& 2 echo ""
exit 1
fi
if ! grep -q " \" $RELAY_CREDENTIALS \" " " $SENTRY_CONFIG_PY " ; then
echo " SENTRY_RELAY_WHITELIST_PK = (SENTRY_RELAY_WHITELIST_PK or []) + ([\" $RELAY_CREDENTIALS \"]) " >> " $SENTRY_CONFIG_PY "
echo " Relay public key written to $SENTRY_CONFIG_PY "
echo ""
2020-04-24 14:31:59 +02:00
fi
2019-07-01 21:48:12 +02:00
cleanup
echo ""
echo "----------------"
2019-11-12 00:18:59 +01:00
echo "You're all done! Run the following command to get Sentry running:"
2019-07-01 21:48:12 +02:00
echo ""
echo " docker-compose up -d"
2019-07-17 21:13:23 +02:00
echo ""