8.2. Установка CKAN в CentOS 7

За основу взята инструкция по установке из официальной документации.

8.2.1. Установка системных пакетов

Устанавливаем необходимые пакеты:

sudo yum install epel-release
sudo yum install python-pip python-virtualenv python-devel
sudo yum install git gcc-c++ java-1.8.0-openjdk unzip lsof libxml2-devel libxslt-devel mailcap
sudo yum install postgresql postgresql-server postgresql-libs postgresql-contrib postgresql-devel

Предупреждение

Если на сервере, куда устанавливается CKAN, уже установлен NextGIS Web, то пакеты для PostgreSQL ставить не нужно. В этом случае PostgreSQL уже должен быть установлен и настроен.

8.2.2. Установка CKAN

Создаём пользователя CKAN:

sudo useradd -m -s /sbin/nologin -d /usr/lib/ckan -c "CKAN User" ckan

Домашняя папка пользователя CKAN остальным пользователям системы доступна только на чтение:

sudo chmod 755 /usr/lib/ckan

Переключаемся на только что созданного пользователя:

sudo su -s /bin/bash - ckan

Устанавливаем виртуальное окружение default:

virtualenv --no-site-packages default

И активируем его:

. default/bin/activate

Скачиваем и устанавливаем последнюю версию CKAN. На момент написания данной инструкции это версия CKAN 2.5.2. Если вы устанавливаете другую версию, то измените URL в следующей команде:

pip install -e git+https://github.com/okfn/ckan.git@ckan-2.5.2#egg=ckan

Устанавливаем пакеты Python, от которых зависит CKAN:

pip install -r default/src/ckan/requirements.txt

8.2.3. Настройка PostgreSQL

Инициализируем базу данных и включаем автоматический запуск PostgreSQL при старте системы:

sudo postgresql-setup initdb
systemctl start postgresql
systemctl enable postgresql

Предупреждение

В ходе работы с CKAN размер базы будет увеличиваться, поэтому в случае необходимости кластер базы данных должен быть вынесен в папку с достаточным объёмом дискового пространства.

Создаём пользователя базы данных ckan_default и саму базу также названную ckan_default:

sudo -u postgres createuser -S -D -R -P ckan_default
sudo -u postgres createdb -O ckan_default ckan_default -E utf-8

Отредактируем параметры аутентификации в соответствующем файле:

sudo nano /var/lib/pgsql/data/pg_hba.conf

Отредактируем его таким образом, чтобы в нём присутствовали следующие строки (исправим метод аутентификации на md5, если указан иной):

# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5

Предупреждение

Предполагается, что CKAN и PostgreSQL установлены на одном хосте. Если это не так, то потребуется дополнительная настройка PostgreSQL.

Не забудьте перезапустить PostgreSQL:

systemctl restart postgresql

Предупреждение

Если вы устанавливали PostgreSQL из стороннего репозитория, например отсюда, то может потребоваться дополнительная настройка переменной PATH, либо придётся писать полный путь до команд, например, /usr/pgsql-9.5/bin/psql.

8.2.4. Создание конфигурационного файла CKAN

sudo mkdir -p /etc/ckan/default
sudo chown -R ckan /etc/ckan/

Переключаемся на пользователя CKAN и создаём конфигурационный файл:

sudo su -s /bin/bash - ckan
. default/bin/activate
cd /usr/lib/ckan/default/src/ckan
paster make-config ckan /etc/ckan/default/development.ini

Отредактируйте файл development.ini, указав пароль пользователя ckan_default и URL сайта, по которому будет доступен CKAN:

sqlalchemy.url = postgresql://ckan_default:pass@localhost/ckan_default
ckan.site_url = http://82.162.194.216
ckan.root_path = /ckan/{{LANG}}

8.2.5. Установка Solr5

Скачиваем Solr. На текущий момент последняя версия - 5.5.0:

wget http://apache-mirror.rbc.ru/pub/apache/lucene/solr/5.5.0/solr-5.5.0.zip
unzip solr-5.5.0.zip
cd solr-5.5.0/bin

Устанавливаем Solr в /opt/solr5:

unzip solr-5.5.0.zip
cd solr-5.5.0/bin
sudo mkdir /opt/solr5
sudo ./install_solr_service.sh ../../solr-5.5.0.zip -i /opt/solr5

После установки сервиса он будет запускаться автоматически при старте системы. Скрипт запуска находится в файле /etc/init.d/solr.

