Ответ 1
ОБНОВЛЕНИЕ: PyCharm 2017.1 имеет решение этой проблемы, см. эту запись в блоге
Вот как я решил проблему. Мои обстоятельства таковы, что мне поручили вмешательство в определенную область веб-приложения, которое использовало docker-compose для создания набора из четырех контейнеров. Docker-compose - это вид мета-докера, который управляет несколькими контейнерами-докерами из одной команды. Я не хотел портить их существующую настройку, так как от этого зависит очень много вещей. Но так как я работал над одной конкретной частью на одном из изображений, я решил, что я расширю один из контейнеров с помощью ssh, чтобы я мог отлаживать из PyCharm. Кроме того, я хотел, чтобы приложение запускалось как обычно при запуске, и только принудительно завершив его работу и подключившись к нему из PyCharm, я получил бы отлаживаемый компонент. Вот что я сделал на своем Mac, который использует boot2docker (на VirtualBox) для правильной настройки докера.
Сначала мне нужно расширить целевой контейнер, который называется jqworker
. Я собираюсь использовать "supervisior"
для тяжелой работы по управлению вещами.
FROM jqworker
# Get supervisor to control multiple processes, sshd to allow connections.
# And supervisor-stdout allows us to send the output to the main docker output.
RUN apt-get update && apt-get install -y supervisor openssh-server python-pip \
&& pip install supervisor-stdout \
&& mkdir -p /var/run/sshd \
&& mkdir -p /var/log/supervisor \
&& mkdir -p /etc/supervisor/conf.d
COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Fix up SSH, probably should rip this out in real deploy situations.
RUN echo 'root:soup4nuts' | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
# SSH login fix. Otherwise user is kicked off after login
RUN sed '[email protected]\s*required\s*[email protected] optional [email protected]' -i /etc/pam.d/sshd
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile
# Expose SSH on 22, but this gets mapped to some other address.
EXPOSE 22
# Replace old entrypoint with supervisiord, starts both sshd and worker.py
ENTRYPOINT ["/usr/bin/supervisord"]
Supervisor позволяет мне запускать несколько задач из одной команды, в данном случае исходной команды и SSHD. Да, все говорят, что SSHD в докере - это зло, и контейнеры должны и то, и это, и тому подобное, но программирование - это решение проблем, а не соответствие произвольному диктату, игнорирующему контекст. Нам нужен SSH для отладки кода, а не его развертывания в поле, что является одной из причин, по которой мы расширяем существующий контейнер, а не добавляем его в структуру развертывания. Я запускаю его локально, чтобы я мог отлаживать код в контексте.
Вот файл supervisord.conf
, обратите внимание, что я использую пакет supervisor-stdout
для прямого вывода в супервизор, а не для регистрации данных, поскольку я предпочитаю видеть все это в одном месте:
[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
[program:worker]
command=python /opt/applications/myproject/worker.py -A args
directory=/opt/applications/myproject
stdout_events_enabled=true
stderr_events_enabled=true
[eventlistener:stdout]
command = supervisor_stdout
buffer_size = 100
events = PROCESS_LOG
result_handler = supervisor_stdout:event_handler
У меня есть каталог сборки, содержащий два вышеупомянутых файла, и из терминала я собираю Dockerfile
с помощью:
docker build -t fgkrqworker .
Это добавляет его, чтобы я мог позвонить из docker
или docker-compose
. Не пропустите завершающую точку!
Поскольку приложение использует docker-compose
для запуска набора контейнеров, существующий контейнер WORKER
будет заменен на тот, который решает мои проблемы. Но сначала я хочу показать, что в другой части моего docker-compose.yml
я определяю отображение из контейнеров на мой локальный жесткий диск, это один из нескольких сопоставляемых томов:
volumes: &VOLUMES
? /Users/me/source/myproject:/opt/applications/myproject
Затем фактическое определение для моего контейнера, которое ссылается на вышеупомянутое VOLUMES
:
jqworker: &WORKER
image: fgkrqworker
privileged: true
stdin_open: true
detach: true
tty: true
volumes:
<<: *VOLUMES
ports:
- "7722:22"
Это сопоставляет порт SSH с известным портом, доступным в виртуальной машине, напомним, что я использую boot2docker
, который работает на VirtualBox, но необходимо сопоставить его с тем, где PyCharm может добраться до него. В VirtualBox откройте виртуальную машину boot2docker
и выберите Adapter 1
. Иногда комбо "Attached to:" отменяет выбор, так что следите за этим. В моем случае он должен был выбрать NAT
.
Нажмите "Переадресация портов" и сопоставьте внутренний порт с портом на локальном хосте, я решил использовать тот же номер порта. Это должно быть что-то вроде:
- Имя:
ssh_mapped
; - Протокол:
TCP
; - IP-адрес хоста:
127.0.0.1
; - Порт хоста:
7722
; - Гостевой IP :;
- Гостевой порт:
7722
Примечание: будьте осторожны, чтобы не изменить настройку boot2docker ssh
, иначе вы не сможете правильно запустить виртуальную машину.
Итак, на данный момент у нас есть контейнер, который расширяет мой целевой контейнер. Он запускает ssh на порту 22
и отображает его на 7722
, поскольку другие контейнеры могут захотеть использовать 22
, и он виден в среде VirtualBox. VirtualBox сопоставляет 7722
с 7722
локальный хост, и вы можете войти в контейнер с помощью ssh:
ssh [email protected] -p 7722
После этого появится запрос на ввод пароля 'sou4nuts', и вы сможете найти что-то конкретное для вашего контейнера, чтобы убедиться, что он правильный и все работает нормально. Я бы не связывался с root, если бы развертывал это где-нибудь, кроме своей локальной машины, так что будьте осторожны. Это только для локальной отладки, и вы должны дважды или трижды подумать об этом на живом сайте.
На данный момент вы, вероятно, сможете понять все остальное, если вы использовали удаленную отладку PyCharm. Но вот как я это настроил:
Во-первых, напомним, что у меня docker-compose.yml
отображается каталог проекта:
? /Users/me/source/myproject:/opt/applications/myproject
В моем контейнере /opt/applications/myproject
на самом деле находится /Users/me/source/myproject
на моем локальном жестком диске. Итак, это корень моего проекта. Мой PyCharm видит этот каталог как корень проекта, и я хочу, чтобы PyCharm записал здесь .pycharm_helpers
, чтобы он сохранялся между сеансами. Я управляю исходным кодом на стороне Mac, но PyCharm думает, что в других местах это не совсем удобно. Да, это немного мешает, пока JetBrains не использует решение Docker.
Сначала перейдите в Project X/Project Structure и создайте корень контента локального сопоставления, в моем случае это означает /Users/me/source/myproject
Позже вернитесь и добавьте .pycharm_helpers
к исключенному набору, мы не хотим, чтобы это заканчивалось управлением исходным кодом или приводило к путанице PyCharm.
Перейдите на вкладку "Сборка, выполнение, развертывание", выберите "Развертывание" и создайте новое развертывание типа SFTP. Хост - localhost, порт 7722
, корневой путь - /opt/applications/myproject
, имя пользователя - root
, пароль - soup4nuts
, и я проверил опцию сохранения пароля. Я назвал свое развертывание "dockercompose", чтобы позже его можно было найти.
На вкладке "Сопоставления развертывания" я установил для локального пути /Users/me/source/myproject
, а для развертывания и веб-пути - один символ "/", но поскольку мой код не соответствует URL-адресу, и я не использую его для отладки, он является заполнителем в настройка веб-пути. Я не знаю, как вы могли бы установить свои.
На вкладке Project X/Project Interpreter создайте новый удаленный интерпретатор Python. Вы можете выбрать конфигурацию развертывания и выбрать конфигурацию dockercompose
, которую мы создали выше. URL хоста должен быть заполнен как ssh://[email protected]:7722
, а путь интерпретатора Python, скорее всего, будет /usr/bin/python
. Нам нужно установить путь помощников PyCharm, так как по умолчанию он не сохранится при повторном контейнере. На самом деле я зашел в локальный каталог своего проекта и создал каталог .pycharm_helpers
в корневом каталоге, затем установил путь здесь как /opt/applications/myproject/.pycharm_helpers
, и когда я нажал кнопку OK, он скопировал файлы "вверх" в каталог. Я не знаю, создаст ли он это автоматически или нет.
Не забывайте, что каталог .pycharm_helpers
, вероятно, следует исключить на вкладке корней проекта.
На этом этапе вы можете перейти на вкладку Build, Execution, Deployment и в Console/Python Console выбрать удаленный интерпретатор, который мы создали выше, и установить рабочий каталог на /opt/applications/myproject
, и вы можете запустить вашу Python Console в контейнере, если вы как.
Теперь вам нужно создать конфигурацию запуска, чтобы вы могли удаленно отлаживать код Python. Создайте новую конфигурацию Python и установите тот скрипт, который использовался для запуска кода Python в контейнере. Мой, из настроек супервизора, выше:
/opt/applications/myproject/worker.py -A args
Итак, я установил сценарий на /opt/applications/myproject/worker.py
, а параметры на -A args
.
Выберите удаленного переводчика, которого мы создали выше, и, при необходимости, рабочий каталог, для меня это /opt/applications/myproject
и для меня, который выполняет эту работу.
Теперь я хочу войти в свой контейнер и остановить скрипт worker.py
, чтобы я мог запустить отладочную версию. Конечно, если вам нравится, вы можете игнорировать запуск скрипта по умолчанию и использовать только контейнер для отладки.
Я мог бы открыть сессию ssh, чтобы остановить сценарий, но docker предоставляет полезную команду, которая сделает всю работу за меня, передав ее в среду.
$> docker exec -i -t supervisorctl stop worker
Поскольку мой процесс называется "работник". Обратите внимание, что вы можете перезапустить, заменив команду stop
на start
.
Теперь в PyCharm запустите сеанс отладки с конфигурацией запуска, созданной выше. Он должен подключиться и запустить все, и вы получите консольный вывод в окне. Поскольку мы убили того, кого изначально начал Supervision, он больше не подключен.
Это было место операции штанов, поэтому могут быть ошибки и неправильные предположения, которые я не заметил. В частности, для установки PyCharm потребовалось несколько итераций, поэтому порядок может быть неправильным, попробуйте повторить его снова, если он потерпит неудачу. Это много вещей и легко пропустить что-то критическое.