更新:PyCharm 2017.1 有解决此问题的方法,请参阅blog entry
这是我解决问题的方法。我的情况是,我被分配对使用 docker-compose 创建一组四个容器的 Web 应用程序的特定区域进行干预。 Docker-compose 是一种元 docker,它通过一个命令管理多个 docker 容器。我不想破坏他们现有的设置,因为很多事情都依赖于它。但由于我正在处理其中一个图像中的一个特定部分,我决定使用 ssh 扩展其中一个容器,以便我可以从 PyCharm 进行调试。此外,我希望应用程序在启动时正常运行,只有强制它退出然后从 PyCharm 连接到它,我才能拥有一个可调试的组件。这是我在使用 boot2docker(在 VirtualBox 上)正确设置 docker 的 Mac 上所做的。
首先,我需要扩展名为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 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -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。是的,每个人都说 docker 中的 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 端口映射到 VM 中可用的已知端口,回想一下,我使用的是 boot2docker,它在 VirtualBox 上运行,但需要映射到 PyCharm 可以访问的位置。在 VirtualBox 中,打开boot2docker VM 并选择Adapter 1。有时“附加到:”组合会自行取消选择,因此请注意这一点。在我的情况下,它应该选择 NAT。
点击“端口转发”,将内部端口映射到本地主机上的a端口,我选择使用相同的端口号。它应该是这样的:
- 姓名:
ssh_mapped;
- 协议:
TCP;
- 主机IP:
127.0.0.1;
- 主机端口:
7722;
- 访客IP:;
- 访客端口:
7722
注意:注意不要更改 boot2docker ssh 设置,否则您最终将无法正确启动 VM。
所以,此时我们有一个扩展我的目标容器的容器。它在端口22 上运行ssh 并将其映射到7722,因为其他容器可能想要使用22,并且在VirtualBox 环境中可见。 VirtualBox 将 7722 映射到 7722 到本地主机,您可以通过 ssh 进入容器:
ssh root@localhost -p 7722
然后它将提示输入密码“soup4nuts”,您应该能够找到特定于您的容器的内容,以验证它是正确的并且一切正常。如果我在本地机器以外的任何地方部署它,我不会与 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 认为它在其他地方是一个 unixy 盒子。是的,在 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,并将部署和 Web 路径设置为单个“/”,但由于我的代码与 URL 不对应,并且我不使用它进行调试,所以它是 Web 路径设置中的占位符。我不知道你怎么设置你的。
在 Project X/Project Interpreter 选项卡上,创建一个新的 Remote Python Interpreter。您可以选择部署配置并选择我们在上面创建的dockercompose 配置。主机 URL 应填写为 ssh://root@localhost:7722,Python 解释器路径可能为 /usr/bin/python。我们需要设置 PyCharm Helpers Path,因为默认值不会在重做的容器中继续存在。我实际上去了我的项目本地目录并在根目录中创建了一个.pycharm_helpers 目录,然后将此处的路径设置为/opt/applications/myproject/.pycharm_helpers,当我点击确定按钮时,它会将文件“向上”复制到目录中。我不知道它是否会自动创建它。
不要忘记.pycharm_helpers 目录可能应该排除在项目根选项卡上。
此时你可以进入Build、Execution、Deployment选项卡,在Console/Python Console下,选择我们上面创建的远程解释器并将工作目录设置为/opt/applications/myproject,你就可以在容器,如果你喜欢。
现在您需要创建一个运行配置,以便您可以远程调试您的 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 设置需要多次迭代,所以顺序可能不正确,如果失败,请再次尝试。这是很多东西,很容易跳过一些关键的东西。