Создадим отдельное ядро Solr для CKAN:

sudo su solr
cd /opt/solr5/solr/bin
./solr create -c ckan

Настроим схему:

cd /var/solr/data/ckan/conf
ln -s /usr/lib/ckan/default/src/ckan/ckan/config/solr/schema.xml .

Удалим файл managed-schema:

rm managed-schema

Откройте файл solrconfig.xml. Найдите элемент <schemaFactory class="ManagedIndexSchemaFactory"> и закомментируйте его:

<!--
<schemaFactory class="ManagedIndexSchemaFactory">
  <bool name="mutable">true</bool>
  <str name="managedSchemaResourceName">managed-schema</str>
</schemaFactory>
-->

Добавьте элемент:

<schemaFactory class="ClassicIndexSchemaFactory"/>

Найдите элемент <initParams>, ссылающийся на add-unknown-fields-to-the-schema и закомментируйте его:

<!--
<initParams path="/update/**">
  <lst name="defaults">
    <str name="update.chain">add-unknown-fields-to-the-schema</str>
  </lst>
</initParams>
-->

Также закомментируйте этот элемент:

<!--
<processor class="solr.AddSchemaFieldsUpdateProcessorFactory">
  <str name="defaultFieldType">strings</str>
  <lst name="typeMapping">
    <str name="valueClass">java.lang.Boolean</str>
    <str name="fieldType">booleans</str>
  </lst>
  <lst name="typeMapping">
    <str name="valueClass">java.util.Date</str>
    <str name="fieldType">tdates</str>
  </lst>
  <lst name="typeMapping">
    <str name="valueClass">java.lang.Long</str>
    <str name="valueClass">java.lang.Integer</str>
    <str name="fieldType">tlongs</str>
  </lst>
  <lst name="typeMapping">
    <str name="valueClass">java.lang.Number</str>
    <str name="fieldType">tdoubles</str>
  </lst>
</processor>
-->

Перезапускаем Solr:

sudo service solr restart

Отредактируем файл /etc/ckan/default/development.ini, раскомментировав соответствующую строку и указав URL Solr:

solr_url = http://127.0.0.1:8983/solr/ckan

8.2.6. Инициализация базы данных

sudo su -s /bin/bash - ckan
. default/bin/activate
cd default/src/ckan
paster db init -c /etc/ckan/default/development.ini

8.2.7. Установка DataStore

Откроём файл development.ini и в список плагинов добавим datastore:

ckan.plugins = stats text_view image_view recline_view datastore

В базе данных создадим пользователя datastore_default, который не будет имет прав на запись, только на чтение:

sudo -u postgres createuser -S -D -R -P -l datastore_default

Создадим базу данных datastore_default, владельцем которой будет пользователь ckan_default:

sudo -u postgres createdb -O ckan_default datastore_default -E utf-8

Откроем файл development.ini, расскомментируем и отредактируем следующие строки, указав соответствующие пароли:

ckan.datastore.write_url = postgresql://ckan_default:pass@localhost/datastore_default
ckan.datastore.read_url = postgresql://datastore_default:pass@localhost/datastore_default

Выставим права в базе данных:

cd /usr/lib/ckan
. default/bin/activate
cd default/src/ckan
paster --plugin=ckan datastore set-permissions -c /etc/ckan/default/development.ini | sudo -u postgres psql --set ON_ERROR_STOP=1

8.2.8. Установка DataPusher

За основу взята инструкция по установке из официальной документации.

sudo su -s /bin/bash - ckan
virtualenv --no-site-packages datapusher
. datapusher/bin/activate
mkdir datapusher/src
cd datapusher/src
git clone -b stable https://github.com/ckan/datapusher.git
cd datapusher
pip install -r requirements.txt

В файле development.ini добавим соответствующий плагин:

ckan.plugins = <прочие плагины> datapusher

Также раскомментируйте следующие строки:

ckan.datapusher.formats = csv xls xlsx tsv application/csv application/vnd.ms-excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
ckan.datapusher.url = http://127.0.0.1:8800/

8.2.9. Ссылка на who.ini

Файл who.ini (конфигурационный файл Repoze.who) должен располагаться в той же папке, что и конфигурационный файл CKAN:

ln -s /usr/lib/ckan/default/src/ckan/who.ini /etc/ckan/default/who.ini

8.2.10. Установка темы

В файл development.ini добавьте плагин с темой:

ckan.plugins = <прочие плагины> fareast_theme

И установите саму тему:

sudo su -s /bin/bash - ckan
. default/bin/activate
cd default/src
git clone https://github.com/nextgis/ckanext-fareast_theme.git
pip install -e ./ckanext-fareast_theme

8.2.11. Дополнительные настройки

В файлe development.ini:

ckan.auth.create_user_via_web = false
ckan.locale_default = ru
ckan.locales_offered = en ru
ckan.locales_filtered_out = ru_RU

# Убрать '/ckan', если приложение монтируется в '/'
ckan.favicon = /ckan/base/images/ckan.ico

# На данный каталог у пользователя, под которым будет
# запускаться CKAN, должны быть права на запись
ckan.storage_path = /mnt/portal/ckan/default

8.2.12. Развёртывание CKAN

За основу взята инструкция по развёртыванию из официальной документации.

Переходим в папку /etc/ckan/default:

sudo su -s /bin/bash - ckan
cd /etc/ckan/default

И создаём файл uwsgiapp.py со следующим содержимым:

import os
activate_this = os.path.join('/usr/lib/ckan/default/bin/activate_this.py')
execfile(activate_this, dict(__file__=activate_this))

from paste.deploy import loadapp
config_filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'development.ini')
from paste.script.util.logging_config import fileConfig
fileConfig(config_filepath)
application = loadapp('config:%s' % config_filepath)

Устанавливаем Nginx и uWSGI:

sudo yum install uwsgi uwsgi-plugin-python

Переходим в папку /etc/uwsgi.d и создаём файл ckan.ini следующего содержания:

[uwsgi]

plugins = python
lazy-apps = true

master = true
workers = 4
no-orphans = true

pidfile = /run/uwsgi/%n.pid
socket = /run/uwsgi/%n.sock
chmod-socket = 666

logto = /var/log/uwsgi/%n.log
log-date = true

harakiri = 6000

mount = /ckan=/etc/ckan/default/uwsgiapp.py
manage-script-name = true

Поскольку uWSGI запущен в режиме Tyrant, то необходимо изменить владельца конфигурационного файла ckan.ini:

sudo chown uwsgi:uwsgi ckan.ini

Создадим папку, куда будут писаться логи:

sudo mkdir /var/log/uwsgi
sudo touch /var/log/uwsgi/ckan.log
sudo chown uwsgi:uwsgi /var/log/uwsgi/ckan.log

Для ротации логов в папке /etc/logrotate.d создадим файл uwsgi следующего содержания:

