Windows 上丝滑开发 Python,并稳定构建 Docker 镜像

在 Windows 上做 Python 容器化开发,最省心的路线是采用一套清晰的分工:

  • Windows 负责桌面应用、浏览器、Docker Desktop
  • WSL 2 负责 Git、Python、Shell 和项目文件
  • Docker 容器 负责统一运行环境
  • VS Code 通过 WSL 或 Dev Containers 进入开发环境

一句话概括就是,代码放进 WSL,编辑器连进 WSL,Docker Desktop 接管容器,最终交付 Linux 镜像。这样可以显著减少路径、权限、换行符和依赖编译方面的麻烦。


一、推荐的整体架构

下面这套结构兼顾开发体验和交付一致性。

这里有一个很关键的原则:

不要把高频开发的代码放在 C:\Users\...,再从 WSL 或 Linux 容器中反复读取。

更推荐放在 WSL 的 Linux 文件系统中,例如:

bash 复制代码
~/projects/my-python-app

也就是:

text 复制代码
/home/你的用户名/projects/my-python-app

WSL 2 和 Docker Desktop 能够直接协作。项目放在 Linux 文件系统后,文件监听、依赖安装、Git 操作以及容器挂载通常都会更快,也更接近最终的 Linux 生产环境。,


二、一次性完成基础环境配置

环境只需要认真配置一次,后面基本就是开机、打开项目、开始写代码。

1. 安装并更新 WSL 2

以管理员身份打开 PowerShell,执行:

powershell 复制代码
wsl --install -d Ubuntu

安装结束后重启 Windows,再检查发行版状态:

powershell 复制代码
wsl --list --verbose

正常情况下会看到类似结果:

text 复制代码
NAME      STATE           VERSION
Ubuntu    Running         2

如果不是 WSL 2,可以执行:

powershell 复制代码
wsl --set-version Ubuntu 2
wsl --set-default-version 2

更新 WSL:

powershell 复制代码
wsl --update

进入 Ubuntu:

powershell 复制代码
wsl

随后更新基础软件:

bash 复制代码
sudo apt update
sudo apt upgrade -y
sudo apt install -y git curl build-essential

Docker Desktop 官方建议使用较新的 WSL 版本,并启用 WSL 2 后端,以获得更完整的文件系统、网络和容器功能支持。,

2. 安装 Docker Desktop

安装 Docker Desktop for Windows 后,打开设置页面:

text 复制代码
Settings
└── General
    └── Use the WSL 2 based engine

然后进入:

text 复制代码
Settings
└── Resources
    └── WSL Integration
        └── Enable integration with Ubuntu

保存并重启 Docker Desktop。

回到 Ubuntu 中验证:

bash 复制代码
docker version
docker compose version
docker run --rm hello-world

这些命令能正常运行,说明 WSL 不需要额外安装一套 Docker Engine。此时,WSL 中的 Docker CLI 会直接连接 Docker Desktop 管理的引擎。

不要同时在 Ubuntu 中用 apt install docker.io 再装一套 Docker,否则容易出现两个守护进程、两个上下文和两套镜像缓存,排查起来颇有一种左右手互搏的美感。,

3. 安装 VS Code 扩展

Windows 侧安装 VS Code,并加入以下扩展:

  • WSL
  • Dev Containers
  • Docker
  • Python
  • Pylance

进入 WSL 后创建项目目录:

bash 复制代码
mkdir -p ~/projects
cd ~/projects
mkdir my-python-app
cd my-python-app
code .

执行 code . 后,VS Code 会以 WSL 模式打开。窗口左下角应显示类似:

text 复制代码
WSL: Ubuntu

此时,VS Code 界面运行在 Windows,终端、Git、Python 扩展和代码分析则运行在 WSL 中,两边分工明确。官方文档也把 WSL 集成和 Dev Containers 作为 Windows 容器开发的推荐工作方式。,


