Skip to content

Commit c8de3aa

Browse files
authored
Merge pull request ClickHouse#76219 from ClickHouse/backport/24.8/75643
Backport ClickHouse#75643 to 24.8: Improve clickhouse-server entrypoint
2 parents 5563c09 + 37805a2 commit c8de3aa

File tree

5 files changed

+241
-109
lines changed

5 files changed

+241
-109
lines changed

docker/server/Dockerfile.ubuntu

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ RUN clickhouse-local -q 'SELECT * FROM system.build_options' \
125125
&& chmod ugo+Xrw -R /var/lib/clickhouse /var/log/clickhouse-server /etc/clickhouse-server /etc/clickhouse-client
126126

127127
RUN locale-gen en_US.UTF-8
128-
ENV LANG en_US.UTF-8
129-
ENV TZ UTC
128+
ENV LANG=en_US.UTF-8
129+
ENV TZ=UTC
130130

131131
RUN mkdir /docker-entrypoint-initdb.d
132132

@@ -136,6 +136,6 @@ COPY entrypoint.sh /entrypoint.sh
136136
EXPOSE 9000 8123 9009
137137
VOLUME /var/lib/clickhouse
138138

139-
ENV CLICKHOUSE_CONFIG /etc/clickhouse-server/config.xml
139+
ENV CLICKHOUSE_CONFIG=/etc/clickhouse-server/config.xml
140140

141141
ENTRYPOINT ["/entrypoint.sh"]

docker/server/entrypoint.sh

