понедельник, 16 апреля 2018 г.

jupyter receipt

На основной вопрос "почему юпитер стоит реализовать server-side" есть один простой ответ: для установки пакетов на windows нужно невероятное количество удачи и терпения. Хотя бы потому что нужно правильно подобрать сраный компилятор, для установки зависимостей не бинарями: пруф (архив, peep)
В этой серии мы будем:
  • ставить Jupyter
  • сделаем ему мультилеер, сингл-преер - не круто
  • сделем из него сервис с автозапуском
  • научим его ходить в базу
  • научим его ходить а рабочую базу по vpn (уже настроено, хост только прописать надо)

Установка


вообще, как адекватно поставить виртуальное окружение описано уже давно https://askubuntu.com/a/244642
когда уже работаешь под venv, то просто можно сделать так
python3 -m pip install --upgrade pip
python3 -m pip install jupyter

тут будет происходит неведома херня: запускаю в виртуальном окружении jupyter notebook, мне в ответ прилетает
[I 03:02:35.436 NotebookApp] Serving notebooks from local directory: /home/enot
[I 03:02:35.436 NotebookApp] 0 active kernels
[I 03:02:35.436 NotebookApp] The Jupyter Notebook is running at:
[I 03:02:35.436 NotebookApp] http://localhost:8888/?token=4f5d7ce48936297318b86b9767e2187e6a5aadaa9000b894
[I 03:02:35.437 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 03:02:35.437 NotebookApp]

    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://localhost:8888/?token=4f5d7ce48936297318b86b9767e2187e6a5aadaa9000b894
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: x-www-browser: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: firefox: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: iceweasel: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: seamonkey: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: mozilla: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: epiphany: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: konqueror: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: chromium-browser: not found
/usr/bin/xdg-open: 771: /usr/bin/xdg-open: google-chrome: not found
т.е. приложка пытается открыть консольный браузер. В моем случае это был Lync и эффект от увиденного был ужасный. Это примерно как когда ты первый раз открываешь vim и не представляешь как из него выйти - так же первые разы просто рвал ssh сессию, как сельский буритино) В общем я был несколько в шоке от происходящего, а всего то надо явно указывать `jupyter notebook --no-browser --port=8888`. Ниже пара команд почитать справочную информацию
`jupyter -h` - Show help information, including available subcommands.
`jupyter --config-dir` - There you can find your {application} cofigs when run `jupyter {application} --generate-config`
`jupyter --paths` - All dirs in one commands
`jupyter --paths --json` -- json like output
`jupyter console -h` - configuration options
`jupyter kernelspec list` - list of available kernels

настройка


Такие грабли происходили, потому что сразу надо было прочитать про его console, делаем следующее
jupyter notebook --generate-config
jupyter notebook password
#hardpass

а теперь подправим конфиг `vim ~/.jupyter/jupyter_notebook_config.py` вот так:
c.NotebookApp.open_browser = False
c.NotebookApp.port = 6745
c.NotebookApp.ip = '0.0.0.0' # Any ip

и можно уже ходить в приложение используя проброс портов по ssh. Это вполне рабочий вариант, но как то коряво.

jupyter hub


Это один из тех немногих способов сделать все не по уебански. Даже мульти-юзерность будет.
Вариантов реализации мульти-юзер доступа много, но самый адекватный вариант - придется пакеты ставить глобально

requirements


sudo apt-get install npm nodejs-legacy -y
python3 -m pip install jupyterhub
sudo useradd rhea
sudo pip install sudospawner
sudo npm install -g configurable-http-proxy

user config


добавим группу для пользаков. Если что, они уже были в системе
sudo addgroup jupyterhub
sudo usermod -a -G jupyterhub enot
sudo usermod -a -G jupyterhub lisa
groups | grep jupyterhub

возможно надо будет вылогиниться. Через `sudo visudo` добавляем возможность запуска
Runas_Alias JUPYTER_USERS = rhea, enot, lisa, stup
Cmnd_Alias JUPYTER_CMD = /usr/local/bin/sudospawner
rhea ALL=(JUPYTER_USERS) NOPASSWD:JUPYTER_CMD
rhea ALL=(%jupyterhub) NOPASSWD:JUPYTER_CMD

а потом проверяем командой `sudo -u rhea sudo -n -u $USER /usr/local/bin/sudospawner --help`
Теперь добавляем PAM авторизацию
ls -l /etc/shadow
sudo groupadd shadow
sudo chgrp shadow /etc/shadow
sudo chmod g+r /etc/shadow
ls -l /etc/shadow
sudo usermod -a -G shadow rhea
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/nodejs
test via `sudo -u rhea python3 -c "import pamela, getpass; print(pamela.authenticate('$USER', getpass.getpass()))"`


server config


сначала делаем его окружение
sudo mkdir /etc/jupyterhub
sudo chown rhea /etc/jupyterhub
sudo mkdir /var/log/jupyterhub/
sudo touch /var/log/jupyterhub/hub.log
chown -R rhea:shadow /var/log/jupyterhub/
cd /etc/jupyterhub

