Створюємо і оптимізуємо свій tile-сервер для OpenStreetMap на основі Ubuntu 14.04
- установка OpenStreetMap
- Налаштування PostgreSQL і Mapnik
- Налаштування Apache
- імпорт карти
- Перевірка працездатності карти
- Налаштування nginx
- Оптимізуємо OpenStreetMap (PostGIS)
Сьогодні я розповім як досить просто підняти і налаштувати свій власний сервер карт (тайловий сервер) на основі Ubuntu Server 14.04 LTS і OpenStreetMap.
Тож почнемо. з Вікіпедії :
OpenStreetMap (дослівно «відкрита карта вулиць»), скорочено OSM - некомерційний веб-картографічний проект по створенню силами спільноти учасників-користувачів Інтернету докладної вільної і безкоштовної географічної карти світу.
Є досить докладна офіційна стаття про встановлення та налаштування tile-сервера , Але є ще більш простий і швидкий спосіб.
установка OpenStreetMap
ставимо
sudo apt-get install python-software-propertiesдодаємо репозиторій
sudo add-apt-repository ppa: kakrueger / openstreetmapоновлюємося
sudo apt-get updateСтавимо єдиний пакет, що потягне за собою все необхідне для роботи повноцінного сервера карт.
sudo apt-get install libapache2-mod-tileПо ходу інсталяції (openstreetmap-postgis-db-setup) вам буде задано кілька запитань:
This will enable the apache config for mod_tile WARNING : This will disable the currently active default site config Do you want to enable mod_tile in the apache config ?
відповідаємо Yes
This script downloads the necessary coastline and boundary data . WARNING: The data is about 400 - 500Mb in size. Do you want to download coastlines?
відповідаємо Yes
Do you want these scripts to create and setup a new postgis database ready to be used with eg Osm2pgsql . WARNING: This will delete an existing db. Do you want to create a postgis db?
відповідаємо Yes
If you do not use the default name, you might need to adapt programs and scripts to use the new name . Name of the database to create:
вказуємо gis
Please specify which users should have access to the newly created db . You will want the user www-data for rendering and your own user name to import data into the db . The list of users is blank separated: Eg «www-data peter». Other users that should have access to the db :
Вказуємо www-data
Встановлено.
Налаштування PostgreSQL і Mapnik
Спочатку про саму Mapnik :
Mapnik - це програма, яку ми використовуємо для відтворення основного Slippy Map шару для OSM, разом з іншими верствами на кшталт «cycle map» і «noname». Також, це ім'я, дане основного шару, що вводить декого в оману.
Mapnik - вільний інструментарій відтворення карти. Він написаний на C ++ і Python. Використовує бібліотеку AGG і дає можливість згладжувати об'єкти на карті з великою точністю. Може читати дані в форматі компанії ESRI, PostGIS, точкові малюнки TIFF, файли .osm, а також підтримує будь-які GDAL або OGR формати. Пакети доступні для більшості випусків Linux, виконавчі файли доступні для Mac OS X і Windows.
Досить дивний нюанс в тому, що нам не запропонували поставити пароль для доступу користувача gis (створюється автоматично) до однойменної БД.
Встановлюємо пароль для користувача gis
su postgres -c "psql -d gis" gis = # \ password gis Тепер необхідно вказати користувача і пароль у файлі конфіга mapnik.
Зробимо це простим способом - заміною за допомогою утиліти rpl:
Де «1234567890» - це пароль, що ми вказали для користувача gis
Даємо можливість підключатися з локального сервера, використовуючи конфиг /etc/postgresql/9.3/main/pg_hba.conf
Додаємо в нього рядок
Налаштовуємо PostgreSQL (щодо характеристик сервера: 16 ядер, 16GB RAM, БД на HDD).
/etc/postgresql/9.3/main/postgresql.confПоказую тільки те, що міняв:
listen_addresses = '*' max_connections = 200 shared_buffers = 12GB work_mem = 2GB maintenance_work_mem = 8GB synchronous_commit = off # тільки для імпорту, потім необхідно прибрати! fsync = off # тільки для імпорту, потім необхідно прибрати! wal_buffers = 16MB checkpoint_segments = 1024 checkpoint_completion_target = 0.9 random_page_cost = 2.0 cpu_tuple_cost = 0.001 cpu_index_tuple_cost = 0.0005 effective_cache_size = 14GB autovacuum = off # тільки для імпорту, потім необхідно прибрати!І перезапускаємо Постгрес для застосування всіх налаштувань
service postgresql restartПотюнім трохи конфиг демона renderd рендерера тайлів (Mapnik rendering daemon) і збільшимо кількість потоків renderd до кількості ядер (в моєму випадки - 16)
vi /etc/renderd.conf [renderd] num_threads = 16Тепер необхідно перезапустити демон renderd рендерер тайлів (Mapnik rendering daemon)
service renderd restartНалаштування Apache
Сервер карт буде працювати на зв'язці Apache + PostgreSQL.
Я не в захваті від цієї зв'язки. Після основних дій ми ще доставимо nginx для швидшої роботи в зв'язці nginx + Apache + PostgreSQL.
Налаштовуємо Apache.
Назви сервера для прикладу буде osm.net.
Зверніть увагу що порт я вказав 8080.
Правимо порт в /etc/apache2/ports.conf на 8080
Listen 8080Дописуємо в /etc/apache2/apache2.conf назва нашого сервера, тому що Апач без цього працювати не може.
echo "ServerName osm.net" >> /etc/apache2/apache2.confПерезапускаємо.
service apache2 restartімпорт карти
Беремо з проекту Geofabrik , Для прикладу, карту України в форматі .pbf і вливаємо його в нашу БД.
mkdir / var / osm wget -O /var/osm/ukraine.`date +% Y-% m-% d`.osm.pbf \ https://download.geofabrik.de/europe/ukraine-latest.osm. pbf osm2pgsql -d gis -U gis -W -c --slim -C 4000 --number-processes 16 \ /var/osm/ukraine.`date +% Y-% m-% d`.osm.pbf На віртуальній машині з 16 ядрами, 16GB ОЗУ і HDD RAID-10 масивом це займає близько 30 хвилин.
На швидкість імпорту досить сильно впливає диск. На SSD вливається помітно швидше.
Зверніть увагу на параметр -c (--create) при імпорті через osm2pgsql. З цим параметром віддалиться вся існуюча інформація з таблиць. Якщо вам необхідно зробити імпорт ще кількох країн, то замість -c вказуйте параметр -a (--append).
Після того як зробите імпорт всіх необхідних країн (або всього світу) не забудьте повернути в default з конфіга PostgreSQL /etc/postgresql/9.3/main/postgresql.conf параметри synchronous_commit, fsync і autovacuum.
Перевірка працездатності карти
Поточна комплектація поставляється прикладом /var/www/osm/slippymap.html
Я пропоную перевірити на даному етапі, чи правильно встановлена PostgreSQL + Apache (mod_tile) + Mapnik, перед тим, як ми перейдемо до налаштування nginx.
Відкриваємо в браузері https://osm.net:8080/osm/slippymap.html
Повинен відобразиться простий інтерфейс і карта. Важливо щоб був обраний пункт в інтерфейсі «Local Tiles».
Якщо позуміть і поскролліть, то карта буде перемальовуватись, поступово довантажуючи різні тайли.
Ось наприклад, адреса тайла центру міста Києва буде вигдядеть так: https://osm.net:8080/osm/11/1197/690.png
І якщо ви побачили картинку - значить все зробили правильно.
Кеш відрендерених тайлів буде складатися в папку / var / lib / mod_tile / default /.
І тут як-раз настав час віддавати цей кеш НЕ через повільного монстра Apache, а через швидкий nginx.
Налаштування nginx
ставимо
apt-get install nginx-light cat /etc/nginx/nginx.conf user www-data www-data; worker_processes 32; worker_priority -20; pid /var/run/nginx.pid; error_log /var/log/nginx/error.log warn; events {worker_connections 1024; accept_mutex on; multi_accept on; } Http {include /etc/nginx/mime.types; default_type application / octet-stream; reset_timedout_connection on; server_tokens off; log_format main '$ remote_addr - [$ time_local]' '$ host {$ upstream_cache_status} "$ request" $ status $ bytes_sent' ' "$ http_referer" "$ http_user_agent"' ' "$ gzip_ratio" $ upstream_response_time'; log_format ip_only '$ remote_addr'; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; client_header_buffer_size 16m; client_max_body_size 512m; server_names_hash_max_size 4096; server_names_hash_bucket_size 512; variables_hash_max_size 4096; variables_hash_bucket_size 512; types_hash_max_size 8192; port_in_redirect off; gzip on; gzip_vary on; gzip_min_length 1024; gzip_buffers 16 8k; gzip_comp_level 5; gzip_http_version 1.0; gzip_proxied any; gzip_disable "msie6"; gzip_types text / plain text / css application / x-javascript text / xml application / xml application / xml + rss text / javascript text / json; fastcgi_temp_path / var / cache / nginx / temp / fastcgi; proxy_temp_path / var / cache / nginx / temp / proxy; charset utf-8; index index.html index.htm; access_log off; error_log / dev / null; include /etc/nginx/conf.d/upstream.conf; include / etc / nginx / sites-enabled / *; } Cat /etc/nginx/conf.d/upstream.conf upstream apache {server 127.0.0.1:8080; } Cat / etc / nginx / sites-enabled / 001-osm server {listen 80; server_name osm.net; access_log /var/log/nginx/osm.net.access.log main; error_log /var/log/nginx/osm.net.error.log; root / var / www; index index.html; charset utf-8; # OSM location ~ * ^ / osm / ([0-9] +) / ([0-9] +) / ([0-9] +) \. Png $ {proxy_pass https: // apache; include / etc / nginx / proxy_params; access_log off; autoindex off; expires 1d; add_header Cache-Control 'public'; } # Static resources location ~ * ^. + \. (Ico | htm | html | txt | jpg | jpeg | css | js | png | gif | map | woff | woff2 | ttf | tif | tiff | pdf) $ {access_log off; autoindex off; expires 1d; add_header Cache-Control 'public'; }}перезапускаємо
service nginx restartОптимізуємо OpenStreetMap (PostGIS)
(на основі статті PostGIS Tuning )
CREATE INDEX idx_poly_idlanduse ON planet_osm_polygon USING gist (way) WHERE ((landuse IS NOT NULL) OR (leisure IS NOT NULL) OR (aeroway = ANY ( '{apron, aerodrome}' :: text [])) OR (amenity = ANY ( '{parking, university, college, school, hospital, kindergarten, grave_yard}' :: text [])) OR (military = ANY ( '{barracks, danger_area}' :: text [])) OR ( "natural" = ANY ( '{field, beach, desert, heath, mud, grassland, wood, sand, scrub}' :: text [])) OR (power = ANY ( '{station, sub_station, generator}' :: text [ ])) OR (tourism = ANY ( '{attraction, camp_site, caravan_site, picnic_site, zoo}' :: text [])) OR (highway = ANY ( '{services, rest_area}' :: text []))) ; CREATE INDEX "planet_osm_polygon_nobuilding_index" ON "planet_osm_polygon" USING gist ( "way") WHERE "building" IS NULL; CREATE INDEX ferry_idx ON planet_osm_line USING gist (way) WHERE (route = 'ferry' :: text); CREATE INDEX "idx_poly_aeroway" on planet_osm_polygon USING gist (way) WHERE "aeroway" IS NOT NULL; CREATE INDEX "idx_poly_aeroway" on planet_osm_polygon USING gist (way) WHERE "aeroway" IS NOT NULL; CREATE INDEX "idx_poly_historic" on planet_osm_polygon USING gist (way) WHERE "historic" IS NOT NULL; CREATE INDEX "idx_poly_leisure" on planet_osm_polygon USING gist (way) WHERE "leisure" IS NOT NULL; CREATE INDEX "idx_poly_man_made" on planet_osm_polygon USING gist (way) WHERE "man_made" IS NOT NULL; CREATE INDEX "idx_poly_military" on planet_osm_polygon USING gist (way) WHERE "military" IS NOT NULL; CREATE INDEX "idx_poly_power" on planet_osm_polygon USING gist (way) WHERE "power" IS NOT NULL; CREATE INDEX "idx_poly_landuse" on planet_osm_polygon USING gist (way) WHERE "landuse" IS NOT NULL; CREATE INDEX "idx_poly_amenity" on planet_osm_polygon USING gist (way) WHERE "amenity" IS NOT NULL; CREATE INDEX "idx_poly_natural" on planet_osm_polygon USING gist (way) WHERE "natural" IS NOT NULL; CREATE INDEX "idx_poly_highway" on planet_osm_polygon USING gist (way) WHERE "highway" IS NOT NULL; CREATE INDEX "idx_poly_tourism" on planet_osm_polygon USING gist (way) WHERE "tourism" IS NOT NULL; CREATE INDEX "idx_poly_building" on planet_osm_polygon USING gist (way) WHERE "building" IS NOT NULL; CREATE INDEX "idx_poly_barrier" on planet_osm_polygon USING gist (way) WHERE "barrier" IS NOT NULL; CREATE INDEX "idx_poly_railway" on planet_osm_polygon USING gist (way) WHERE "railway" IS NOT NULL; CREATE INDEX "idx_poly_aerialway" on planet_osm_polygon USING gist (way) WHERE "aerialway" IS NOT NULL; CREATE INDEX "idx_poly_power_source" on planet_osm_polygon USING gist (way) WHERE "power_source" IS NOT NULL; CREATE INDEX "idx_poly_generator: source" on planet_osm_polygon USING gist (way) WHERE "generator: source" IS NOT NULL; CREATE INDEX "idx_line_aerialway" on planet_osm_line USING gist (way) WHERE "aerialway" IS NOT NULL; CREATE INDEX "idx_line_waterway" on planet_osm_line USING gist (way) WHERE "waterway" IS NOT NULL; CREATE INDEX "idx_line_bridge" on planet_osm_line USING gist (way) WHERE "bridge" IS NOT NULL; CREATE INDEX "idx_line_tunnel" on planet_osm_line USING gist (way) WHERE "tunnel" IS NOT NULL; CREATE INDEX "idx_line_access" on planet_osm_line USING gist (way) WHERE "access" IS NOT NULL; CREATE INDEX "idx_line_railway" on planet_osm_line USING gist (way) WHERE "railway" IS NOT NULL; CREATE INDEX "idx_line_power" on planet_osm_line USING gist (way) WHERE "power" IS NOT NULL; CREATE INDEX "idx_line_name" on planet_osm_line USING gist (way) WHERE "name" IS NOT NULL; CREATE INDEX "idx_line_ref" on planet_osm_line USING gist (way) WHERE "ref" IS NOT NULL; CREATE INDEX "idx_point_aerialway" on planet_osm_point USING gist (way) WHERE "aerialway" IS NOT NULL; CREATE INDEX "idx_point_power_source" on planet_osm_point USING gist (way) WHERE "power_source" IS NOT NULL; CREATE INDEX "idx_point_shop" on planet_osm_point USING gist (way) WHERE "shop" IS NOT NULL; CREATE INDEX "idx_point_place" on planet_osm_point USING gist (way) WHERE "place" IS NOT NULL; CREATE INDEX "idx_point_barrier" on planet_osm_point USING gist (way) WHERE "barrier" IS NOT NULL; CREATE INDEX "idx_point_railway" on planet_osm_point USING gist (way) WHERE "railway" IS NOT NULL; CREATE INDEX "idx_point_amenity" on planet_osm_point USING gist (way) WHERE "amenity" IS NOT NULL; CREATE INDEX "idx_point_natural" on planet_osm_point USING gist (way) WHERE "natural" IS NOT NULL; CREATE INDEX "idx_point_highway" on planet_osm_point USING gist (way) WHERE "highway" IS NOT NULL; CREATE INDEX "idx_point_tourism" on planet_osm_point USING gist (way) WHERE "tourism" IS NOT NULL; CREATE INDEX "idx_point_power" on planet_osm_point USING gist (way) WHERE "power" IS NOT NULL; CREATE INDEX "idx_point_aeroway" on planet_osm_point USING gist (way) WHERE "aeroway" IS NOT NULL; CREATE INDEX "idx_point_historic" on planet_osm_point USING gist (way) WHERE "historic" IS NOT NULL; CREATE INDEX "idx_point_leisure" on planet_osm_point USING gist (way) WHERE "leisure" IS NOT NULL; CREATE INDEX "idx_point_man_made" on planet_osm_point USING gist (way) WHERE "man_made" IS NOT NULL; CREATE INDEX "idx_point_waterway" on planet_osm_point USING gist (way) WHERE "waterway" IS NOT NULL; CREATE INDEX "idx_point_generator: source" on planet_osm_point USING gist (way) WHERE "generator: source" IS NOT NULL; CREATE INDEX "idx_point_capital" on planet_osm_point USING gist (way) WHERE "capital" IS NOT NULL; CREATE INDEX "idx_point_lock" on planet_osm_point USING gist (way) WHERE "lock" IS NOT NULL; CREATE INDEX "idx_point_landuse" on planet_osm_point USING gist (way) WHERE "landuse" IS NOT NULL; CREATE INDEX "idx_point_military" on planet_osm_point USING gist (way) WHERE "military" IS NOT NULL; CREATE INDEX idx_poly_wayarea_text ON planet_osm_polygon USING gist (way) WHERE name IS NOT NULL AND place IS NULL AND way_areaЗбережемо ці запити в файл /var/osm/osm.indexes.sql і виконаємо їх su postgres -з "psql -d gis -U gis -W" gis => \ i /var/osm/osm.indexes.sql
Розігрів кеша Mapnik
Для часто використовуваних тайлів є сенс "розігріти" кеш Mapnik.
Для цього є дуже проста команда: cd / var / lib / mod_tile render_list -m default -a -z 0 -Z 10 -n 16
де
-z 0 - початковий зум (від)
-Z 10 - кінцевий зум (до)
-n 16 - кількість ядер
Все той же приклад з картою України для 16 ядер і з зумом від 0 до 10 займає близько 20 хвилин.
Це все.
Хай щастить.
Читайте також:
Do you want to download coastlines?Do you want to create a postgis db?