Lines changed: 130 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ readarray -t DISKS_METADATA_PATHS < <(clickhouse extract-from-config --config-fi
5454
CLICKHOUSE_USER="${CLICKHOUSE_USER:-default}"
5555
CLICKHOUSE_PASSWORD_FILE="${CLICKHOUSE_PASSWORD_FILE:-}"
5656
if [[ -n "${CLICKHOUSE_PASSWORD_FILE}" && -f "${CLICKHOUSE_PASSWORD_FILE}" ]]; then
57-
CLICKHOUSE_PASSWORD="$(cat "${CLICKHOUSE_PASSWORD_FILE}")"
57+
CLICKHOUSE_PASSWORD="$(cat "${CLICKHOUSE_PASSWORD_FILE}")"
5858
fi
5959
CLICKHOUSE_PASSWORD="${CLICKHOUSE_PASSWORD:-}"
6060
CLICKHOUSE_DB="${CLICKHOUSE_DB:-}"
@@ -88,29 +88,47 @@ function create_directory_and_do_chown() {
8888
fi
8989
}
9090

91-
create_directory_and_do_chown "$DATA_DIR"
92-
93-
# Change working directory to $DATA_DIR in case there're paths relative to $DATA_DIR, also avoids running
94-
# clickhouse-server at root directory.
95-
cd "$DATA_DIR"
96-
97-
for dir in "$ERROR_LOG_DIR" \
98-
"$LOG_DIR" \
99-
"$TMP_DIR" \
100-
"$USER_PATH" \
101-
"$FORMAT_SCHEMA_PATH" \
102-
"${DISKS_PATHS[@]}" \
103-
"${DISKS_METADATA_PATHS[@]}"
104-
do
105-
create_directory_and_do_chown "$dir"
106-
done
107-
108-
if [ "$CLICKHOUSE_SKIP_USER_SETUP" == "1" ]; then
109-
echo "$0: explicitly skip changing user 'default'"
110-
# if clickhouse user is defined - create it (user "default" already exists out of box)
111-
elif [ -n "$CLICKHOUSE_USER" ] && [ "$CLICKHOUSE_USER" != "default" ] || [ -n "$CLICKHOUSE_PASSWORD" ] || [ "$CLICKHOUSE_ACCESS_MANAGEMENT" != "0" ]; then
112-
echo "$0: create new user '$CLICKHOUSE_USER' instead 'default'"
113-
cat <<EOT > /etc/clickhouse-server/users.d/default-user.xml
91+
function manage_clickhouse_directories() {
92+
for dir in "$ERROR_LOG_DIR" \
93+
"$LOG_DIR" \
94+
"$TMP_DIR" \
95+
"$USER_PATH" \
96+
"$FORMAT_SCHEMA_PATH" \
97+
"${DISKS_PATHS[@]}" \
98+
"${DISKS_METADATA_PATHS[@]}"
99+
do
100+
create_directory_and_do_chown "$dir"
101+
done
102+
}
103+
104+
function manage_clickhouse_user() {
105+
# Check if the `defaul` user is changed through any mounted file. It will mean that user took care of it already
106+
# First, extract the users_xml.path and check it's relative or absolute
107+
local USERS_XML USERS_CONFIG
108+
USERS_XML=$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key='user_directories.users_xml.path')
109+
case $USERS_XML in
110+
/* ) # absolute path
111+
cp "$USERS_XML" /tmp
112+
USERS_CONFIG="/tmp/$(basename $USERS_XML)"
113+
;;
114+
* ) # relative path to the $CLICKHOUSE_CONFIG
115+
cp "$(dirname "$CLICKHOUSE_CONFIG")/${USERS_XML}" /tmp
116+
USERS_CONFIG="/tmp/$(basename $USERS_XML)"
117+
;;
118+
esac
119+
120+
# Compare original `users.default` to the processed one
121+
local ORIGINAL_DEFAULT PROCESSED_DEFAULT CLICKHOUSE_DEFAULT_CHANGED
122+
ORIGINAL_DEFAULT=$(clickhouse extract-from-config --config-file "$USERS_CONFIG" --key='users.default' | sha256sum)
123+
PROCESSED_DEFAULT=$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --users --key='users.default' --try | sha256sum)
124+
[ "$ORIGINAL_DEFAULT" == "$PROCESSED_DEFAULT" ] && CLICKHOUSE_DEFAULT_CHANGED=0 || CLICKHOUSE_DEFAULT_CHANGED=1
125+
126+
if [ "$CLICKHOUSE_SKIP_USER_SETUP" == "1" ]; then
127+
echo "$0: explicitly skip changing user 'default'"
128+
elif [ -n "$CLICKHOUSE_USER" ] && [ "$CLICKHOUSE_USER" != "default" ] || [ -n "$CLICKHOUSE_PASSWORD" ] || [ "$CLICKHOUSE_ACCESS_MANAGEMENT" != "0" ]; then
129+
# if clickhouse user is defined - create it (user "default" already exists out of box)
130+
echo "$0: create new user '$CLICKHOUSE_USER' instead 'default'"
131+
cat <<EOT > /etc/clickhouse-server/users.d/default-user.xml
114132
<clickhouse>
115133
<!-- Docs: <https://clickhouse.com/docs/en/operations/settings/settings_users/> -->
116134
<users>
@@ -130,9 +148,12 @@ elif [ -n "$CLICKHOUSE_USER" ] && [ "$CLICKHOUSE_USER" != "default" ] || [ -n "$
130148
</users>
131149
</clickhouse>
132150
EOT
133-
else
134-
echo "$0: neither CLICKHOUSE_USER nor CLICKHOUSE_PASSWORD is set, disabling network access for user '$CLICKHOUSE_USER'"
135-
cat <<EOT > /etc/clickhouse-server/users.d/default-user.xml
151+
elif [ "$CLICKHOUSE_DEFAULT_CHANGED" == "1" ]; then
152+
# Leave users as is, do nothing
153+
:
154+
else
155+
echo "$0: neither CLICKHOUSE_USER nor CLICKHOUSE_PASSWORD is set, disabling network access for user '$CLICKHOUSE_USER'"
156+
cat <<EOT > /etc/clickhouse-server/users.d/default-user.xml
136157
<clickhouse>
137158
<!-- Docs: <https://clickhouse.com/docs/en/operations/settings/settings_users/> -->
138159
<users>
@@ -146,85 +167,88 @@ else
146167
</users>
147168
</clickhouse>
148169
EOT
149-
fi
170+
fi
171+
}
150172
151173
CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS="${CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS:-}"
152174
153-
# checking $DATA_DIR for initialization
154-
if [ -d "${DATA_DIR%/}/data" ]; then
155-
DATABASE_ALREADY_EXISTS='true'
156-
fi
157-
158-
# run initialization if flag CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS is not empty or data directory is empty
159-
if [[ -n "${CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS}" || -z "${DATABASE_ALREADY_EXISTS}" ]]; then
160-
RUN_INITDB_SCRIPTS='true'
161-
fi
162-
163-
if [ -n "${RUN_INITDB_SCRIPTS}" ]; then
164-
if [ -n "$(ls /docker-entrypoint-initdb.d/)" ] || [ -n "$CLICKHOUSE_DB" ]; then
165-
# port is needed to check if clickhouse-server is ready for connections
166-
HTTP_PORT="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=http_port --try)"
167-
HTTPS_PORT="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=https_port --try)"
175+
function init_clickhouse_db() {
176+
# checking $DATA_DIR for initialization
177+
if [ -d "${DATA_DIR%/}/data" ]; then
178+
DATABASE_ALREADY_EXISTS='true'
179+
fi
168180
169-
if [ -n "$HTTP_PORT" ]; then
170-
URL="http://127.0.0.1:$HTTP_PORT/ping"
171-
else
172-
URL="https://127.0.0.1:$HTTPS_PORT/ping"
173-
fi
181+
# run initialization if flag CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS is not empty or data directory is empty
182+
if [[ -n "${CLICKHOUSE_ALWAYS_RUN_INITDB_SCRIPTS}" || -z "${DATABASE_ALREADY_EXISTS}" ]]; then
183+
RUN_INITDB_SCRIPTS='true'
184+
fi
174185
175-
# Listen only on localhost until the initialization is done
176-
clickhouse su "${USER}:${GROUP}" clickhouse-server --config-file="$CLICKHOUSE_CONFIG" -- --listen_host=127.0.0.1 &
177-
pid="$!"
186+
if [ -n "${RUN_INITDB_SCRIPTS}" ]; then
187+
if [ -n "$(ls /docker-entrypoint-initdb.d/)" ] || [ -n "$CLICKHOUSE_DB" ]; then
188+
# port is needed to check if clickhouse-server is ready for connections
189+
HTTP_PORT="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=http_port --try)"
190+
HTTPS_PORT="$(clickhouse extract-from-config --config-file "$CLICKHOUSE_CONFIG" --key=https_port --try)"
178191
179-
# check if clickhouse is ready to accept connections
180-
# will try to send ping clickhouse via http_port (max 1000 retries by default, with 1 sec timeout and 1 sec delay between retries)
181-
tries=${CLICKHOUSE_INIT_TIMEOUT:-1000}
182-
while ! wget --spider --no-check-certificate -T 1 -q "$URL" 2>/dev/null; do
183-
if [ "$tries" -le "0" ]; then
184-
echo >&2 'ClickHouse init process timeout.'
185-
exit 1
192+
if [ -n "$HTTP_PORT" ]; then
193+
URL="http://127.0.0.1:$HTTP_PORT/ping"
194+
else
195+
URL="https://127.0.0.1:$HTTPS_PORT/ping"
186196
fi
187-
tries=$(( tries-1 ))
188-
sleep 1
189-
done
190197
191-
clickhouseclient=( clickhouse-client --multiquery --host "127.0.0.1" -u "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" )
198+
# Listen only on localhost until the initialization is done
199+
clickhouse su "${USER}:${GROUP}" clickhouse-server --config-file="$CLICKHOUSE_CONFIG" -- --listen_host=127.0.0.1 &
200+
pid="$!"
192201
193-
echo
202+
# check if clickhouse is ready to accept connections
203+
# will try to send ping clickhouse via http_port (max 1000 retries by default, with 1 sec timeout and 1 sec delay between retries)
204+
tries=${CLICKHOUSE_INIT_TIMEOUT:-1000}
205+
while ! wget --spider --no-check-certificate -T 1 -q "$URL" 2>/dev/null; do
206+
if [ "$tries" -le "0" ]; then
207+
echo >&2 'ClickHouse init process timeout.'
208+
exit 1
209+
fi
210+
tries=$(( tries-1 ))
211+
sleep 1
212+
done
194213
195-
# create default database, if defined
196-
if [ -n "$CLICKHOUSE_DB" ]; then
197-
echo "$0: create database '$CLICKHOUSE_DB'"
198-
"${clickhouseclient[@]}" -q "CREATE DATABASE IF NOT EXISTS $CLICKHOUSE_DB";
199-
fi
214+
clickhouseclient=( clickhouse-client --multiquery --host "127.0.0.1" -u "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" )
200215
201-
for f in /docker-entrypoint-initdb.d/*; do
202-
case "$f" in
203-
*.sh)
204-
if [ -x "$f" ]; then
205-
echo "$0: running $f"
206-
"$f"
207-
else
208-
echo "$0: sourcing $f"
209-
# shellcheck source=/dev/null
210-
. "$f"
211-
fi
212-
;;
213-
*.sql) echo "$0: running $f"; "${clickhouseclient[@]}" < "$f" ; echo ;;
214-
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${clickhouseclient[@]}"; echo ;;
215-
*) echo "$0: ignoring $f" ;;
216-
esac
217216
echo
218-
done
219217
220-
if ! kill -s TERM "$pid" || ! wait "$pid"; then
221-
echo >&2 'Finishing of ClickHouse init process failed.'
222-
exit 1
218+
# create default database, if defined
219+
if [ -n "$CLICKHOUSE_DB" ]; then
220+
echo "$0: create database '$CLICKHOUSE_DB'"
221+
"${clickhouseclient[@]}" -q "CREATE DATABASE IF NOT EXISTS $CLICKHOUSE_DB";
222+
fi
223+
224+
for f in /docker-entrypoint-initdb.d/*; do
225+
case "$f" in
226+
*.sh)
227+
if [ -x "$f" ]; then
228+
echo "$0: running $f"
229+
"$f"
230+
else
231+
echo "$0: sourcing $f"
232+
# shellcheck source=/dev/null
233+
. "$f"
234+
fi
235+
;;
236+
*.sql) echo "$0: running $f"; "${clickhouseclient[@]}" < "$f" ; echo ;;
237+
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${clickhouseclient[@]}"; echo ;;
238+
*) echo "$0: ignoring $f" ;;
239+
esac
240+
echo
241+
done
242+
243+
if ! kill -s TERM "$pid" || ! wait "$pid"; then
244+
echo >&2 'Finishing of ClickHouse init process failed.'
245+
exit 1
246+
fi
223247
fi
248+
else
249+
echo "ClickHouse Database directory appears to contain a database; Skipping initialization"
224250
fi
225-
else
226-
echo "ClickHouse Database directory appears to contain a database; Skipping initialization"
227-
fi
251+
}
228252
229253
# if no args passed to `docker run` or first argument start with `--`, then the user is passing clickhouse-server arguments
230254
if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then
@@ -233,9 +257,24 @@ if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then
233257
CLICKHOUSE_WATCHDOG_ENABLE=${CLICKHOUSE_WATCHDOG_ENABLE:-0}
234258
export CLICKHOUSE_WATCHDOG_ENABLE
235259
260+
create_directory_and_do_chown "$DATA_DIR"
261+
262+
# Change working directory to $DATA_DIR in case there're paths relative to $DATA_DIR, also avoids running
263+
# clickhouse-server at root directory.
264+
cd "$DATA_DIR"
265+
266+
# Using functions here to avoid unnecessary work in case of launching other binaries,
267+
# inspired by postgres, mariadb etc. entrypoints
268+
# It is necessary to pass the docker library consistency test
269+
manage_clickhouse_directories
270+
manage_clickhouse_user
271+
init_clickhouse_db
272+
236273
# This replaces the shell script with the server:
237274
exec clickhouse su "${USER}:${GROUP}" clickhouse-server --config-file="$CLICKHOUSE_CONFIG" "$@"
238275
fi
239276
240277
# Otherwise, we assume the user want to run his own process, for example a `bash` shell to explore this image
241278
exec "$@"
279+
280+
# vi: ts=4: sw=4: sts=4: expandtab

programs/extract-from-config/ExtractFromConfig.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <filesystem>
12
#include <iostream>
23
#include <string>
34
#include <vector>
@@ -19,6 +20,16 @@
1920
#include <Common/parseGlobs.h>
2021
#include <Common/re2.h>
2122

23+
namespace DB
24+
{
25+
namespace ErrorCodes
26+
{
27+
extern const int CANNOT_LOAD_CONFIG;
28+
}
29+
}
30+
31+
namespace fs = std::filesystem;
32+
2233
static void setupLogging(const std::string & log_level)
2334
{
2435
Poco::AutoPtr<Poco::ConsoleChannel> channel(new Poco::ConsoleChannel);
@@ -79,8 +90,7 @@ static std::vector<std::string> extactFromConfigAccordingToGlobs(DB::Configurati
7990
}
8091

8192

82-
static std::vector<std::string> extractFromConfig(
83-
const std::string & config_path, const std::string & key, bool process_zk_includes, bool try_get = false)
93+
static DB::ConfigurationPtr get_configuration(const std::string & config_path, bool process_zk_includes)
8494
{
8595
DB::ConfigProcessor processor(config_path, /* throw_on_bad_incl = */ false, /* log_to_console = */ false);
8696
bool has_zk_includes;
@@ -97,7 +107,27 @@ static std::vector<std::string> extractFromConfig(
97107
zkutil::ZooKeeperNodeCache zk_node_cache([&] { return zookeeper; });
98108
config_xml = processor.processConfig(&has_zk_includes, &zk_node_cache);
99109
}
100-
DB::ConfigurationPtr configuration(new Poco::Util::XMLConfiguration(config_xml));
110+
return DB::ConfigurationPtr(new Poco::Util::XMLConfiguration(config_xml));
111+
}
112+
113+
114+
static std::vector<std::string> extractFromConfig(
115+
const std::string & config_path, const std::string & key, bool process_zk_includes, bool try_get = false, bool get_users = false)
116+
{
117+
DB::ConfigurationPtr configuration = get_configuration(config_path, process_zk_includes);
118+
119+
if (get_users)
120+
{
121+
bool has_user_directories = configuration->has("user_directories");
122+
if (!has_user_directories && !try_get)
123+
throw DB::Exception(DB::ErrorCodes::CANNOT_LOAD_CONFIG, "Can't load config for users");
124+
125+
std::string users_config_path = configuration->getString("user_directories.users_xml.path");
126+
const auto config_dir = fs::path{config_path}.remove_filename().string();
127+
if (fs::path(users_config_path).is_relative() && fs::exists(fs::path(config_dir) / users_config_path))
128+
users_config_path = fs::path(config_dir) / users_config_path;
129+
configuration = get_configuration(users_config_path, process_zk_includes);
130+
}
101131

102132
/// Check if a key has globs.
103133
if (key.find_first_of("*?{") != std::string::npos)
@@ -117,6 +147,7 @@ int mainEntryClickHouseExtractFromConfig(int argc, char ** argv)
117147
bool print_stacktrace = false;
118148
bool process_zk_includes = false;
119149
bool try_get = false;
150+
bool get_users = false;
120151
std::string log_level;
121152
std::string config_path;
122153
std::string key;
@@ -130,6 +161,7 @@ int mainEntryClickHouseExtractFromConfig(int argc, char ** argv)
130161
("process-zk-includes", po::bool_switch(&process_zk_includes),
131162
"if there are from_zk elements in config, connect to ZooKeeper and process them")
132163
("try", po::bool_switch(&try_get), "Do not warn about missing keys")
164+
("users", po::bool_switch(&get_users), "Return values from users.xml config")
133165
("log-level", po::value<std::string>(&log_level)->default_value("error"), "log level")
134166
("config-file,c", po::value<std::string>(&config_path)->required(), "path to config file")
135167
("key,k", po::value<std::string>(&key)->required(), "key to get value for");
@@ -156,7 +188,7 @@ int mainEntryClickHouseExtractFromConfig(int argc, char ** argv)
156188
po::notify(options);
157189

158190
setupLogging(log_level);
159-
for (const auto & value : extractFromConfig(config_path, key, process_zk_includes, try_get))
191+
for (const auto & value : extractFromConfig(config_path, key, process_zk_includes, try_get, get_users))
160192
std::cout << value << std::endl;
161193
}
162194
catch (...)

tests/ci/ci_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ def normalize_string(string: str) -> str:
298298
(",", "_"),
299299
("/", "_"),
300300
("-", "_"),
301+
(":", "_"),
301302
):
302303
res = res.replace(*r)
303304
return res

0 commit comments

Comments
 (0)