проверяем командой `sudo -u rhea jupyterhub --JupyterHub.spawner_class=sudospawner.SudoSpawner`
Если все нормас, то потом генерим конфиг, что бы не заниматься херней в консоли
sudo su
jupyterhub --generate-config
mv jupyterhub_config.py jupyterhub_config.py.old
sudo cat < /etc/jupyterhub/jupyterhub_config.py
c.JupyterHub.active_server_limit = 10
c.JupyterHub.concurrent_spawn_limit = 5
c.JupyterHub.base_url = '/jupyter/'
c.JupyterHub.spawner_class = 'sudospawner.SudoSpawner'
c.JupyterHub.port = 6535
c.Authenticator.admin_users = {'enot'}
c.JupyterHub.extra_log_file = '/var/log/jupyterhub/hub.log'
EOT
# ctrl+D

открываем лог `tail -f /var/log/jupyterhub/hub.log` в одной консоли
и проверяем командой `sudo -u rhea jupyterhub` в другой
и заходим по адресу http://buben.lan:6535/jupyter/

демонизация


Вообще я обычно демонизирую женщин, но тут я намерен проделать это с приложкой (с). По мотивам /jupyterhub/wiki
делаем `sudo vim /etc/systemd/system/jupyterhub.service`

[Unit]
Description=JupyterHub
After=network-online.target

[Service]
User=rhea
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
ExecStart=/usr/local/bin/jupyterhub
WorkingDirectory=/etc/jupyterhub

[Install]
WantedBy=multi-user.target

потом докинем библиотеки в системные файлы
ls -la /etc/systemd/system/jupyterhub.service
sudo cp /lib/systemd/system/jupyterhub.service /etc/systemd/system/jupyterhub.service
ls -la /lib/systemd/system/jupyterhub.service

далее подгружаем новый конфиг с сервисы и магия должна заработать
sudo systemctl daemon-reload
sudo systemctl start jupyterhub
sudo systemctl status jupyterhub

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

autostarup


в афтербут сервис добавляется командой `sudo systemctl enable jupyterhub.service`

web-app


у нас есть приготовленный nginx с https терминированием. Только благодаря этому не надо городить кучу конфигов на виртуалке и мудохаться с перевыпуском сертификата за натом. И если мы хотим иметь веб приложение jupyter по адресу `https://box.now.io/jupyter/`, то надо добавить в конфиг такой локэйшн
location /jupyter/ {
    proxy_pass http://buben.lan:6535;
    default_type  application/octet-stream;
    proxy_redirect     off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Origin "";
}

Есть пара интересных рекомендаций, как можно сделать еще, у этого улыбчивого парня
http://nathan.vertile.com/blog/2017/12/07/run-jupyter-notebook-behind-a-nginx-reverse-proxy-subpath/
но я забил, работает и ладно.

лайфхак для ленивых `sed -i 's/\r$//' nginx.conf`
и рестартануть веб-сервер `/etc/init.d/nginx reload`

database connect


Поставить довольно просто для postgre довольно просто
sudo add-apt-repository "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -sc)-pgdg main"
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql-client-9.6 -y # собсно только ради этого все и затевалось
sudo apt-get install postgresql postgresql-contrib -y # какие то очень нужные модули
sudo apt-get install pgadmin3 -Y #веб-клиент, довольно не удобный уж лучше поставьте DataGrip с паленым серером лицензирования
sudo apt-get install postgresql-9.6 -y # ну и почему бы до кучи саму базу не поставить
sudo pip install psycopg2-binary
sudo -u postgres psql

Далее надо настроить тестового пользователя
create user bender with password 'test1';
create database bender owner bender;
grant all privileges on database bender to bender;

теперь пробуем подключиться в консоли из под своего робота `sudo -u bender psql`
create schema report authorization bender;
CREATE TABLE report.play2 (
    equip_id serial PRIMARY KEY,
    type varchar (50) NOT NULL,
    color varchar (25) NOT NULL,
    location varchar(25) check (location in ('north', 'south', 'west', 'east', 'northeast', 'southeast', 'southwest', 'northwest')),
    install_date date
);
\dt
alter table report.play2 owner to bender;
insert into report.play2 (type, color, location, install_date) values ('blabla', 'blue', 'north', current_date) ;
select * from report.play2;
\dt
\q

теперь пробуем из под jupyterhub в ноутбуке используя psycopg пробуем
import psycopg2
import sys

dsn_database = "bender"
dsn_hostname = "localhost"
dsn_port = "5432"
dsn_uid = "bender"
dsn_pwd = "test1"
try:
    conn_string = "host="+dsn_hostname+" port="+dsn_port+" dbname="+dsn_database+" user="+dsn_uid+" password="+dsn_pwd
    print("Connecting to database\n  ->%s" % (conn_string))
    conn=psycopg2.connect(conn_string)
    print("Connected!\n")
except:
    print("Unable to connect to the database.")

cursor = conn.cursor()
cursor.execute("""SELECT datname from pg_database""")
rows = cursor.fetchall()

print("\nShow me the databases:\n")
for row in rows:
    print("   ", row[0])

vpn

Отлично, в местное биде приложка ходить умеет. Теперь надо ее нацелить на рабочую базуку. Если она за фаерволом - не беда, воспользуемся cisco vpn clinet'ом.