Ketika Anda menggunakan Docker, file-file yang ada di dalam
image Docker tidak dapat diubah secara langsung setelah image dibuat. Untuk memodifikasi leaflet_options.js, Anda harus membuat image Docker kustom yang menggantikan file asli dengan versi yang sudah Anda ubah. Anda dapat melakukan ini dengan bantuan Dockerfile dan mengelola semuanya dengan docker-compose.yml.
Berikut adalah panduan langkah demi langkah untuk menerapkan perubahan ini menggunakan Docker Compose.
Langkah 1: Siapkan semua file
Pertama, Anda perlu mengumpulkan semua file yang diperlukan di satu direktori proyek Anda.
Struktur direktori:
my-osrm-project/
├── docker-compose.yml
├── leaflet_options.js
├── Dockerfile.frontend
└── (direktori data, misalnya ./data untuk .osrm files)
Penjelasan:
docker-compose.yml: File utama untuk mendefinisikan dan menjalankan layanan multi-kontainer Anda.
leaflet_options.js: File ini adalah versi kustom dari src/leaflet_options.js dari repositori osrm-frontend. Di sinilah Anda akan mendefinisikan profil rute yang berbeda.
Dockerfile.frontend: File ini digunakan untuk membuat image Docker kustom untuk frontend Anda.
data: Direktori lokal tempat Anda menyimpan semua file .osm.pbf dan .osrm yang sudah diproses.
Langkah 2: Buat file leaflet_options.js kustom
Buat file baru bernama leaflet_options.js di direktori proyek Anda, dan isi dengan konfigurasi untuk setiap profil yang Anda inginkan.
Isi file leaflet_options.js:
javascript
// leaflet_options.js
module.exports = {
services: [
{
label: 'Mobil (Tercepat)',
path: 'http://backend-car:5000/route/v1' // Ganti dengan nama service backend mobil
},
{
label: 'Sepeda (Tercepat)',
path: 'http://backend-bike:5000/route/v1' // Ganti dengan nama service backend sepeda
},
{
label: 'Jalan Kaki (Tercepat)',
path: 'http://backend-foot:5000/route/v1' // Ganti dengan nama service backend jalan kaki
}
],
center: [ -6.2, 106.8 ], // Koordinat pusat peta (contoh untuk Jakarta)
zoom: 13,
// Tambahkan opsi konfigurasi lain di sini jika diperlukan
};
Gunakan kode dengan hati-hati.
Penting: Perhatikan bahwa path mengarah ke nama layanan Docker (misalnya, backend-car) yang akan didefinisikan di docker-compose.yml, bukan localhost.
Langkah 3: Buat Dockerfile kustom untuk frontend
Buat file baru bernama Dockerfile.frontend untuk membuat image Docker frontend kustom Anda.
Isi file Dockerfile.frontend:
dockerfile
# Gunakan image osrm-frontend resmi sebagai basis
FROM osrm/osrm-frontend:latest
# Salin file leaflet_options.js kustom Anda ke dalam direktori src
COPY leaflet_options.js src/leaflet_options.js
# Kompilasi ulang aset frontend dengan konfigurasi baru
RUN npm run compile
Gunakan kode dengan hati-hati.
COPY akan menimpa file asli di dalam image dengan versi kustom Anda. Perintah RUN npm run compile memastikan bahwa semua aset JavaScript diperbarui dengan perubahan konfigurasi Anda.
Langkah 4: Tulis file docker-compose.yml
Buat docker-compose.yml yang akan menyatukan semua layanan (backend dan frontend) yang berbeda.
build: Menunjuk ke direktori saat ini (.) dan Dockerfile.frontend untuk membuat image kustom.
volumes: Pastikan semua backend mengacu pada direktori data yang sama.
ports: Backend berjalan pada port 5000 di dalam setiap kontainer, tetapi dipetakan ke port yang berbeda di host untuk menghindari konflik. Frontend tidak perlu terhubung ke port host ini karena ia berkomunikasi dengan nama layanan backend (misalnya backend-car) di jaringan Docker.
depends_on: Memastikan bahwa semua backend sudah berjalan sebelum frontend mencoba untuk memulai.
Langkah 5: Jalankan Docker Compose
Dari direktori proyek Anda, jalankan perintah berikut di terminal:
bash
# Lakukan proses data terlebih dahulu (extract, partition, customize)
# Perintah ini akan memakan waktu.
docker compose run --rm backend-car osrm-extract -p /opt/car.lua /data/indonesia-latest.osm.pbf
docker compose run --rm backend-car osrm-partition /data/indonesia-latest.osrm
docker compose run --rm backend-car osrm-customize /data/indonesia-latest.osrm
# Lakukan hal yang sama untuk profil sepeda dan jalan kaki
docker compose run --rm backend-bike osrm-extract -p /opt/bicycle.lua /data/indonesia-latest.osm.pbf
# ... dan seterusnya ...
# Jalankan semua layanan
docker compose up --build -d
Gunakan kode dengan hati-hati.
Setelah semua kontainer berjalan, Anda bisa mengakses frontend Anda di http://localhost:9966. Anda akan melihat dropdown dengan profil rute yang telah Anda definisikan.
-prereqs_check() { info "Running prerequisite checks..."; local ok=0; if ! command_exists docker; then err "Docker is not installed."; ok=1; fi; if ! docker compose version >/dev/null 2>&1; then err "Docker Compose V2 plugin is not installed."; ok=1; fi; if ! command_exists curl; then err "curl is not installed."; ok=1; fi; if [ ${ok} -ne 0 ]; then err "Prerequisite checks failed."; exit 1; fi; success "All prerequisites are met."; }
-SELINUX_LABEL=""; setup_selinux_flag() { if command_exists sestatus && sestatus 2>/dev/null | grep -qi "SELinux status:.*enabled"; then SELINUX_LABEL=",z"; info "SELinux is enabled. Appending ',z' to volume mounts."; fi; }
-ask_confirm() { local prompt="$1"; if [ "${yes_all}" = true ]; then info "[auto-yes] ${prompt} -> yes"; return 0; fi; while true; do printf "%s [y/N]: " "${prompt}"; read -r yn; case "${yn}" in [Yy]*) return 0 ;; [Nn]*|"") return 1 ;; *) echo "Please answer y or n." ;; esac; done; }
+prereqs_check() {
+ info "Running prerequisite checks..."
+ local ok=0
+ if ! command_exists docker; then err "Docker is not installed."; ok=1; fi
+ if ! docker compose version >/dev/null 2>&1; then err "Docker Compose V2 plugin is not installed."; ok=1; fi
+ if ! command_exists curl; then err "curl is not installed."; ok=1; fi
+ if [ ${ok} -ne 0 ]; then err "Prerequisite checks failed."; exit 1; fi
+ success "All prerequisites are met."
+}
+SELINUX_LABEL="";
+setup_selinux_flag() {
+ if command_exists sestatus && sestatus 2>/dev/null | grep -qi "SELinux status:.*enabled"; then
+ SELINUX_LABEL=",z"
+ info "SELinux is enabled. Appending ',z' to volume mounts."
+ fi
+}
+ask_confirm() {
+ local prompt="$1"
+ if [ "${yes_all}" = true ]; then info "[auto-yes] ${prompt} -> yes"; return 0; fi
+ while true; do
+ printf "%s [y/N]: " "${prompt}"
+ read -r yn
+ case "${yn}" in
+ [Yy]*) return 0 ;;
+ [Nn]*|"") return 1 ;;
+ *) echo "Please answer y or n." ;;
+ esac
+ done
+}
# -------------------------
# Core Logic
# -------------------------
-download_map() { info "Preparing map from source: ${map_source}"; local map_instance_dir="${work_dir}/${map_name}/dataset"; mkdir -p "${map_instance_dir}"; MAP_PATH="${map_instance_dir}/$(basename "${map_source}")"; if [[ -f "${MAP_PATH}" ]]; then info "Map file already exists. Skipping download."; return 0; fi; if [[ "${map_source}" =~ ^https?:// ]]; then if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would download map to ${MAP_PATH}"; return 0; fi; info "Downloading map with curl..."; if ! curl -L --fail -o "${MAP_PATH}.tmp" "${map_source}"; then rm -f "${MAP_PATH}.tmp"; err "Download failed."; exit 1; fi; mv "${MAP_PATH}.tmp" "${MAP_PATH}"; success "Map downloaded successfully."; elif [[ -f "${map_source}" ]]; then if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would copy local map to ${MAP_PATH}"; return 0; fi; info "Copying local map file..."; cp -v "${map_source}" "${MAP_PATH}"; success "Local map copied."; else err "Map source '${map_source}' is not a valid URL or an existing file."; exit 1; fi; }
-generate_profiles() { info "Generating profile files for map instance '${map_name}'"; local profile_dir="${work_dir}/${map_name}/profiles"; local patched_dir="${work_dir}/${map_name}/patched-profiles"; mkdir -p "${profile_dir}" "${patched_dir}"; for profile in "${PROFILE_ARR[@]}"; do local pf="${profile_dir}/${profile}.lua"; if [ ! -f "${pf}" ]; then case "${profile}" in car|bicycle|bike|foot) info "Generating wrapper for built-in profile: ${profile}"; cat > "${pf}" <<LUA
+download_map() {
+ info "Preparing map from source: ${map_source}"
+ local map_instance_dir="${work_dir}/${map_name}/dataset"
- local frontend_block=""; if [ "${no_frontend}" != true ]; then
+ local frontend_block=""
+ if [ "${no_frontend}" != true ]; then
if [ -z "${mapbox_token}" ]; then
- warn "Mapbox token not provided. The frontend map may not render. Use --mapbox-token <your_token>."
+ warn "Mapbox token not provided. The frontend map may not render."
fi
- local backend_urls=""; local idx2=0
- for profile in "${PROFILE_ARR[@]}"; do idx2=$((idx2+1)); local host_port=$((backend_port + idx2 - 1)); local url="http://${backend_domain}:${host_port}${backend_path}/${api_version}/${profile}"; backend_urls+="${url},"; done
+ local backend_urls=""
+ local idx2=0
+ for profile in "${PROFILE_ARR[@]}"; do
+ idx2=$((idx2 + 1))
+ local host_port=$((backend_port + idx2 - 1))
+ local url="http://${backend_domain}:${host_port}${backend_path}/${api_version}/${profile}"
+ backend_urls+="${url},"
+ done
backend_urls=${backend_urls%,}
frontend_block+="
osrm-frontend-${map_name}:
@@ -224,15 +322,29 @@ EOF
# -------------------------
run_install() {
info "Starting OSRM installation for map instance: '${map_name}'"
- if [ -f "${COMPOSE_FILE}" ] && ! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate containers. Continue?"; then info "Installation aborted."; exit 0; fi
- if [ "${dry_run}" = true ]; then warn "[DRY RUN] Skipping network and container startup."; return; fi
- info "Ensuring Docker network '${network_name}' exists..."; docker network create "${network_name}" >/dev/null 2>&1 || info "Network already exists."
- info "Pulling latest Docker images..."; docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"; if [[ "${no_frontend}" != "true" ]]; then docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"; fi
+ if [ -f "${COMPOSE_FILE}" ] && ! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate containers. Continue?"; then
+ info "Installation aborted."
+ exit 0
+ fi
+ download_map
+ generate_profiles
+ generate_docker_compose
+ if [ "${dry_run}" = true ]; then
+ warn "[DRY RUN] Skipping network and container startup."
+ return
+ fi
+ info "Ensuring Docker network '${network_name}' exists..."
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
- if [ ! -f "${pdata_dir}/map.osrm" ]; then
+ # FIX (v2.7): Check for the .properties file, which is a reliable indicator of completion for MLD.
+ if [ ! -f "${pdata_dir}/map.osrm.properties" ]; then
needs_preprocessing=true
break
fi
@@ -241,25 +353,33 @@ run_install() {
info "Starting data preprocessing for missing profiles..."
for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
- if [ -f "${pdata_dir}/map.osrm" ]; then
+ # FIX (v2.7): Same check here.
+ if [ -f "${pdata_dir}/map.osrm.properties" ]; then
info "--- Skipping profile '${profile}', data already exists. ---"
continue
fi
- info "Preparing map file for profile '${profile}'..."; cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
+ info "Preparing map file for profile '${profile}'..."
+ cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
local service_name="osrm-preprocess-${map_name}-${profile}"
info "--- Processing profile: ${profile} ---"
if docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}"; then
success "Preprocessing for '${profile}' completed."
else
- err "Preprocessing for '${profile}' FAILED. Check logs for details."; if ! ask_confirm "Continue with other profiles?"; then err "Aborting installation."; exit 1; fi
+ err "Preprocessing for '${profile}' FAILED. Check logs for details."
+ if ! ask_confirm "Continue with other profiles?"; then
prereqs_check(){ info "Running prerequisite checks...";local ok=0;if! command_exists docker;then err "Docker is not installed.";ok=1;fi;if! docker compose version >/dev/null 2>&1;then err "Docker Compose V2 plugin is not installed.";ok=1;fi;if! command_exists curl;then err "curl is not installed.";ok=1;fi;if[${ok}-ne 0 ];then err "Prerequisite checks failed.";exit 1;fi; success "All prerequisites are met.";}
info "Running prerequisite checks..."
SELINUX_LABEL=""; setup_selinux_flag(){if command_exists sestatus && sestatus 2>/dev/null | grep-qi"SELinux status:.*enabled";then SELINUX_LABEL=",z"; info "SELinux is enabled. Appending ',z' to volume mounts.";fi;}
local ok=0
ask_confirm(){local prompt="$1";if["${yes_all}"=true];then info "[auto-yes] ${prompt} -> yes";return 0;fi;while true;do printf"%s [y/N]: ""${prompt}";read-r yn;case"${yn}"in[Yy]*)return 0 ;;[Nn]*|"")return 1 ;;*)echo"Please answer y or n.";;esac;done;}
if! command_exists docker;then err "Docker is not installed.";ok=1;fi
if! docker compose version >/dev/null 2>&1;then err "Docker Compose V2 plugin is not installed.";ok=1;fi
if! command_exists curl;then err "curl is not installed.";ok=1;fi
info "Starting OSRM installation for map instance: '${map_name}'"
info "Starting OSRM installation for map instance: '${map_name}'"
if[-f"${COMPOSE_FILE}"]&&! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate containers. Continue?";then
if[-f"${COMPOSE_FILE}"]&&! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate/rebuild containers. Continue?";then info "Installation aborted.";exit 0;fi
info "Installation aborted."
download_map; generate_profiles
exit 0
fi
download_map
generate_profiles
generate_docker_compose
if["${dry_run}"=true];then
warn "[DRY RUN] Skipping network and container startup."
return
fi
info "Ensuring Docker network '${network_name}' exists..."
docker network create "${network_name}">/dev/null 2>&1 || info "Network already exists."