三、选择适合自己的开发模式

Python 容器化开发大致有三种模式。对大多数团队来说,推荐从第二种起步,项目复杂后再切到第三种。

模式 Python 运行位置 优势 适用场景
WSL 本地虚拟环境 WSL 启动快、调试简单 脚本、算法、小型项目
WSL 编码 + Docker 运行 容器 环境一致、操作直观 Web 服务、常规团队项目
Dev Containers 容器 编辑器和工具链也统一 多人协作、复杂依赖、跨平台团队

Dev Containers 可以把 Python、调试器、扩展、系统依赖和项目配置一起声明。新成员克隆代码后直接打开容器,不必照着安装文档逐项"考古"。,


四、一个实用的 Python 项目模板

下面以一个普通 Python 服务为例。项目结构可以这样安排:

text 复制代码
my-python-app/
├── app/
│   ├── __init__.py
│   └── main.py
├── tests/
│   └── test_main.py
├── .devcontainer/
│   └── devcontainer.json
├── .dockerignore
├── .gitignore
├── compose.yaml
├── Dockerfile
├── requirements.txt
└── README.md

1. 示例程序

app/main.py

python 复制代码
def main() -> None:
    print("Hello from Python and Docker")


if __name__ == "__main__":
    main()

requirements.txt

text 复制代码
pytest==8.3.5

实际项目中更推荐使用明确版本或锁文件,避免今天构建和下个月构建得到不同的依赖组合。

2. 编写生产友好的 Dockerfile

dockerfile 复制代码
# syntax=docker/dockerfile:1

FROM python:3.12-slim AS builder

ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
    PIP_NO_CACHE_DIR=1

WORKDIR /build

RUN python -m venv /opt/venv

ENV PATH="/opt/venv/bin:$PATH"

COPY requirements.txt ./

RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt


FROM python:3.12-slim AS runtime

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PATH="/opt/venv/bin:$PATH"

WORKDIR /workspace

COPY --from=builder /opt/venv /opt/venv
COPY app ./app

RUN useradd --create-home --uid 10001 appuser \
    && chown -R appuser:appuser /workspace

USER appuser

CMD ["python", "-m", "app.main"]

这份 Dockerfile 做了几件实用的事:

  • 使用 python:3.12-slim,减少镜像体积
  • 先复制依赖文件,再复制源码,充分利用构建缓存
  • 使用多阶段构建,不把编译过程中的杂物带入运行镜像
  • 设置 PYTHONUNBUFFERED=1,让日志及时输出
  • 生产环境使用普通用户运行,而不是默认的 root
  • 使用 exec 格式的 CMD,便于容器正确接收终止信号

如果依赖中包含 psycopgnumpyPillow 等需要系统库的软件包,可以只在 builder 阶段安装编译工具,在 runtime 阶段保留必要的动态链接库。这样既能成功编译,也不会把整套编译环境塞进最终镜像。

3. 配置 .dockerignore

dockerignore 复制代码
.git
.github
.vscode
.devcontainer
.venv
venv
__pycache__
*.py[cod]
.pytest_cache
.mypy_cache
.ruff_cache
.coverage
htmlcov
dist
build
*.egg-info
.env
.env.*
tests
README.md

.dockerignore 可以减少发送给 Docker 的构建上下文,让构建速度更快,也能防止 .env、Git 历史和本地虚拟环境被误装进镜像。,


五、用 Compose 打通开发流程

compose.yaml

yaml 复制代码
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    working_dir: /workspace
    volumes:
      - .:/workspace
    command: python -m app.main
    environment:
      PYTHONUNBUFFERED: "1"

在项目目录运行:

bash 复制代码
docker compose up --build

停止服务:

bash 复制代码
docker compose down

重新构建:

bash 复制代码
docker compose build

后台运行:

bash 复制代码
docker compose up -d

查看日志:

bash 复制代码
docker compose logs -f app

进入容器:

