Skip to content

Commit fe3de80

Browse files
committed
Put PG data to second drive
The bootstrap.sh and install.sh scripts were reworked to configure the second drive and put PG data on it. Also some improvements done for future update procedure. Signed-off-by: Anton Kremenetsky <anton.kremenetsky@gmail.com>
1 parent da81fa5 commit fe3de80

3 files changed

Lines changed: 329 additions & 16 deletions

File tree

genesis/images/bootstrap.sh

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,297 @@ set -eu
2020
set -x
2121
set -o pipefail
2222

23+
GC_PATH="/opt/genesis_core"
24+
GC_CFG_DIR=/etc/genesis_core
25+
VENV_PATH="$GC_PATH/.venv"
26+
27+
# Disk auto-provisioning for Genesis data directory.
28+
#
29+
# Goal:
30+
# - Detect the "secondary" disk (any disk that is not the root disk)
31+
# - If it has no partitions: create a single GPT partition
32+
# - If the partition has no filesystem: format it as ext4
33+
# - Ensure it is mounted at /var/lib/genesis/data
34+
# - Ensure a persistent /etc/fstab entry using UUID
35+
#
36+
# This block is intended to be idempotent and safe to run on every boot.
37+
38+
log() {
39+
echo "[genesis-bootstrap] $*"
40+
}
41+
42+
host_mountpoint() {
43+
if command -v nsenter >/dev/null 2>&1; then
44+
nsenter -t 1 -m -- mountpoint -q "$1"
45+
return $?
46+
fi
47+
48+
mountpoint -q "$1"
49+
}
50+
51+
# Identify the block device that backs the root filesystem.
52+
# Example output:
53+
# [genesis-bootstrap] root source: /dev/vda1
54+
# [genesis-bootstrap] root disk: vda
55+
ROOT_SRC="$(findmnt -n -o SOURCE / || true)"
56+
ROOT_DISK=""
57+
if [[ -n "${ROOT_SRC}" ]]; then
58+
ROOT_DISK="$(lsblk -no PKNAME "${ROOT_SRC}" 2>/dev/null || true)"
59+
fi
60+
log "root source: ${ROOT_SRC:-<unknown>}"
61+
log "root disk: ${ROOT_DISK:-<unknown>}"
62+
63+
# Pick the first disk device that is not the root disk.
64+
# Example output:
65+
# [genesis-bootstrap] secondary disk candidate: vdb
66+
SECOND_DISK="$(lsblk -dn -o NAME,TYPE | awk -v root_disk="${ROOT_DISK}" '$2=="disk" && $1!=root_disk {print $1; exit}')"
67+
log "secondary disk candidate: ${SECOND_DISK:-<none>}"
68+
69+
if [[ -n "${SECOND_DISK}" ]]; then
70+
DISK_DEV="/dev/${SECOND_DISK}"
71+
72+
# Count partitions on the selected disk.
73+
# Example output:
74+
# [genesis-bootstrap] /dev/vdb partitions: 0
75+
PART_COUNT="$(lsblk -nr -o TYPE "${DISK_DEV}" | awk '$1=="part" {c++} END {print c+0}')"
76+
log "${DISK_DEV} partitions: ${PART_COUNT}"
77+
78+
# Create a single partition when there are no partitions yet.
79+
# Example output:
80+
# [genesis-bootstrap] creating GPT partition table and one partition on /dev/vdb
81+
if [[ "${PART_COUNT}" -eq 0 ]]; then
82+
log "creating GPT partition table and one partition on ${DISK_DEV}"
83+
sfdisk --label gpt "${DISK_DEV}" <<'EOF'
84+
,,
85+
EOF
86+
partprobe "${DISK_DEV}" || true
87+
udevadm settle || true
88+
else
89+
log "${DISK_DEV} already has partitions; skipping partition creation"
90+
fi
91+
92+
# Pick the first partition on the disk.
93+
# Example output:
94+
# [genesis-bootstrap] selected partition: /dev/vdb1
95+
PART_NAME="$(lsblk -nr -o NAME,TYPE "${DISK_DEV}" | awk '$2=="part" {print $1; exit}')"
96+
if [[ -n "${PART_NAME}" ]]; then
97+
PART_DEV="/dev/${PART_NAME}"
98+
log "selected partition: ${PART_DEV}"
99+
100+
# Detect filesystem type; create ext4 if unformatted.
101+
# Example output:
102+
# [genesis-bootstrap] filesystem on /dev/vdb1: <none>
103+
# [genesis-bootstrap] formatting /dev/vdb1 as ext4
104+
FS_TYPE="$(blkid -o value -s TYPE "${PART_DEV}" 2>/dev/null || true)"
105+
log "filesystem on ${PART_DEV}: ${FS_TYPE:-<none>}"
106+
if [[ -z "${FS_TYPE}" ]]; then
107+
log "formatting ${PART_DEV} as ext4"
108+
mkfs.ext4 -F "${PART_DEV}"
109+
FS_TYPE="ext4"
110+
else
111+
log "${PART_DEV} already has filesystem '${FS_TYPE}'; skipping mkfs"
112+
fi
113+
114+
# Mount and persist only if it is ext4.
115+
if [[ "${FS_TYPE}" == "ext4" ]]; then
116+
MOUNTPOINT="/var/lib/genesis/data"
117+
118+
# Ensure mountpoint exists.
119+
# Example output:
120+
# [genesis-bootstrap] ensuring mountpoint exists: /var/lib/genesis/data
121+
mkdir -p "${MOUNTPOINT}"
122+
log "ensuring mountpoint exists: ${MOUNTPOINT}"
123+
124+
# Ensure /etc/fstab has the correct UUID-based entry.
125+
# Example output:
126+
# [genesis-bootstrap] partition UUID: 1234-...
127+
# [genesis-bootstrap] updating /etc/fstab entry for /var/lib/genesis/data
128+
UUID="$(blkid -o value -s UUID "${PART_DEV}" 2>/dev/null || true)"
129+
if [[ -n "${UUID}" ]]; then
130+
log "partition UUID: ${UUID}"
131+
if grep -qs "[[:space:]]${MOUNTPOINT}[[:space:]]" /etc/fstab; then
132+
if ! grep -qs "^[[:space:]]*UUID=${UUID}[[:space:]].*[[:space:]]${MOUNTPOINT}[[:space:]]" /etc/fstab; then
133+
log "updating /etc/fstab entry for ${MOUNTPOINT}"
134+
TMP_FSTAB="$(mktemp)"
135+
awk -v mp="${MOUNTPOINT}" '!(($2==mp)) {print}' /etc/fstab > "${TMP_FSTAB}"
136+
printf '%s\n' "UUID=${UUID} ${MOUNTPOINT} ext4 defaults,nofail 0 2" >> "${TMP_FSTAB}"
137+
mv "${TMP_FSTAB}" /etc/fstab
138+
139+
# Reload systemd units generated from fstab so the mount can work on first run.
140+
# Example output:
141+
# [genesis-bootstrap] running: systemctl daemon-reload
142+
if command -v systemctl >/dev/null 2>&1; then
143+
log "running: systemctl daemon-reload"
144+
systemctl daemon-reload || true
145+
fi
146+
else
147+
log "/etc/fstab already has the correct UUID entry for ${MOUNTPOINT}"
148+
fi
149+
else
150+
log "adding /etc/fstab entry for ${MOUNTPOINT}"
151+
printf '%s\n' "UUID=${UUID} ${MOUNTPOINT} ext4 defaults,nofail 0 2" >> /etc/fstab
152+
153+
# Reload systemd units generated from fstab so the mount can work on first run.
154+
# Example output:
155+
# [genesis-bootstrap] running: systemctl daemon-reload
156+
if command -v systemctl >/dev/null 2>&1; then
157+
log "running: systemctl daemon-reload"
158+
systemctl daemon-reload || true
159+
fi
160+
fi
161+
else
162+
log "could not determine UUID for ${PART_DEV}; skipping fstab update"
163+
fi
164+
165+
# Mount if not mounted yet.
166+
# Example output:
167+
# [genesis-bootstrap] mounting /var/lib/genesis/data
168+
if ! host_mountpoint "${MOUNTPOINT}"; then
169+
log "mounting ${MOUNTPOINT}"
170+
mount "${MOUNTPOINT}" || mount "${PART_DEV}" "${MOUNTPOINT}"
171+
172+
# Some systemd units may run with an isolated mount namespace (e.g. PrivateMounts=yes).
173+
# In that case, a mount performed here may not be visible from the host namespace.
174+
# If it is still not mounted, retry in PID 1 mount namespace.
175+
# Example output:
176+
# [genesis-bootstrap] mount not visible after mount; retrying in PID 1 mount namespace
177+
if ! host_mountpoint "${MOUNTPOINT}"; then
178+
if command -v nsenter >/dev/null 2>&1; then
179+
log "mount not visible from host namespace; retrying in PID 1 mount namespace"
180+
nsenter -t 1 -m -- mount "${MOUNTPOINT}" || nsenter -t 1 -m -- mount "${PART_DEV}" "${MOUNTPOINT}" || true
181+
fi
182+
fi
183+
184+
if host_mountpoint "${MOUNTPOINT}"; then
185+
log "mounted successfully: ${MOUNTPOINT}"
186+
else
187+
log "mount failed or not visible: ${MOUNTPOINT}"
188+
fi
189+
else
190+
log "${MOUNTPOINT} is already mounted; skipping"
191+
fi
192+
else
193+
log "filesystem on ${PART_DEV} is '${FS_TYPE}', not ext4; skipping mount setup"
194+
fi
195+
else
196+
log "no partition found on ${DISK_DEV}; skipping"
197+
fi
198+
else
199+
log "no secondary disk detected; skipping data disk provisioning"
200+
fi
201+
202+
203+
# Execution can continue only if the secondary disk was detected and is mounted at /var/lib/genesis/data.
204+
if [[ -z "${SECOND_DISK}" ]]; then
205+
echo "[genesis-bootstrap] ERROR: secondary disk was not detected; refusing to continue" >&2
206+
exit 1
207+
fi
208+
209+
if ! host_mountpoint "/var/lib/genesis/data"; then
210+
echo "[genesis-bootstrap] ERROR: /var/lib/genesis/data is not mounted; refusing to continue" >&2
211+
exit 1
212+
fi
213+
214+
# PostgreSQL data relocation and genesis_core DB bootstrap.
215+
#
216+
# Goal:
217+
# - Keep PostgreSQL packages installation logic in install.sh
218+
# - Ensure PostgreSQL data files live under /var/lib/genesis/data so the disk can be moved
219+
220+
if host_mountpoint "/var/lib/genesis/data"; then
221+
if command -v psql >/dev/null 2>&1; then
222+
GC_PG_USER="${GC_PG_USER:-genesis_core}"
223+
GC_PG_PASS="${GC_PG_PASS:-genesis_core}"
224+
GC_PG_DB="${GC_PG_DB:-genesis_core}"
225+
226+
PG_VERSION_DIR=""
227+
if [[ -d /etc/postgresql ]]; then
228+
PG_VERSION_DIR="$(ls -1 /etc/postgresql 2>/dev/null | sort -V | tail -n 1 || true)"
229+
fi
230+
231+
if [[ -n "${PG_VERSION_DIR}" ]]; then
232+
PG_CONF_DIR="/etc/postgresql/${PG_VERSION_DIR}/main"
233+
PG_CONF_FILE="${PG_CONF_DIR}/postgresql.conf"
234+
OLD_PGDATA="/var/lib/postgresql/${PG_VERSION_DIR}/main"
235+
NEW_PGDATA="/var/lib/genesis/data/postgresql/${PG_VERSION_DIR}/main"
236+
237+
log "postgresql version detected: ${PG_VERSION_DIR}"
238+
log "postgresql old data dir: ${OLD_PGDATA}"
239+
log "postgresql new data dir: ${NEW_PGDATA}"
240+
241+
if [[ -f "${PG_CONF_FILE}" ]]; then
242+
if ! grep -qs "^[[:space:]]*data_directory[[:space:]]*=[[:space:]]*'${NEW_PGDATA}'" "${PG_CONF_FILE}"; then
243+
log "configuring PostgreSQL data_directory to ${NEW_PGDATA}"
244+
245+
if command -v systemctl >/dev/null 2>&1; then
246+
systemctl stop postgresql || true
247+
fi
248+
249+
mkdir -p "${NEW_PGDATA}"
250+
chown -R postgres:postgres "/var/lib/genesis/data/postgresql" || true
251+
252+
if [[ -d "${OLD_PGDATA}" && ! -f "${NEW_PGDATA}/PG_VERSION" ]]; then
253+
log "copying PostgreSQL data directory to ${NEW_PGDATA}"
254+
if command -v rsync >/dev/null 2>&1; then
255+
rsync -aHAX --numeric-ids "${OLD_PGDATA}/" "${NEW_PGDATA}/"
256+
else
257+
cp -a "${OLD_PGDATA}/." "${NEW_PGDATA}/"
258+
fi
259+
chown -R postgres:postgres "${NEW_PGDATA}" || true
260+
else
261+
log "PostgreSQL data directory already present under ${NEW_PGDATA}; skipping copy"
262+
fi
263+
264+
if grep -qs "^[[:space:]]*data_directory[[:space:]]*=" "${PG_CONF_FILE}"; then
265+
sed -i "s|^[[:space:]]*data_directory[[:space:]]*=.*|data_directory = '${NEW_PGDATA}'|" "${PG_CONF_FILE}"
266+
else
267+
printf '%s\n' "data_directory = '${NEW_PGDATA}'" >> "${PG_CONF_FILE}"
268+
fi
269+
270+
if command -v systemctl >/dev/null 2>&1; then
271+
systemctl daemon-reload || true
272+
systemctl start postgresql || true
273+
fi
274+
else
275+
log "PostgreSQL is already configured to use ${NEW_PGDATA}"
276+
fi
277+
278+
if command -v systemctl >/dev/null 2>&1; then
279+
systemctl start postgresql || true
280+
fi
281+
else
282+
log "PostgreSQL config not found at ${PG_CONF_FILE}; skipping PostgreSQL relocation"
283+
fi
284+
else
285+
log "PostgreSQL version directory not found under /etc/postgresql; skipping PostgreSQL relocation"
286+
fi
287+
else
288+
log "psql is not available; skipping PostgreSQL relocation"
289+
fi
290+
else
291+
log "/var/lib/genesis/data is not mounted; skipping PostgreSQL relocation"
292+
fi
293+
294+
# Additional PostgreSQL configuration
295+
sudo -u postgres psql -c "ALTER SYSTEM SET io_method = 'io_uring';"
296+
297+
# Apply migrations
298+
source "$VENV_PATH/bin/activate"
299+
# TODO(akremenetsky): Database configuration parameters should be taken
300+
# from persistent configuration file.
301+
ra-apply-migration --config-dir "$GC_CFG_DIR/" --path "$GC_PATH/migrations"
302+
303+
# Enable genesis core services
304+
sudo systemctl enable --now \
305+
gc-user-api \
306+
gc-orch-api \
307+
gc-status-api \
308+
gc-boot-api \
309+
gc-gservice \
310+
gc-core-agent \
311+
genesis-universal-agent \
312+
genesis-universal-scheduler
313+
23314
# Perform the bootstrap of GC
24315
gc-bootstrap
25316

0 commit comments

Comments
 (0)