在 Win11 下使用 docker 构建服务器开发环境

Last Update: @2020/5/12

🏠 - Github 项目传送门

Note: 如果你没有 "正确" 使用互联网的手段, 可以站内私信联系我.

有很多服务器开发常用的包都是 Linux 特供的 (例如 Gunicorn), 因此要在 Windows 下开发服务器环境, 虚拟机是一项很常见的选择.

虽然在 Windows11 中, 巨硬发布的 WSL2 已经支持了 docker 的使用, 但是 WSL2 有几项致命的缺陷 (我已经亲身体验过了):

  1. 跨系统文件 IO. WSL2 和 Windows 本系统之间使用网络协议通信, 假设你把待编译的文件放在 Win 中, 编译工具链放在 WSL2 中, 编译速度将会大幅度的下降. 这意味着你必须将文件和工具链同时放在 WSL2 中.

    使用 VSCode 的 WSL2 Remote 插件可以一定程度上解决这个问题. 但是 JetBrains 全家桶虽然目前支持了使用 WSL2 的工具链, Remote 功能仍然不完善, 让我异常难受.

  2. 网络. WSL2 使用了类似虚拟机的技术, 这意味着 WSL2 与本机并不共享 localhost, 以及其他更多的网络问题. 当你开发一些需要配置网络环境的项目, 或着挂 VPN 时, WSL2 会有很多令人困惑的表现.

    网络上有很多各种各样的 bug 解决方案, github 上也有不少和网络相关的 issue. 但我始终都没有彻底地解决问题.

  3. 与 Virtual Box 等虚拟机的不兼容问题. 这意味着 WSL2 与虚拟机不可得兼, 而虚拟机是更加成熟, 有更多资料可供查询的解决方案.

    Virtual Box 6.0 以上的版本似乎已经支持了与 WSL2 的兼容.

不过, WSL2 纵然有这么多问题... 还是想试试在上面部署 docker, 顺便记录一下遇到的坑.

!!! 注意 !!! 本文不是一篇教程, 不建议完全跟随操作, 应当先阅读全文, 因为完全存在我尝试失败, 或着后面的操作撤销了前面的操作的可能性.

如果你不需要了解项目的构建细节, 而只是想快速地启动 docker 服务器, 可以参考 4. 一键配置指南 .

0. 环境前提

  1. Windows11
  2. PowerShell 7.2.3
  3. 可访问 Github, 且已安装 Git 指令

1. 配置 WSL2

  • 在设置中的 系统 > 存储 > 保存新内容的地方 修改 Windows 应用的默认存储位置, 避免大量应用全部堆到 C 盘中去.

  • 打开 Microsoft Store, 搜索 Ubuntu,下载 Ubuntu 20.04.4 LTS.

  • 在设置中的 应用 > 可选功能 中选择 更多 Windows 功能 , 启用 适用于 Windows 的 Linux子系统虚拟机平台 功能, 然后重新启动.

  • 使用管理员权限打开 PowerShell, 执行指令 wsl --update ,再执行 wsl --shutdown 重启 WSL 服务.

  • 修改 WSL 存储路径, 参考链接 , 接下来的操作在 WSL 命令行中执行.

  • 跟随提示设置用户名和密码, 进入系统后再设置 root 权限的密码, 指令是 sudo passwd -u root .

    如果忘记了 WSL 的密码, 可以使用管理员权限打开 PowerShell, 执行 wsl.exe --user root 来强制进入 root 用户, 再使用指令 passwd [username] 来修改密码即可.

  • 更换国内镜像源 参考链接 , 下载网络应用常用的工具包 sudo apt install net-tools .

2. 配置 Docker

官方文档暨下载地址传送门: Install Docker Desktop on Windows

  • 在安装之前, 提前设置 Docker 默认下载位置的软连接, 以防 C 盘爆炸.

用管理员权限打开 PowerShell, 执行指令:

$PS = "Program Files"
New-Item -ItemType SymbolicLink `
         -Path C:\$PS\Docker `
         -Target D:\Docker

