Кеширующее зеркало репозитория для python — DevPi

В статье я расскажу, как можно устанавливать пакеты для python с помощью pip, если на этом сервере нет интернета. Поможет нам в этом DevPI.















Теория




Когда вам нужно установить дополнительный модуль для python, то вы обычно выполняете команду pip install <имя модуля>.




pip — это установщик пакетов для python. Он их скачивает из определённого репозитория и устанавливает.




Репозитории для python называют индексами и по умолчанию pip использует индекс PyPI (Python Package Index). Но для pip можно указать и другой индекс.




В этой статье мы познакомимся с инструментом DevPI (github), который позволяет создать свой индекс (root/pypi), который синхронизован с индексом PyPI. Документация по этому инструменту доступна здесь.




Постановка задачи




В этой статье будем реализовывать следующую схему:




Схема работы
Схема работы




На рисунке выше, сервера Server2 и Server3 не имеют выхода в интернет, зато у них есть доступ к серверу Server1.




Server1 имеет доступ в интернет, поэтому на нём можно установить программу DevPI. С его помощью можно создать свой индекс (root/pypi) и синхронизировать его с индексом PyPI.




Server2 и Server3 смогут устанавливать пакеты с помощью pip из индекса, который мы создадим на Server1.




Практика




У меня все сервера на Debian 10.




В самом начале у меня есть интернет на всех серверах. Это необходимо, чтобы установить pip. Для python3 это пакет python3-pip. Его нужно поставить и на всех трёх серверах.




# apt install python3-pip




Теперь можете отключить интернет на серверах Server2 и Server3. Я просто удаляю на них шлюз по умолчанию.




Следующие действия будем выполнять на Server1 (на нём интернет остался).




Сервер devpi требует установки libffi-dev, установим его. Затем используя pip установим devpi-server и devpi-web.




# apt install libffi-dev
# pip3 install -U devpi-server
# pip3 install -U devpi-web




Дальше вам нужно добавить в переменную PATH путь /usr/local/bin. Туда были установлены следующие утилиты:




# ls -l /usr/local/bin | grep devpi
-rwxr-xr-x 1 root root  237 Apr 19 13:15 devpi-clear-search-index
-rwxr-xr-x 1 root root  231 Apr 19 13:12 devpi-export
-rwxr-xr-x 1 root root  219 Apr 19 13:12 devpi-fsck
-rwxr-xr-x 1 root root  234 Apr 19 13:12 devpi-gen-config
-rwxr-xr-x 1 root root  231 Apr 19 13:12 devpi-gen-secret
-rwxr-xr-x 1 root root  233 Apr 19 13:12 devpi-import
-rwxr-xr-x 1 root root  219 Apr 19 13:12 devpi-init
-rwxr-xr-x 1 root root  225 Apr 19 13:12 devpi-passwd
-rwxr-xr-x 1 root root  219 Apr 19 13:12 devpi-server




Чтобы добавить путь в переменную PATH нужно в файле .bashrc (расположен в домашней директории пользователя) дописать две строчки:




# nano .bashrc
*****
оставляем без изменения а снизу дописываем
*****
PATH="/usr/local/bin/:$PATH"
export PATH




А затем, чтобы применить изменения, выполните:




# . .bashrc




Теперь вы можете посмотреть версию вашего сервера:




# devpi-server --version
6.5.0




Управлять сервером devpi можно с помощью ещё одного модуля на python, который называется supervisor. Установим его:




# pip3 install supervisor




Теперь вы можете инициализировать devpi, сгенерировать конфиг и запустить сервер!




Инициализируем:




# devpi-init
INFO  NOCTX Loading node info from /root/.devpi/server/.nodeinfo
INFO  NOCTX generated uuid: 8d832c2dab2248c3ae18a6a088a9befd
INFO  NOCTX wrote nodeinfo to: /root/.devpi/server/.nodeinfo
INFO  NOCTX DB: Creating schema
INFO  [Wtx-1] setting password for user 'root'
INFO  [Wtx-1] created user 'root'
INFO  [Wtx-1] created root user
INFO  [Wtx-1] created root/pypi index
INFO  [Wtx-1] fswriter0: commited at 0

# cat /root/.devpi/server/.nodeinfo
{
  "role": "standalone",
  "uuid": "8d832c2dab2248c3ae18a6a088a9befd"
}




Выше вы видите, что был сгенерирован uuid для нашего индекса, информация об узле была добавлена в файл /root/.devpi/server/.nodeinfo. А также был создан пользователь индекса (root), его пароль (по умолчанию он пустой). И был создан индекс root/pypi.




Теперь сгенерируем конфиги:




# devpi-gen-config --port 4040 --host 0.0.0.0
It is highly recommended to use a configuration file for devpi-server, see --configfile option.
wrote gen-config/crontab
wrote gen-config/net.devpi.plist
wrote gen-config/launchd-macos.txt
wrote gen-config/nginx-devpi.conf
wrote gen-config/supervisor-devpi.conf
wrote gen-config/supervisord.conf
wrote gen-config/devpi.service
wrote gen-config/windows-service.txt

# cat gen-config/supervisord.conf
[supervisord]
[unix_http_server]
file=/tmp/devpi-supervisor.sock
[supervisorctl]
serverurl=unix:///tmp/devpi-supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
[include]
files = %(here)s/supervisor-devpi.conf

# cat gen-config/supervisor-devpi.conf
[program:devpi-server]
command=/usr/local/bin/devpi-server --port 4040 --host 0.0.0.0
user = root
priority = 999
startsecs = 5
redirect_stderr = True
autostart = True




