从Incus出发
在过去的一些文章里,我曾经着迷于Incus作为容器实现。
在我看来,LXC/Incus那一套共享内核同时分隔文件系统与命名空间的法则很有魅力,用来创建低开销的类虚拟机实例再适合不过了。
不过,Incus的局限也有不少,或许配置麻烦就是其中之一,需要用户学习一套全新的配置模式。
另一方面,尽管Incus已经实现OCI镜像支持,但它不直接兼容当下流行的Dockerfile + Docker Compose模式。
理论上,这两者可被Canonical生态的cloud-init脚本替代,Incus对此有非常好的支持,但改写这些脚本总归是一个调试繁琐、易出Bug的工作。
说到底,Incus更偏向于系统级容器的理念,本身就和Docker/Podman的应用级容器定位有很大差异。
如果仍然迷恋Docker风格的应用级容器,或许可以试试Podman,其绝大部分操作API或指令均和Docker相似,并默认运行在用户态,不依赖root权限级别的daemon。
安装
Podman的安装非常简单,在Debian下只需要:
|
|
在Windows下安装也非常简单,可参考Podman for Windows文档。基本步骤是:
- 前往Github Release,下载最新版
podman-installer-windows-amd64.exe文件(MSI也行;别被附件里几个不同名字的.exe骗了,它们的SHA256可能是相同的); - 开始安装,安装时可选HyperV或WSL后端,推荐WSL;
- 安装程序结束后,打开命令提示符,执行
podman machine init,自动下载宿主虚拟机/WSL镜像并导入
若是好奇podman machine init命令干了什么,可以翻阅上面的文档,内有非常详细的介绍。
常见问题
找不到podman.sock
/run/user/<uid>/podman/podman.sock No such file or directory
请尝试:
|
|
容器莫名其妙自动结束
如果是以下情况:用户在保持有登录会话(如SSH)时,容器从不自动结束。而在用户关闭所有会话后一小段时间内,容器就自动退出了。
那么请尝试:
|
|
Windows WSL Podman设置代理
若要让基于Windows WSL的Podman使用宿主机网络代理,通常要先确定宿主机的IP,再设置环境变量。
太麻烦了,让我们用一个脚本解决这个问题吧。
使用方法:
- 复制以下脚本
- 根据自己的网络配置修改
USER CONFIGURATION配置项 - 打开Podman WSL Machine,在WSL的
/etc/profile.d/文件夹下新建个配置文件,贴进去
WSL代理自动配置脚本
/etc/profile.d/wsl-proxy.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121#!/bin/bash # WSL Proxy Environment Setup Script # Place this script in /etc/profile.d/ to auto-load proxy settings for all users # Or source it manually # ============================================================================= # USER CONFIGURATION SECTION # Modify these variables according to your proxy server settings # ============================================================================= # Proxy protocol schema (http, https, socks4, socks5) # Default: http PROXY_SCHEMA="${PROXY_SCHEMA:-http}" # Proxy server hostname or IP address # Use "{GATEWAY}" to automatically detect Windows host IP via WSL gateway # PROXY_HOST="${PROXY_HOST:-localhost}" PROXY_HOST="{GATEWAY}" # Proxy server port number # Default: 8080 PROXY_PORT="${PROXY_PORT:-8080}" # Proxy authentication username (leave empty if not required) # Default: empty PROXY_USER="${PROXY_USER:-}" # Proxy authentication password (leave empty if not required) # Default: empty PROXY_PASSWORD="${PROXY_PASSWORD:-}" # Comma-separated list of addresses to bypass proxy # These domains/IPs will not use the proxy connection # Default: local addresses (localhost, 127.0.0.1, etc.) PROXY_BYPASS="${PROXY_BYPASS:-localhost,127.0.0.1,::1,169.254.169.254}" # ============================================================================= # INTERNAL FUNCTIONS # ============================================================================= # Detect WSL gateway IP address (Windows host IP) # This is useful when proxy server runs on Windows host _get_wsl_gateway() { local gateway_ip # Method 1: Use ip route to find default gateway gateway_ip=$(ip route show default 2>/dev/null | awk '/default/ {print $3}' | head -n1) # Method 2: Fallback to /etc/resolv.conf nameserver (WSL2) if [[ -z "$gateway_ip" ]] && [[ -r /etc/resolv.conf ]]; then gateway_ip=$(awk '/^nameserver/ {print $2; exit}' /etc/resolv.conf) fi # Method 3: Fallback to Windows host IP pattern (WSL1) if [[ -z "$gateway_ip" ]]; then gateway_ip=$(ip route show | grep -oP 'via \K[0-9.]+' | head -n1) fi echo "$gateway_ip" } # Build proxy URL with optional authentication _build_proxy_url() { local schema="$1" local host="$2" local port="$3" local user="$4" local password="$5" local url="${schema}://" # Add authentication if credentials provided if [[ -n "$user" ]]; then url+="${user}" if [[ -n "$password" ]]; then url+=":${password}" fi url+="@" fi # Add host and port url+="${host}:${port}" echo "$url" } # ============================================================================= # MAIN EXECUTION # ============================================================================= # Resolve special {GATEWAY} keyword to actual WSL gateway IP if [[ "$PROXY_HOST" == "{GATEWAY}" ]]; then PROXY_HOST=$(_get_wsl_gateway) # Validate gateway detection if [[ -z "$PROXY_HOST" ]]; then echo "Warning: Failed to detect WSL gateway IP. Using 'localhost' as fallback." >&2 PROXY_HOST="localhost" fi fi # Construct full proxy URL PROXY_URL=$(_build_proxy_url "$PROXY_SCHEMA" "$PROXY_HOST" "$PROXY_PORT" "$PROXY_USER" "$PROXY_PASSWORD") # Export standard proxy environment variables export HTTP_PROXY="$PROXY_URL" export http_proxy="$PROXY_URL" export HTTPS_PROXY="$PROXY_URL" export https_proxy="$PROXY_URL" # Export no-proxy settings export NO_PROXY="$PROXY_BYPASS" export no_proxy="$PROXY_BYPASS" # ============================================================================= # VERBOSE OUTPUT (optional) # Uncomment the following lines to enable debug output # ============================================================================= # echo "WSL Proxy Configuration Loaded:" # echo " HTTP_PROXY: $HTTP_PROXY" # echo " HTTPS_PROXY: $HTTPS_PROXY" # echo " NO_PROXY: $NO_PROXY"
为容器启用N卡
通常来说,只需要首先安装NVIDIA Container Toolkit,然后使用以下命令启动容器:
|
|
找不到GPU
Podman主要通过Container Device Interface (CDI)寻找调用系统中的显卡。
在安装NVIDIA Container Toolkit时,会附带一个Systemd服务nvidia-cdi-refresh.service,该服务理论上会在后台运行,自动查找系统中的N卡并刷新CDI信息。
可以通过以下命令查看N卡是否成功注册到CDI中,正常应该是这样的:
|
|
然而,有时这一服务无法正常运行(可通过sudo systemctl status nvidia-cdi-refresh.service查看),上述服务不能正确更新CDI中的N卡注册信息,最终会导致Podman无法找到我们想要启用的显卡资源。
解决方法是,可以手动运行CDI注册命令:
|
|
Podman Compose设置GPU
在docker-compose.yml文件中,可以这样定义使用GPU:
|
|
如果使用Docker Compose运行,可能会遇到错误:Error response from daemon: container create: stat nvidia.com/gpu=all: no such file or directory
解决方法非常简单:请使用Podman-Compose而不是Docker-Compose:
|
|