你可以直接在文件管理器中看到创建的符号链接 (Symbolic Link, 你可以简单的理解为快捷方式), 并且在需要的时候直接删除. 这不会影响到被链接的位置.

  • 打开安装程序, 安装完成后删除安装包.
  • 替换镜像源, 设置日志文件的上限以防挤占磁盘空间. 在设置的 Docker Engine 条目的大括号中增加下列内容.
  "registry-mirrors" : [
    "https://docker.mirrors.ustc.edu.cn",
    "https://registry.docker-cn.com"
  ],
  "debug": true,
  "log-level": "warn",
  "log-driver": "json-file", 
  "log-opts": { 
    "max-size": "100m", 
    "max-file": "3"
  },

输入指令 docker info 以检查 docker 安装和镜像源设置. 对于该指令弹出的警告, 可以忽略.

由于我们是在 WSL 中运行 docker, 因此只要 WSL 不在 C 盘中, 镜像就不会挤占 C 盘的空间.

附: 📕 - Docker 中文手册 .

3. 制作服务器镜像

现在 docker 已经配置完成, 接下来测试服务器开发环境, 最后用 Dockerfile 构建镜像.

  • 下载 Ubuntu 镜像: docker pull ubuntu
  • 按顺序测试 docker 指令.
docker run -it -d --name ubuntu_server -p 8080:80 -p 8050:5000 `
  -v [route]/docker_server_helloworld:/mnt/docker_server_helloworld ubuntu
docker ps
netstat -aon|findstr "0.0.0.0:8080"

👆创建容器, -d 设置容器在后台运行, -p [local]:[container] 绑定端口. clone 本文开头提到的远程仓库 docker_server_helloworld , 用 -v 选项挂载到容器中, [route] 是本地仓库的路径, 同时注意容器中的对应路径必须在 /mnt 之下.

比如说, 我把文件夹克隆到了 D 盘下, [route] 就是 //d . 参考链接

列出所有正在运行的容器. 如果要列出关闭的容器, 应当执行 docker ps -a, 最后使用 netstat 检查端口监听情况.

docker exec -it ubuntu_server /bin/bash

👆在后台正在运行的 ubuntu_server 中执行指令 /bin/bash , 这可以打开一个 bash 命令行. 该命令行与 Ubuntu 下的 bash 命令行没有差别, 因此执行 exit 同样可以退出.

exit

👆退出 bash 命令行.

docker stop ubuntu_server
docker ps -a
docker start ubuntu_server
docker ps

👆停止后台正在运行的容器, 检查停止状态 (即 STATUS 标签), 再启动关闭的容器.

如果要直接重启, 可以使用 docker restart [CONTAINER] 的指令.

与上面的 WSL 一样, 这里也需要用一样的方法配置镜像源. 但是初始镜像出于精简起见没有 vi 指令, 导致问题有点麻烦. 🎉 解决方案

更新镜像源后, 进入 docker 容器, 更新包管理工具.

sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
apt-get clean
apt-get update
apt-get upgrade

3.1. Python 环境

  • 安装 Python 3.9, 使用 VirtualEnv 作为 Python 的环境管理工具. 我已经预先在仓库中准备了 get-pip.py 文件, 以避免访问外网. 这里设置一个临时的镜像源, 用来下载 pip.
apt-get install python3.9
apt-get install python3-distutils
MIR=https://pypi.tuna.tsinghua.edu.cn/simple
python3.9 /mnt/docker_server_helloworld/build/get-pip.py -i $MIR
# ↑ 这会报一个建议不要在 root 用户下安装 python 的 warning, 无关紧要

下载过程中可能会要求给出地理位置以确定时区, 分别输入 670 设置为 Asia 和 Shanghai 即可.

  • 根据仓库的 requirements.txt 配置 Python 虚拟环境, 这里有可能需要重启 bash 才能找到刚刚安装的 pip 指令. 另外, 这里也需要换源 (国内真是好麻烦).
pip install virtualenv -i $MIR
virtualenv /home/venv
source /home/venv/bin/activate
pip install --upgrade pip -i $MIR
pip install -r /mnt/docker_server_helloworld/requirements.txt -i $MIR
  • 启动 Flask Helloworld: python run.py , 能看到 Flask 正常运行, 使用 ctrl + c 退出.

3.2. 用 Supervisor 守护 Gunicorn

  • 安装 Supervisor 和 Gunicorn 运行 eventlet 需要的 netbase包, 使用仓库中的配置文件覆盖原本的 supervisor 配置文件, 最后启动服务.
apt-get install supervisor
apt-get install netbase
NET=/home/docker_server_helloworld/network
cp -f $NET/supervisor/supervisord.conf /etc/supervisor/
supervisord -c /etc/supervisor/supervisord.conf
supervisorctl reread
supervisorctl update
supervisorctl start all

进入 WSL2 的 bash, 由于我们前面下载了 net 工具包, 这里执行 ifconfig -a 指令后应该会给出 IP 地址相关的信息. 这里找到 eth0 项中的 inet.

注意, 这个 IP 每次重启 WSL 都会变更!

你也可以在启动 WSL 命令行时看到 IP 信息, 即这个包并不是必要的, 但这是我后来发现的方法, 因此在这里我使用的仍然是 ifconfig 指令.

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.22.85.31  netmask 255.255.240.0  broadcast 172.22.95.255

这个时候在浏览器中输入 172.22.85.31:8050 , 应该能成功看到由 gunicorn 生成的 Hello World.

3.3. Nginx 配置

  • 下载 Nginx, 检查版本后启动.
apt-get install nginx
nginx -v
# nginx version: nginx/1.18.0 (Ubuntu)
/etc/init.d/nginx start
  • 在浏览器中输入 172.22.85.31:8080 , 规则与前文相同. 这个时候应该能成功显示出 nginx 的 Hello World 界面.

  • 输入 nginx -s stop 关闭服务, 再次在浏览器发起请求, 请求失败, 可见 nginx 确实成功与宿主机连接.
  • 用仓库中的设置覆盖初始 nginx 设置, 使 nginx 转发 80 端口的消息到 gunicorn 中.
cp -i $NET/nginx/nginx.conf /etc/nginx/
nginx -c /etc/nginx/nginx.conf
nginx -s reload
/etc/init.d/nginx start

这时再访问 172.22.85.31:8080 , 就可以看到和 Gunicorn 一样的 Hello World界面了.

至此, 所有的配置完成, 我已经在远程仓库中编写了整个过程的 Dockerfile, 可以使用它来构建镜像.

4. 一键配置指南

要直接配置 docker server, 你需要至少跟随本文执行到 2.0 配置Docker , 即在开始一键配置时, 你需要已经能够使用 docker 指令.

同时, 只要你所在的平台已经能够使用 docker 指令, 在非 windows 的环境下, 你也可以用这个镜像来快速开始服务器开发 -- 比如说, 你软件工程课的大作业为你提供了一台云服务器, 或者你的开发环境是 Mac.

  • 下载远程仓库, 进入仓库目录后执行指令构建镜像, 制作容器.
git clone https://github.com/Parallel-Paradox/docker_server_helloworld.git
cd [Where your repo is]\docker_server_helloworld
docker build -t docker_server:latest . 
docker run -itd --name test_c -p 8080:80 docker_server
  • 在 Windows 下需要打开 WSL 查看子系统 IP 地址 (虚拟机同理), 否则应该默认为宿主机的 IP 地址.

    假设我 WSL 的 IP 地址是 172.22.85.31 , 在浏览器中输入 172.22.85.31:8080 就可以看到 docker 发送给浏览器的 Hello World 了.

4.1. 进一步开发

仓库中的 run.py 是 Flask 框架的 helloworld, 如果你使用 Flask 框架, 你只需要保证 run.py 的文件名不变, 同时内容中的 flask 实例名为 app, 就可以不做任何修改.

python 虚拟环境位于 docker 中的 /home/venv 中, 项目使用了虚拟环境, 因此如果你需要增加 python 包, 你只需要替换 clone 后的仓库中的 ./build/requirements.txt 即可.

这样的更新需要重新 build docker 镜像, 你也可以使用 docker 的 exec 指令进入容器, 直接打开虚拟环境 install 你需要的包.

项目在我的渣机上 build 一次大概要花数百秒, 因此如果你有更复杂的开发需求, 我建议你阅读整个配置流程, 了解每个文件的作用后, 对生成的容器做更多的修改.

一种更简单的代码同步方法是在容器中下载 git 指令, 然后用 git 来做版本管理.

posted @   ParallelParadox  阅读(1091)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示