/var/log/uwsgi/*.log {
    copytruncate
    daily
    rotate 5
    compress
    delaycompress
    missingok
    notifempty
}

Запускаем uWSGI:

sudo systemctl start uwsgi
sudo systemctl enable uwsgi

В папке /etc/nginx/conf.d создадим файл ckan.conf следующего содержания:

uwsgi_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=ckan:30m max_size=250m;

server {
      listen               80;
      server_name          82.162.194.216;
      client_max_body_size 100M;

      location /ckan {
        uwsgi_read_timeout 600s;
        uwsgi_send_timeout 600s;

        include            uwsgi_params;
        uwsgi_pass         unix:/run/uwsgi/ckan.sock;

        # Cache stuff
        uwsgi_cache         ckan;
        uwsgi_cache_methods GET HEAD;
        uwsgi_cache_bypass  $cookie_auth_tkt;
        uwsgi_no_cache      $cookie_auth_tkt;
        uwsgi_cache_valid   30m;
        uwsgi_cache_key     $host$scheme$proxy_host$request_uri;
    }
}

Создадим папку под кэш:

sudo mkdir /var/lib/nginx/cache
sudo chown nginx:nginx /var/lib/nginx/cache

Запускаем Nginx:

sudo systemctl start nginx
sudo systemctl enable nginx

Предупреждение

Если приложение при попытке его открыть возвращает 502 Bad Gateway, а в логах Nginx connect() to unix:/run/uwsgi/ckan.sock failed (13: Permission denied), то причина в SELinux - либо настройте его, либо отключите.

8.2.13. Развёртывание DataPusher

sudo cp /usr/lib/ckan/datapusher/src/datapusher/deployment/datapusher.wsgi /etc/ckan/
sudo cp /usr/lib/ckan/datapusher/src/datapusher/deployment/datapusher_settings.py /etc/ckan/
sudo chown ckan:ckan /etc/ckan/datapusher.wsgi
sudo chown ckan:ckan /etc/ckan/datapusher_settings.py

В файле datapusher_settings.py допишите строку:

MAX_CONTENT_LENGTH = 1073741824

Подготавливаем uWSGI:

sudo touch /etc/uwsgi.d/datapusher.ini
sudo chown uwsgi:uwsgi /etc/uwsgi.d/datapusher.ini
sudo touch /var/log/uwsgi/datapusher.log
sudo chown uwsgi:uwsgi /var/log/uwsgi/datapusher.log

В файл datapusher.ini помещаем следующее содержимое:

[uwsgi]

plugins = python

master = true
workers = 1
threads = 5
no-orphans = true

pidfile = /run/uwsgi/%n.pid
http-socket = 127.0.0.1:8800

logto = /var/log/uwsgi/%n.log
log-date = true

harakiri = 6000

wsgi-file = /etc/ckan/datapusher.wsgi

8.2.14. Установка расширения ckanext-geoview

Данное расширение предоставляет набор плагинов, позволяющих осуществлять предпросмотр опубликованных ресурсов прямо в браузере.

Оригинальная версия данного расширения не содержит некоторых нужных нам возможностей, поэтому была выполнена его доработка. В связи с этим устанавливать его мы будем из собственного репозитория:

sudo su -s /bin/bash - ckan
. default/bin/activate
cd default/src
git clone https://github.com/nextgis/ckanext-geoview.git
pip install -e ./ckanext-geoview

Изменим стандартную подложку, для этого в файл development.ini добавим следующие строки:

# Settings for ckanext-geoview extension
ckanext.spatial.common_map.type = custom
ckanext.spatial.common_map.custom.url = http://tiles.maps.sputnik.ru/{z}/{x}/{y}.png
ckanext.spatial.common_map.custom.name = Карта Спутник
ckanext.spatial.common_map.attribution = © <a href="http://sputnik.ru">Спутник</a> | © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>

8.2.15. Исправление текущего кода CKAN

В последующих релизах эти ошибки будут устранены, но пока они не выпущены, то приходится модифицировать код самостоятельно. Это необходимо сделать в соответствии со следующими коммитами: #9cf0369, #a63de1b, #a76da0c.

8.2.16. Исправления текущего кода DataStore и DataPusher

Откроем файл commas.py:

cd /usr/lib/ckan/datapusher/lib/python2.7/site-packages/messytables
nano commas.py

И отредактируем следующую строку. Вместо:

# Fix the maximum field size to something a little larger
csv.field_size_limit(256000)

должно быть:

# Fix the maximum field size to something a little larger
csv.field_size_limit(100 * 1024 * 1024)

На данный момент в CKAN DataStore нет возможности исключить поле из индекса на основе его имени, см. #2837, поэтому (как временное решение) отключим текстовые поля для индексации. В противном случае DataStore пытается построить индекс для поля с WKT геометрией (если такое поле есть в CSV) и падает. Откроем файл helpers.py:

cd /usr/lib/ckan/default/src/ckan/ckanext/datastore
nano helpers.py

И отредактируем следующий фрагмент. Вместо:

def should_fts_index_field_type(field_type):
    return field_type.lower() in ['tsvector', 'text', 'number']

должно быть:

def should_fts_index_field_type(field_type):
    return field_type.lower() in ['tsvector', 'number']

Сделаем ещё одно исправление для отключения включения WKT в полнотекстовый поиск. Откроем файл db.py:

cd /usr/lib/ckan/default/src/ckan/ckanext/datastore
nano db.py

И отредактируем функцию _to_full_text:

def _to_full_text(fields, record):
    full_text = []
    ft_types = ['int8', 'int4', 'int2', 'float4', 'float8', 'date', 'time',
                'timetz', 'timestamp', 'numeric', 'text']
    for field in fields:
        value = record.get(field['id'])
        if not value:
            continue

        if 'POINT' in unicode(value) or \
           'LINESTRING' in unicode(value) or \
           'POLYGON' in unicode(value):
                continue

        if field['type'].lower() in ft_types and unicode(value):
            full_text.append(unicode(value))
        else:
            full_text.extend(json_get_values(value))
    return ' '.join(set(full_text))

Увеличим таймаут в файле jobs.py:

cd /usr/lib/ckan/datapusher/src/datapusher/datapusher
nano jobs.py

Вместо DOWNLOAD_TIMEOUT = 30 выставьте DOWNLOAD_TIMEOUT = 5 * 60.

Предупреждение

Проблемы, с которыми мы столкнулись при загрузке CSV в DataStore. В некоторых полях был текст NULL, но по первой записи DataStore определял, что это numeric, и когда доходил до NULL - падал. В некоторых полях в качестве разделителя разрядов была ,. Исправляется путём редактирования соответствующих ресурсов NextGIS Web.

При загрузке данных всегда сверяйте количество загруженных объектов и фактическое количество объектов в слое. Количество объектов в слое можно получить по API (подставьте свой идентификатор ресурса):

http://82.162.194.216/ngw/api/resource/167/feature_count

8.2.17. Открытие портов

Откроем 80 порт:

sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
sudo firewall-cmd --reload

Если с локального хоста CKAN недоступен по своему публичному адресу, то это может быть исправлено, например, так:

sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -d 82.162.194.216 -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:80 --permanent
sudo firewall-cmd --reload

8.2.18. Backup и Restore

Данные процедуры описаны в разделе db: Manage databases официальной документации.

Если CKAN и NextGIS Web были развёрнуты на одной машине, то при переносе этой связки на другой адрес, необходимо изменить адреса ресурсов NextGIS Web на новые. Проще всего это сделать, отредактировав файл с бэкапом CKAN до момента его восставновления, например:

sed -i 's:78.46.100.76/opendata_ngw:82.162.194.216/ngw:g' ckan.pg_dump

Если вы используете DataStore, то помимо переноса базы самого CKAN, необходимо переносить и базу данных DataStore, datastore_default в нашем случае.

Предупреждение

Если после восстановления данных из архива отображается, что число наборов данных равно 0, то поможет переиндексация.

8.2.19. Дополнительно

Конфигурационный файл uWSGI для NextGIS Web:

[uwsgi]

plugins = python
lazy-apps = true

master = true
workers = 4
no-orphans = true

pidfile = /run/uwsgi/%n.pid
socket = /run/uwsgi/%n.sock
chmod-socket = 666

logto = /var/log/uwsgi/%n.log
log-date = true

limit-post = 7516192768

harakiri = 6000
socket-timeout = 6000

env = PASTE_CONFIG=/opt/ngw/development.ini
env = LANG=ru_RU.UTF-8

home = /opt/ngw/env
mount = /ngw=/opt/ngw/nextgisweb/nextgisweb/uwsgiapp.py
manage-script-name = true

Конфигурационный файл Nginx:

uwsgi_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=ckan:30m max_size=250m;
uwsgi_cache_path /mnt/portal/ngw/cache levels=1:2 keys_zone=ngw:30m max_size=10g inactive=7d;

server {
      listen                      80;
      server_name                 82.162.194.216;
      client_max_body_size        6G;
      large_client_header_buffers 8 32k;

      location /ckan {
        uwsgi_read_timeout 600s;
        uwsgi_send_timeout 600s;

        include            uwsgi_params;
        uwsgi_pass         unix:/run/uwsgi/ckan.sock;

        # Cache stuff
        uwsgi_cache         ckan;
        uwsgi_cache_methods GET HEAD;
        uwsgi_cache_bypass  $cookie_auth_tkt;
        uwsgi_no_cache      $cookie_auth_tkt;
        uwsgi_cache_valid   30m;
        uwsgi_cache_key     $host$scheme$proxy_host$request_uri;
    }

    location /ngw {
        uwsgi_read_timeout 600s;
        uwsgi_send_timeout 600s;

        include            uwsgi_params;
        uwsgi_pass         unix:/run/uwsgi/ngw.sock;

        # Cache stuff
        uwsgi_cache          ngw;
        uwsgi_cache_methods  GET HEAD;
        uwsgi_cache_bypass   $cookie_tkt;
        uwsgi_no_cache       $cookie_tkt;
        uwsgi_cache_valid    7d;
        uwsgi_ignore_headers Expires Cache-Control Set-Cookie;
        uwsgi_cache_key      $host$scheme$proxy_host$request_uri;
        add_header           X-uWSGI-Cache $upstream_cache_status;
    }

    location /opendata_map {
        index index.html index.htm;
        root /var/www;
    }
}

Пример удаления записей из кэша Nginx по маске:

grep -lr 'ngw/api/resource/604*' /mnt/portal/ngw/cache/* | xargs rm -rf