This repository has been archived by the owner on Dec 9, 2021. It is now read-only.
forked from getsentry/self-hosted
-
Notifications
You must be signed in to change notification settings - Fork 0
/
install.sh
executable file
·346 lines (294 loc) · 13.9 KB
/
install.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#!/usr/bin/env bash
set -e
if [[ -n "$MSYSTEM" ]]; then
echo "Seems like you are using an MSYS2-based system (such as Git Bash) which is not supported. Please use WSL instead.";
exit 1
fi
# Read .env for default values with a tip o' the hat to https://stackoverflow.com/a/59831605/90297
t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t
dc="docker-compose --no-ansi"
dcr="$dc run --rm"
# 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")
MIN_DOCKER_VERSION='19.03.6'
MIN_COMPOSE_VERSION='1.24.1'
MIN_RAM=2400 # MB
# Increase the default 10 second SIGTERM timeout
# to ensure celery queues are properly drained
# between upgrades as task signatures may change across
# versions
STOP_TIMEOUT=60 # seconds
SENTRY_CONFIG_PY='sentry/sentry.conf.py'
SENTRY_CONFIG_YML='sentry/config.yml'
SYMBOLICATOR_CONFIG_YML='symbolicator/config.yml'
RELAY_CONFIG_YML='relay/config.yml'
RELAY_CREDENTIALS_JSON='relay/credentials.json'
SENTRY_EXTRA_REQUIREMENTS='sentry/requirements.txt'
MINIMIZE_DOWNTIME=
show_help() {
cat <<EOF
Usage: $0 [options]
Install Sentry with docker-compose.
Options:
-h, --help Show this message and exit.
--no-user-prompt Skips the initial user creation prompt (ideal for non-interactive installs).
--minimize-downtime EXPERIMENTAL: try to keep accepting events for as long as possible while upgrading.
This will disable cleanup on error, and might leave your installation in partially upgraded state.
This option might not reload all configuration, and is only meant for in-place upgrades.
EOF
}
while (( $# )); do
case "$1" in
-h | --help) show_help; exit;;
--no-user-prompt) SKIP_USER_PROMPT=1;;
--minimize-downtime) MINIMIZE_DOWNTIME=1;;
--) ;;
*) echo "Unexpected argument: $1. Use --help for usage information."; exit 1;;
esac
shift
done
# Courtesy of https://stackoverflow.com/a/2183063/90297
trap_with_arg() {
func="$1" ; shift
for sig ; do
trap "$func $sig "'$LINENO' "$sig"
done
}
DID_CLEAN_UP=0
# the cleanup function will be the exit point
cleanup () {
if [[ "$DID_CLEAN_UP" -eq 1 ]]; then
return 0;
fi
DID_CLEAN_UP=1
if [[ "$1" != "EXIT" ]]; then
echo "An error occurred, caught SIG$1 on line $2";
if [[ -n "$MINIMIZE_DOWNTIME" ]]; then
echo "*NOT* cleaning up, to clean your environment run \"docker-compose stop\"."
else
echo "Cleaning up..."
fi
fi
if [[ -z "$MINIMIZE_DOWNTIME" ]]; then
$dc stop -t $STOP_TIMEOUT &> /dev/null
fi
}
trap_with_arg cleanup ERR INT TERM EXIT
echo "Checking minimum requirements..."
DOCKER_VERSION=$(docker version --format '{{.Server.Version}}')
COMPOSE_VERSION=$($dc --version | sed 's/docker-compose version \(.\{1,\}\),.*/\1/')
RAM_AVAILABLE_IN_DOCKER=$(docker run --rm busybox free -m 2>/dev/null | awk '/Mem/ {print $2}');
# 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); }'; }
# 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
}
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"
exit 1
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"
exit 1
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"
exit 1
fi
#SSE4.2 required by Clickhouse (https://clickhouse.yandex/docs/en/operations/requirements/)
# 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" -eq 0 ]]; then
SUPPORTS_SSE42=$(docker run --rm busybox grep -c sse4_2 /proc/cpuinfo || :)
if [[ "$SUPPORTS_SSE42" -eq 0 ]]; then
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."
exit 1
fi
fi
echo ""
echo "Creating volumes for persistent storage..."
echo "Created $(docker volume create --name=sentry-data)."
echo "Created $(docker volume create --name=sentry-postgres)."
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)."
echo ""
ensure_file_from_example $SENTRY_CONFIG_PY
ensure_file_from_example $SENTRY_CONFIG_YML
ensure_file_from_example $SENTRY_EXTRA_REQUIREMENTS
ensure_file_from_example $SYMBOLICATOR_CONFIG_YML
ensure_file_from_example $RELAY_CONFIG_YML
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
replace_tsdb() {
if (
[[ -f "$SENTRY_CONFIG_PY" ]] &&
! grep -xq 'SENTRY_TSDB = "sentry.tsdb.redissnuba.RedisSnubaTSDB"' "$SENTRY_CONFIG_PY"
); then
# Do NOT indent the following string as it would be reflected in the end result,
# breaking the final config file. See getsentry/onpremise#624.
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
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
# We may not have the set image on the repo (local images) so allow fails
docker pull ${SENTRY_IMAGE}${SENTRY_PYTHON3:+-py3} || true;
echo ""
echo "Building and tagging Docker images..."
echo ""
# Build the sentry onpremise image first as it is needed for the cron image
$dc build --force-rm web
$dc build --force-rm --parallel
echo ""
echo "Docker images built."
if [[ -n "$MINIMIZE_DOWNTIME" ]]; then
# Stop everything but relay and nginx
$dc rm -fsv $($dc config --services | grep -v -E '^(nginx|relay)$')
else
# Clean up old stuff and ensure nothing is working while we install/update
# This is for older versions of on-premise:
$dc -p onpremise down -t $STOP_TIMEOUT --rmi local --remove-orphans
# This is for newer versions
$dc down -t $STOP_TIMEOUT --rmi local --remove-orphans
fi
ZOOKEEPER_SNAPSHOT_FOLDER_EXISTS=$($dcr zookeeper bash -c 'ls 2>/dev/null -Ubad1 -- /var/lib/zookeeper/data/version-2 | wc -l | tr -d '[:space:]'')
if [[ "$ZOOKEEPER_SNAPSHOT_FOLDER_EXISTS" -eq 1 ]]; then
ZOOKEEPER_LOG_FILE_COUNT=$($dcr zookeeper bash -c 'ls 2>/dev/null -Ubad1 -- /var/lib/zookeeper/log/version-2/* | wc -l | tr -d '[:space:]'')
ZOOKEEPER_SNAPSHOT_FILE_COUNT=$($dcr zookeeper bash -c 'ls 2>/dev/null -Ubad1 -- /var/lib/zookeeper/data/version-2/* | wc -l | tr -d '[:space:]'')
# This is a workaround for a ZK upgrade bug: https://issues.apache.org/jira/browse/ZOOKEEPER-3056
if [[ "$ZOOKEEPER_LOG_FILE_COUNT" -gt 0 ]] && [[ "$ZOOKEEPER_SNAPSHOT_FILE_COUNT" -eq 0 ]]; then
$dcr -v $(pwd)/zookeeper:/temp zookeeper bash -c 'cp /temp/snapshot.0 /var/lib/zookeeper/data/version-2/snapshot.0'
$dc run -d -e ZOOKEEPER_SNAPSHOT_TRUST_EMPTY=true zookeeper
fi
fi
echo "Bootstrapping and migrating Snuba..."
$dcr snuba-api bootstrap --no-migrate --force
$dcr snuba-api migrations migrate --force
echo ""
# NOTE: This step relies on `kafka` being available from the previous `snuba-api bootstrap` step
# XXX(BYK): We cannot use auto.create.topics as Confluence and Apache hates it now (and makes it very hard to enable)
EXISTING_KAFKA_TOPICS=$($dcr kafka kafka-topics --list --bootstrap-server kafka:9092 2>/dev/null)
NEEDED_KAFKA_TOPICS="ingest-attachments ingest-transactions ingest-events"
for topic in $NEEDED_KAFKA_TOPICS; do
if ! echo "$EXISTING_KAFKA_TOPICS" | grep -wq $topic; then
echo "Creating additional Kafka topics..."
$dcr kafka kafka-topics --create --topic $topic --bootstrap-server kafka:9092
echo ""
fi
done
# Very naively check whether there's an existing sentry-postgres volume and the PG version in it
if [[ -n "$(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
docker volume rm sentry-postgres-new || true
# 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
echo ""
echo "Setting up database..."
if [[ -n "$CI" || "$SKIP_USER_PROMPT" == 1 ]]; then
$dcr web upgrade --noinput
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
$dcr web upgrade
fi
SENTRY_DATA_NEEDS_MIGRATION=$(docker run --rm -v sentry-data:/data alpine ash -c "[ ! -d '/data/files' ] && ls -A1x /data | wc -l || true")
if [[ -n "$SENTRY_DATA_NEEDS_MIGRATION" ]]; then
echo "Migrating file storage..."
# Use the web (Sentry) image so the file owners are kept as sentry:sentry
# The `\"` escape pattern is to make this compatible w/ Git Bash on Windows. See #329.
$dcr --entrypoint \"/bin/bash\" web -c \
"mkdir -p /tmp/files; mv /data/* /tmp/files/; mv /tmp/files /data/files; chown -R sentry:sentry /data"
fi
if [[ ! -f "$RELAY_CREDENTIALS_JSON" ]]; then
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
if [[ "$MINIMIZE_DOWNTIME" ]]; then
# Start the whole setup, except nginx and relay.
$dc up -d --remove-orphans $($dc config --services | grep -v -E '^(nginx|relay)$')
$dc exec -T nginx service nginx reload
echo "Waiting for Sentry to start..."
docker run --rm --network="${COMPOSE_PROJECT_NAME}_default" alpine ash \
-c 'while [[ "$(wget -T 1 -q -O- http://web:9000/_health/)" != "ok" ]]; do sleep 0.5; done'
# Make sure everything is up. This should only touch relay and nginx
$dc up -d
else
echo ""
echo "----------------"
echo "You're all done! Run the following command to get Sentry running:"
echo ""
echo " docker-compose up -d"
echo ""
fi