Skip to content

Commit cfbd716

Browse files
committed
Add Dockerfiles for TSAN/ASAN/UBSAN
1 parent e15ec3c commit cfbd716

File tree

5 files changed

+391
-2
lines changed

5 files changed

+391
-2
lines changed

Diff for: clickhouse-server/Dockerfile

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
FROM ubuntu:22.04
2+
3+
# see https://github.com/moby/moby/issues/4032#issuecomment-192327844
4+
# It could be removed after we move on a version 23:04+
5+
ARG DEBIAN_FRONTEND=noninteractive
6+
7+
# ARG for quick switch to a given ubuntu mirror
8+
ARG apt_archive="http://archive.ubuntu.com"
9+
10+
# We shouldn't use `apt upgrade` to not change the upstream image. It's updated biweekly
11+
12+
# user/group precreated explicitly with fixed uid/gid on purpose.
13+
# It is especially important for rootless containers: in that case entrypoint
14+
# can't do chown and owners of mounted volumes should be configured externally.
15+
# We do that in advance at the begining of Dockerfile before any packages will be
16+
# installed to prevent picking those uid / gid by some unrelated software.
17+
# The same uid / gid (101) is used both for alpine and ubuntu.
18+
RUN sed -i "s|http://archive.ubuntu.com|${apt_archive}|g" /etc/apt/sources.list \
19+
&& groupadd -r clickhouse --gid=101 \
20+
&& useradd -r -g clickhouse --uid=101 --home-dir=/var/lib/clickhouse --shell=/bin/bash clickhouse \
21+
&& apt-get update \
22+
&& apt-get install --yes --no-install-recommends \
23+
ca-certificates \
24+
locales \
25+
tzdata \
26+
wget \
27+
&& rm -rf /var/lib/apt/lists/* /var/cache/debconf /tmp/*
28+
29+
ARG REPO_CHANNEL="stable"
30+
ARG REPOSITORY="deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg] https://packages.clickhouse.com/deb ${REPO_CHANNEL} main"
31+
ARG VERSION="24.10.1.2812"
32+
ARG PACKAGES="clickhouse-client clickhouse-server clickhouse-common-static"
33+
34+
#docker-official-library:off
35+
# The part between `docker-official-library` tags is related to our builds
36+
37+
# set non-empty deb_location_url url to create a docker image
38+
# from debs created by CI build, for example:
39+
# docker build . --network host --build-arg version="21.4.1.6282" --build-arg deb_location_url="https://..." -t ...
40+
ARG deb_location_url=""
41+
ARG DIRECT_DOWNLOAD_URLS=""
42+
43+
# set non-empty single_binary_location_url to create docker image
44+
# from a single binary url (useful for non-standard builds - with sanitizers, for arm64).
45+
ARG single_binary_location_url=""
46+
47+
ARG TARGETARCH
48+
49+
# install from direct URL
50+
RUN if [ -n "${DIRECT_DOWNLOAD_URLS}" ]; then \
51+
echo "installing from custom predefined urls with deb packages: ${DIRECT_DOWNLOAD_URLS}" \
52+
&& rm -rf /tmp/clickhouse_debs \
53+
&& mkdir -p /tmp/clickhouse_debs \
54+
&& for url in $DIRECT_DOWNLOAD_URLS; do \
55+
wget --progress=bar:force:noscroll "$url" -P /tmp/clickhouse_debs || exit 1 \
56+
; done \
57+
&& dpkg -i /tmp/clickhouse_debs/*.deb \
58+
&& rm -rf /tmp/* ; \
59+
fi
60+
61+
# install from a web location with deb packages
62+
RUN arch="${TARGETARCH:-amd64}" \
63+
&& if [ -n "${deb_location_url}" ]; then \
64+
echo "installing from custom url with deb packages: ${deb_location_url}" \
65+
&& rm -rf /tmp/clickhouse_debs \
66+
&& mkdir -p /tmp/clickhouse_debs \
67+
&& for package in ${PACKAGES}; do \
68+
{ wget --progress=bar:force:noscroll "${deb_location_url}/${package}_${VERSION}_${arch}.deb" -P /tmp/clickhouse_debs || \
69+
wget --progress=bar:force:noscroll "${deb_location_url}/${package}_${VERSION}_all.deb" -P /tmp/clickhouse_debs ; } \
70+
|| exit 1 \
71+
; done \
72+
&& dpkg -i /tmp/clickhouse_debs/*.deb \
73+
&& rm -rf /tmp/* ; \
74+
fi
75+
76+
# install from a single binary
77+
RUN if [ -n "${single_binary_location_url}" ]; then \
78+
echo "installing from single binary url: ${single_binary_location_url}" \
79+
&& rm -rf /tmp/clickhouse_binary \
80+
&& mkdir -p /tmp/clickhouse_binary \
81+
&& wget --progress=bar:force:noscroll "${single_binary_location_url}" -O /tmp/clickhouse_binary/clickhouse \
82+
&& chmod +x /tmp/clickhouse_binary/clickhouse \
83+
&& /tmp/clickhouse_binary/clickhouse install --user "clickhouse" --group "clickhouse" \
84+
&& rm -rf /tmp/* ; \
85+
fi
86+
87+
# The rest is the same in the official docker and in our build system
88+
#docker-official-library:on
89+
90+
# A fallback to installation from ClickHouse repository
91+
RUN if ! clickhouse local -q "SELECT ''" > /dev/null 2>&1; then \
92+
apt-get update \
93+
&& apt-get install --yes --no-install-recommends \
94+
apt-transport-https \
95+
dirmngr \
96+
gnupg2 \
97+
&& mkdir -p /etc/apt/sources.list.d \
98+
&& GNUPGHOME=$(mktemp -d) \
99+
&& GNUPGHOME="$GNUPGHOME" gpg --batch --no-default-keyring \
100+
--keyring /usr/share/keyrings/clickhouse-keyring.gpg \
101+
--keyserver hkp://keyserver.ubuntu.com:80 \
102+
--recv-keys 3a9ea1193a97b548be1457d48919f6bd2b48d754 \
103+
&& rm -rf "$GNUPGHOME" \
104+
&& chmod +r /usr/share/keyrings/clickhouse-keyring.gpg \
105+
&& echo "${REPOSITORY}" > /etc/apt/sources.list.d/clickhouse.list \
106+
&& echo "installing from repository: ${REPOSITORY}" \
107+
&& apt-get update \
108+
&& for package in ${PACKAGES}; do \
109+
packages="${packages} ${package}=${VERSION}" \
110+
; done \
111+
&& apt-get install --allow-unauthenticated --yes --no-install-recommends ${packages} || exit 1 \
112+
&& rm -rf \
113+
/var/lib/apt/lists/* \
114+
/var/cache/debconf \
115+
/tmp/* \
116+
&& apt-get autoremove --purge -yq libksba8 \
117+
&& apt-get autoremove -yq \
118+
; fi
119+
120+
# post install
121+
# we need to allow "others" access to clickhouse folder, because docker container
122+
# can be started with arbitrary uid (openshift usecase)
123+
RUN clickhouse-local -q 'SELECT * FROM system.build_options' \
124+
&& mkdir -p /var/lib/clickhouse /var/log/clickhouse-server /etc/clickhouse-server /etc/clickhouse-client \
125+
&& chmod ugo+Xrw -R /var/lib/clickhouse /var/log/clickhouse-server /etc/clickhouse-server /etc/clickhouse-client
126+
127+
RUN locale-gen en_US.UTF-8
128+
ENV LANG en_US.UTF-8
129+
ENV LANGUAGE en_US:en
130+
ENV LC_ALL en_US.UTF-8
131+
ENV TZ UTC
132+
133+
RUN mkdir /docker-entrypoint-initdb.d
134+
135+
COPY docker_related_config.xml /etc/clickhouse-server/config.d/
136+
COPY entrypoint.sh /entrypoint.sh
137+
138+
EXPOSE 9000 8123 9009
139+
VOLUME /var/lib/clickhouse
140+
141+
ENV CLICKHOUSE_CONFIG /etc/clickhouse-server/config.xml
142+
143+
ENTRYPOINT ["/entrypoint.sh"]

Diff for: clickhouse-server/Makefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
flavor = tsan
2+
version = 24.11.1.1178%2B$(flavor)
3+
url = https://s3.amazonaws.com/clickhouse-builds/24.11/506b30fd4997c11273567cb96410fc0324d3bb12/package_$(flavor)
4+
docker_registry = us-central1-docker.pkg.dev/molten-verve-216720/clickhouse-repository
5+
6+
all:
7+
docker build . -t clickhouse-server-$(flavor):latest --build-arg VERSION="$(version)" --build-arg deb_location_url="$(url)"
8+
docker tag clickhouse-server-$(flavor) $(docker_registry)/clickhouse-server-$(flavor):latest
9+
10+
push:
11+
docker push $(docker_registry)/clickhouse-server-$(flavor):latest
12+
13+
.PHONY: %

Diff for: clickhouse-server/docker_related_config.xml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<clickhouse>
2+
<!-- Listen wildcard address to allow accepting connections from other containers and host network. -->
3+
<listen_host>::</listen_host>
4+
<listen_host>0.0.0.0</listen_host>
5+
<listen_try>1</listen_try>
6+
7+
<!--
8+
<logger>
9+
<console>1</console>
10+
</logger>
11+
-->
12+
</clickhouse>

Diff for: clickhouse-server/entrypoint.sh

+221
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#!/bin/bash
2+
3+
set -eo pipefail
4+
shopt -s nullglob
5+
6+
DO_CHOWN=1
7+
if [ "${CLICKHOUSE_DO_NOT_CHOWN:-0}" = "1" ]; then
8+
DO_CHOWN=0
9+
fi
10+
11+
CLICKHOUSE_UID="${CLICKHOUSE_UID:-"$(id -u clickhouse)"}"
12+
CLICKHOUSE_GID="${CLICKHOUSE_GID:-"$(id -g clickhouse)"}"
13+
14+
# support --user
15+
if [ "$(id -u)" = "0" ]; then
16+
USER=$CLICKHOUSE_UID
17+
GROUP=$CLICKHOUSE_GID
18+
else
19+
USER="$(id -u)"
20+
GROUP="$(id -g)"
21+
DO_CHOWN=0
22+
fi
23+
24+
# set some vars
25+
CLICKHOUSE_CONFIG="${CLICKHOUSE_CONFIG:-/etc/clickhouse-server/config.xml}"
26+
27+
# get CH directories locations
28+
DATA_DIR="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=path || true)"
29+
TMP_DIR="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=tmp_path || true)"
30+
USER_PATH="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=user_files_path || true)"
31+
LOG_PATH="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=logger.log || true)"
32+
LOG_DIR=""
33+
if [ -n "$LOG_PATH" ]; then LOG_DIR="$(dirname "$LOG_PATH")"; fi
34+
ERROR_LOG_PATH="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=logger.errorlog || true)"
35+
ERROR_LOG_DIR=""
36+
if [ -n "$ERROR_LOG_PATH" ]; then ERROR_LOG_DIR="$(dirname "$ERROR_LOG_PATH")"; fi
37+
FORMAT_SCHEMA_PATH="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=format_schema_path || true)"
38+
39+
# There could be many disks declared in config
40+
readarray -t DISKS_PATHS < <(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key='storage_configuration.disks.*.path' || true)
41+
readarray -t DISKS_METADATA_PATHS < <(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key='storage_configuration.disks.*.metadata_path' || true)
42+
43+
CLICKHOUSE_USER="${CLICKHOUSE_USER:-default}"
44+
CLICKHOUSE_PASSWORD_FILE="${CLICKHOUSE_PASSWORD_FILE:-}"
45+
if [[ -n "${CLICKHOUSE_PASSWORD_FILE}" && -f "${CLICKHOUSE_PASSWORD_FILE}" ]]; then
46+
CLICKHOUSE_PASSWORD="$(cat "${CLICKHOUSE_PASSWORD_FILE}")"
47+
fi
48+
CLICKHOUSE_PASSWORD="${CLICKHOUSE_PASSWORD:-}"
49+
CLICKHOUSE_DB="${CLICKHOUSE_DB:-}"
50+
CLICKHOUSE_ACCESS_MANAGEMENT="${CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT:-0}"
51+
52+
function create_directory_and_do_chown() {
53+
local dir=$1
54+
# check if variable not empty
55+
[ -z "$dir" ] && return
56+
# ensure directories exist
57+
if [ "$DO_CHOWN" = "1" ]; then
58+
mkdir="mkdir"
59+
else
60+
# if DO_CHOWN=0 it means that the system does not map root user to "admin" permissions
61+
# it mainly happens on NFS mounts where root==nobody for security reasons
62+
# thus mkdir MUST run with user id/gid and not from nobody that has zero permissions
63+
mkdir="/usr/bin/clickhouse su "${USER}:${GROUP}" mkdir"
64+
fi
65+
if ! $mkdir -p "$dir"; then
66+
echo "Couldn't create necessary directory: $dir"
67+
exit 1
68+
fi
69+
70+
if [ "$DO_CHOWN" = "1" ]; then
71+
# ensure proper directories permissions
72+
# but skip it for if directory already has proper premissions, cause recursive chown may be slow
73+
if [ "$(stat -c %u "$dir")" != "$USER" ] || [ "$(stat -c %g "$dir")" != "$GROUP" ]; then
74+
chown -R "$USER:$GROUP" "$dir"
75+
fi
76+
fi
77+
}
78+
79+
create_directory_and_do_chown "$DATA_DIR"
80+
81+
# Change working directory to $DATA_DIR in case there're paths relative to $DATA_DIR, also avoids running
82+
# clickhouse-server at root directory.
83+
cd "$DATA_DIR"
84+
85+
for dir in "$ERROR_LOG_DIR" \
86+
"$LOG_DIR" \
87+
"$TMP_DIR" \
88+
"$USER_PATH" \
89+
"$FORMAT_SCHEMA_PATH" \
90+
"${DISKS_PATHS[@]}" \
91+
"${DISKS_METADATA_PATHS[@]}"
92+
do
93+
create_directory_and_do_chown "$dir"
94+
done
95+
96+
# if clickhouse user is defined - create it (user "default" already exists out of box)
97+
if [ -n "$CLICKHOUSE_USER" ] && [ "$CLICKHOUSE_USER" != "default" ] || [ -n "$CLICKHOUSE_PASSWORD" ] || [ "$CLICKHOUSE_ACCESS_MANAGEMENT" != "0" ]; then
98+
echo "$0: create new user '$CLICKHOUSE_USER' instead 'default'"
99+
cat <<EOT > /etc/clickhouse-server/users.d/default-user.xml
100+
<clickhouse>
101+
<!-- Docs: <https://clickhouse.com/docs/en/operations/settings/settings_users/> -->
102+
<users>
103+
<!-- Remove default user -->
104+
<default remove="remove">
105+
</default>
106+
107+
<${CLICKHOUSE_USER}>
108+
<profile>default</profile>
109+
<networks>
110+
<ip>::/0</ip>
111+
</networks>
112+
<password><![CDATA[${CLICKHOUSE_PASSWORD//]]>/]]]]><![CDATA[>}]]></password>
113+
<quota>default</quota>
114+
<access_management>${CLICKHOUSE_ACCESS_MANAGEMENT}</access_management>
115+
</${CLICKHOUSE_USER}>
116+
</users>
117+
</clickhouse>
118+
EOT
119+
fi
120+
121+
CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS="${CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS:-}"
122+
123+
# checking $DATA_DIR for initialization
124+
if [ -d "${DATA_DIR%/}/data" ]; then
125+
DATABASE_ALREADY_EXISTS='true'
126+
fi
127+
128+
# run initialization if flag CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS is not empty or data directory is empty
129+
if [[ -n "${CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS}" || -z "${DATABASE_ALREADY_EXISTS}" ]]; then
130+
RUN_INITDB_SCRIPTS='true'
131+
fi
132+
133+
if [ -n "${RUN_INITDB_SCRIPTS}" ]; then
134+
if [ -n "$(ls /docker-entrypoint-initdb.d/)" ] || [ -n "$CLICKHOUSE_DB" ]; then
135+
# port is needed to check if clickhouse-server is ready for connections
136+
HTTP_PORT="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=http_port --try)"
137+
HTTPS_PORT="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=https_port --try)"
138+
139+
if [ -n "$HTTP_PORT" ]; then
140+
URL="http://127.0.0.1:$HTTP_PORT/ping"
141+
else
142+
URL="https://127.0.0.1:$HTTPS_PORT/ping"
143+
fi
144+
145+
# Listen only on localhost until the initialization is done
146+
/usr/bin/clickhouse su "${USER}:${GROUP}" /usr/bin/clickhouse-server --config-file="$CLICKHOUSE_CONFIG" -- --listen_host=127.0.0.1 &
147+
pid="$!"
148+
149+
# check if clickhouse is ready to accept connections
150+
# will try to send ping clickhouse via http_port (max 1000 retries by default, with 1 sec timeout and 1 sec delay between retries)
151+
tries=${CLICKHOUSE_INIT_TIMEOUT:-1000}
152+
while ! wget --spider --no-check-certificate -T 1 -q "$URL" 2>/dev/null; do
153+
if [ "$tries" -le "0" ]; then
154+
echo >&2 'ClickHouse init process failed.'
155+
exit 1
156+
fi
157+
tries=$(( tries-1 ))
158+
sleep 1
159+
done
160+
161+
clickhouseclient=( clickhouse-client --multiquery --host "127.0.0.1" -u "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" )
162+
163+
echo
164+
165+
# create default database, if defined
166+
if [ -n "$CLICKHOUSE_DB" ]; then
167+
echo "$0: create database '$CLICKHOUSE_DB'"
168+
"${clickhouseclient[@]}" -q "CREATE DATABASE IF NOT EXISTS $CLICKHOUSE_DB";
169+
fi
170+
171+
for f in /docker-entrypoint-initdb.d/*; do
172+
case "$f" in
173+
*.sh)
174+
if [ -x "$f" ]; then
175+
echo "$0: running $f"
176+
"$f"
177+
else
178+
echo "$0: sourcing $f"
179+
# shellcheck source=/dev/null
180+
. "$f"
181+
fi
182+
;;
183+
*.sql) echo "$0: running $f"; "${clickhouseclient[@]}" < "$f" ; echo ;;
184+
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${clickhouseclient[@]}"; echo ;;
185+
*) echo "$0: ignoring $f" ;;
186+
esac
187+
echo
188+
done
189+
190+
if ! kill -s TERM "$pid" || ! wait "$pid"; then
191+
echo >&2 'Finishing of ClickHouse init process failed.'
192+
exit 1
193+
fi
194+
fi
195+
else
196+
echo "ClickHouse Database directory appears to contain a database; Skipping initialization"
197+
fi
198+
199+
# if no args passed to `docker run` or first argument start with `--`, then the user is passing clickhouse-server arguments
200+
if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then
201+
# Watchdog is launched by default, but does not send SIGINT to the main process,
202+
# so the container can't be finished by ctrl+c
203+
CLICKHOUSE_WATCHDOG_ENABLE=${CLICKHOUSE_WATCHDOG_ENABLE:-0}
204+
export CLICKHOUSE_WATCHDOG_ENABLE
205+
206+
# An option for easy restarting and replacing clickhouse-server in a container, especially in Kubernetes.
207+
# For example, you can replace the clickhouse-server binary to another and restart it while keeping the container running.
208+
if [[ "${CLICKHOUSE_DOCKER_RESTART_ON_EXIT:-0}" -eq "1" ]]; then
209+
while true; do
210+
# This runs the server as a child process of the shell script:
211+
/usr/bin/clickhouse su "${USER}:${GROUP}" /usr/bin/clickhouse-server --config-file="$CLICKHOUSE_CONFIG" "$@" ||:
212+
echo >&2 'ClickHouse Server exited, and the environment variable CLICKHOUSE_DOCKER_RESTART_ON_EXIT is set to 1. Restarting the server.'
213+
done
214+
else
215+
# This replaces the shell script with the server:
216+
exec /usr/bin/clickhouse su "${USER}:${GROUP}" /usr/bin/clickhouse-server --config-file="$CLICKHOUSE_CONFIG" "$@"
217+
fi
218+
fi
219+
220+
# Otherwise, we assume the user want to run his own process, for example a `bash` shell to explore this image
221+
exec "$@"

0 commit comments

Comments
 (0)