Выше вы видите что создалась директория gen-config с конфигами для devpi сервера. Основной конфиг — это supervisord.conf, и дополнительный supervisor-devpi.conf. В последнем конфиге можно увидеть строку запуска сервера — /usr/local/bin/devpi-server —port 4040 —host 0.0.0.0.




Запустим сервер с помощью supervisord используя основной конфиг:




# supervisord -c gen-config/supervisord.conf




Теперь из server2 проверим что server1 отвечает на веб запросы с помощью curl:




# curl http://192.168.0.62:4040
<!doctype html>
<html>
    <head>
        <title>devpi</title>

    <link rel="stylesheet" type="text/css" href="http://192.168.0.62:4040/+static-4.0.8/style.css" />


    <script src="http://192.168.0.62:4040/+static-4.0.8/jquery-1.11.1.min.js"></script>
    <script src="http://192.168.0.62:4040/+static-4.0.8/moment-2.8.2.min.js"></script>
    <script src="http://192.168.0.62:4040/+static-4.0.8/common.js"></script>


    </head>
    <body>
       *********
    </body>
</html>




Также эту страничку можно открыть в браузере:




Внешний вид DevPI в браузере
Внешний вид DevPI в браузере




Сервер у нас пока в состоянии degraded, это потому что он ещё не успел синхронизоваться с Pypi:




DevPI - статус degraded (синхронизация не закончилась)
DevPI — статус degraded (синхронизация не закончилась)




После синхронизации статус станет «OK»:




DevPI - статус ok
DevPI — статус ok




Убедимся что на Server2 нет интернета и пробуем что-нибудь установить с помощью pip используя Server1:




# ping 8.8.8.8
connect: Network is unreachable

# pip3 install -i http://192.168.0.62:4040/root/pypi/+simple/ simplejson --trusted-host 192.168.0.62
Looking in indexes: http://192.168.0.62:4040/root/pypi/+simple/
Collecting simplejson
  Downloading http://192.168.0.62:4040/root/pypi/+f/65b/998193bd7b0c7/simplejson-3.17.6-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (130kB)
    100% |████████████████████████████████| 133kB 1.0MB/s
Installing collected packages: simplejson
Successfully installed simplejson-3.17.6




Всё работает!




Управление с помощью supervisord и supervisorctl




Теперь запускать и останавливать ваш devpi сервер, а также посмотреть его статус можно с помощью supervisorctl:




# supervisorctl -c gen-config/supervisord.conf stop devpi-server
# supervisorctl -c gen-config/supervisord.conf start devpi-server
# supervisorctl -c gen-config/supervisord.conf status devpi-server




Но после перезагрузки сервера вам нужно будет выполнить:




# supervisord -c gen-config/supervisord.conf




А затем управлять службой с помощью supervisorctl.




Управление с помощью systemd




Если ваша система использует systemd, то есть более удобный способ управлять сервером devpi. Для этого нужно скопировать файл службы gen-config/devpi.service в каталог /etc/systemd/system/, и выполнить команду systemctl daemon-reload:




# cp gen-config/devpi.service /etc/systemd/system/
# systemctl daemon-reload




А дальше вы можете запустить службу, добавить её в автозагрузку и управлять с помощью systemctl:




# systemctl start devpi
# systemctl enable devpi
# systemctl status devpi
# systemctl stop devpi




Облегчаем процесс установки пакетов




Как вы помните, нам приходится устанавливать пакеты с помощью такой команды:




# pip3 install -i http://192.168.0.62:4040/root/pypi/+simple/ simplejson --trusted-host 192.168.0.62




То есть нужно указывать адрес индекса и написать --trusted-host так как наш сервер не использует https.




Чтобы pip по умолчанию использовал нужный индекс, нужно создать в домашней директории каталог и конфиг для pip:




# mkdir .pip

# nano .pip/pip.conf
[global]
index-url = http://192.168.0.62:4040/root/pypi/+simple/

[search]
index = http://192.168.0.62:4040/root/pypi/




Теперь вы можете не указывать индекс:




# pip3 install getmac --trusted-host 192.168.0.62




Чтобы избавится от —trusted-host нужно на сервер поставить какой-нибудь веб-сервер (apache2 или nginx). Настроить на нём https и использовать в качестве прокси. Кстати, конфиг для nginx уже лежит в каталоге gen-config:




# cat gen-config/nginx-devpi.conf
server {
    server_name localhost $hostname "";
    listen 80;
    gzip             on;
    gzip_min_length  2000;
    gzip_proxied     any;
    gzip_types       application/json;

    proxy_read_timeout 60s;
    client_max_body_size 64M;

    # set to where your devpi-server state is on the filesystem
    root /root/.devpi/server;

    # try serving static files directly
    location ~ /+f/ {
        # workaround to pass non-GET/HEAD requests through to the named location below
        error_page 418 = @proxy_to_app;
        if ($request_method !~ (GET)|(HEAD)) {
            return 418;
        }

        expires max;
        try_files /+files$uri @proxy_to_app;
    }
    # try serving docs directly
    location ~ /+doc/ {
        # if the --documentation-path option of devpi-web is used,
        # then the root must be set accordingly here
        root /root/.devpi/server;
        try_files $uri @proxy_to_app;
    }
    location / {
        # workaround to pass all requests to / through to the named location below
        error_page 418 = @proxy_to_app;
        return 418;
    }
    location @proxy_to_app {
        proxy_pass http://localhost:4040;
        proxy_set_header X-outside-url $scheme://$http_host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}













На этом всё, спасибо за внимание!



2022-04-20T13:36:40
Сервера Linux