bash 复制代码
docker compose exec app bash

运行测试:

bash 复制代码
docker compose run --rm app pytest

这里把项目目录挂载到 /workspace,修改代码后容器能立即看到变化。由于项目本身位于 WSL 的 Linux 文件系统中,这种 bind mount 通常比挂载 C:\... 下的项目更稳定、响应更快。,


六、进一步升级为 Dev Containers

当团队希望连 VS Code 扩展、解释器和终端环境都完全一致时,可以加入 .devcontainer/devcontainer.json

json 复制代码
{
  "name": "Python Docker Development",
  "dockerComposeFile": "../compose.yaml",
  "service": "app",
  "workspaceFolder": "/workspace",
  "overrideCommand": true,
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "charliermarsh.ruff",
        "ms-azuretools.vscode-docker"
      ],
      "settings": {
        "python.defaultInterpreterPath": "/opt/venv/bin/python",
        "python.testing.pytestEnabled": true,
        "python.testing.unittestEnabled": false,
        "editor.formatOnSave": true
      }
    }
  },
  "postCreateCommand": "python --version && pip --version"
}

在 VS Code 命令面板中执行:

text 复制代码
Dev Containers: Reopen in Container

重新打开后:

  • VS Code 终端位于容器内
  • Python 解释器使用 /opt/venv/bin/python
  • 调试和测试都在容器环境执行
  • 团队成员使用同一组扩展和配置
  • Windows 本机不必单独安装项目所需的 Python 版本

Dev Containers 的核心价值不是"把代码关进容器",而是把开发环境本身变成可版本管理的配置。这对于新成员入职、CI 故障复现和多项目切换尤其有帮助。, ,


七、本地开发与生产镜像要适当分开

开发环境追求热更新、调试便利和源码挂载,生产镜像追求小体积、安全和可重复部署。两者可以共享 Dockerfile,但运行方式不要完全混在一起。

开发阶段

bash 复制代码
docker compose up --build

特点包括:

  • 挂载源码
  • 开放调试端口
  • 安装测试工具
  • 允许热更新
  • 日志直接输出到终端

交付阶段

构建镜像:

bash 复制代码
docker build -t my-python-app:1.0.0 .

运行验证:

bash 复制代码
docker run --rm my-python-app:1.0.0

查看镜像:

bash 复制代码
docker image ls my-python-app

检查镜像历史:

bash 复制代码
docker history my-python-app:1.0.0

生产镜像中不要挂载本地源码,也不要把 .env 文件复制进去。数据库密码、令牌和证书应在部署阶段通过环境变量、Docker secrets 或云平台的密钥管理服务注入。

开发和生产共享同一套基础定义,但保留必要差异,往往比强行追求"一份配置统治所有环境"更可靠。,


八、Windows 环境最常见的几个坑

1. 项目放在 Windows 盘中

不推荐:

bash 复制代码
cd /mnt/c/Users/your-name/project

更推荐:

bash 复制代码
cd ~/projects/project

在 Windows 资源管理器中需要访问 WSL 文件时,可以输入:

text 复制代码
\\wsl$\Ubuntu\home\你的用户名\projects

日常编辑仍建议从 WSL 中执行 code .,不要频繁通过 Windows 文件系统工具批量操作 Linux 项目文件。,

2. Git 换行符变成 CRLF

Linux 容器通常期望 LF。可以在 WSL 中设置:

bash 复制代码
git config --global core.autocrlf input

项目根目录加入 .gitattributes

gitattributes 复制代码
* text=auto eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

否则 Shell 脚本进入容器后,可能出现这种颇具迷惑性的错误:

text 复制代码
/bin/sh^M: bad interpreter

3. Windows 和 WSL 各装一套 Git

代码放在 WSL 后,应优先使用 WSL 中的 Git:

bash 复制代码
which git
git --version

SSH 密钥也放在:

text 复制代码
~/.ssh/

设置权限:

bash 复制代码
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519

测试 GitHub 连接:

bash 复制代码
ssh -T git@github.com

这样能避免 Windows Git、WSL Git、不同凭据管理器之间互相"猜心思"。

4. 容器里写出的文件归 root

开发容器以 root 运行时,可能在挂载目录中生成 root 所有的文件。可采用以下办法:

  • 在镜像中创建普通用户
  • Dev Container 中配置 remoteUser
  • 让容器用户 UID 与 WSL 用户 UID 对齐
  • 避免容器向源码目录写入缓存和构建产物

查看 WSL 用户 UID:

bash 复制代码
id -u
id -g

团队项目可以通过 Dockerfile 的构建参数传入 UID 和 GID,不过 Windows + Docker Desktop 场景通常没有原生 Linux 那么容易出现严重权限冲突。

5. 修改代码后镜像仍是旧版本

如果 Dockerfile 先执行:

dockerfile 复制代码
COPY . .
RUN pip install -r requirements.txt

那么任何代码变更都会让依赖安装层失效。正确顺序应当是:

dockerfile 复制代码
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app ./app

缓存利用率的差别相当明显,尤其是科学计算和机器学习依赖较多时。

6. 容器访问宿主机服务失败

容器中的 localhost 指向容器自己,不是 Windows。

从容器访问 Windows 宿主机上的服务,可以尝试:

text 复制代码
host.docker.internal

例如 Windows 上的服务监听 8000 端口:

text 复制代码
http://host.docker.internal:8000

容器之间则应通过 Compose 服务名通信。例如数据库服务名为 db,应用应连接:

text 复制代码
postgresql://user:password@db:5432/database

而不是连接 localhost


九、推荐的日常工作流

一套稳定的操作节奏可以压缩成下面几步。

bash 复制代码
# 从 Windows Terminal 进入 WSL
wsl

# 进入项目
cd ~/projects/my-python-app

# 拉取代码
git pull

# 用 WSL 模式打开 VS Code
code .

# 启动开发容器
docker compose up --build

# 运行测试
docker compose run --rm app pytest

# 构建交付镜像
docker build -t my-python-app:$(git rev-parse --short HEAD) .

# 验证交付镜像
docker run --rm my-python-app:$(git rev-parse --short HEAD)

如果采用 Dev Containers,打开项目后执行 Reopen in Container,Python 解释器、测试工具和调试环境都会跟随项目自动加载。

最终可以记住这套组合:

WSL 文件系统存代码,VS Code Remote WSL 做编辑,Docker Compose 做开发编排,Dockerfile 做正式交付,Dev Containers 做团队环境统一。

它不会消灭所有环境问题------软件工程还没幸福到那个程度------但能把 Windows、Linux 和容器之间最常见的摩擦压到很低。, ,


参考资料

相关推荐
Nturmoils1 小时前
备份完不算完,先还原到临时库验一遍
后端
TELL5211 小时前
selenium webdriver 第二次初始化的异常
开发语言·python
Csvn2 小时前
📊 SQL 入门 Day 22:视图与物化视图
后端·sql
Csvn2 小时前
🐍 Day 4: Python 控制流 — 条件、循环与推导式的艺术
后端·python
2601_953720822 小时前
【计算机毕业设计】基于Vue与Spring Boot的高校兼职信息服务平台设计与实现
spring boot·后端·课程设计
再吃一根胡萝卜4 小时前
用 Rust 写一个桌面悬浮图标:为什么它比 Python 更适合 AI 桌面工具?
后端
IT_陈寒4 小时前
Vue的computed属性把我坑惨了,原来我一直用错姿势
前端·人工智能·后端
大鹏说大话4 小时前
从爬虫到决策引擎:大数据下自媒体如何用Python挖掘用户痛点
开发语言·爬虫·python
Niuguangshuo4 小时前
silero-vad:超轻量级开源 VAD 实践指南
开发语言·python