还是因为不够强,否则不会失业。
大家好我是大龄程序员码农阿峰。今年47周岁,79年的。我差点失业了,我的linux技能现在好像把我保住了。我的感觉是不够强。所以还要继续学。今天展示一下我跟AI学Ubuntu server2604搭建docker环境。
宠辱不惊,闲看庭前花开花落;去留无意,漫随天外云卷云舒。
附安装过程(附件在这里!)
#!/usr/bin/env bash
# 全新 Ubuntu 虚拟机:必须在 VirtualBox 本地控制台窗口执行。
# 此时系统还没有 SSH 服务,无法通过 ssh_run.py 远程下发本脚本。
set -e
sudo apt update
sudo apt install -y openssh-server
# 关键:设置开机自启(Ubuntu 服务单元名是 ssh,不是 sshd)
sudo systemctl enable ssh
sudo systemctl start ssh
# 也可以用一条命令替代上面两条:sudo systemctl enable --now ssh
# Ubuntu 24.04+ 的 socket 按需激活单元,保持启用即可
sudo systemctl enable ssh.socket 2>/dev/null || true
# 若启用了 ufw,放行 SSH(未启用时添加规则也无害)
sudo ufw allow OpenSSH || true
echo '--- 验证 ---'
systemctl is-enabled ssh
systemctl is-active ssh
sudo ss -ltnp | grep ':22' || true
ip -4 addr | grep inet
#!/usr/bin/env bash
# 全新 Ubuntu 虚拟机:必须在 VirtualBox 本地控制台窗口执行。
# 此时系统还没有 SSH 服务,无法通过 ssh_run.py 远程下发本脚本。
set -e
sudo apt update
sudo apt install -y openssh-server
# 关键:设置开机自启(Ubuntu 服务单元名是 ssh,不是 sshd)
sudo systemctl enable ssh
sudo systemctl start ssh
# 也可以用一条命令替代上面两条:sudo systemctl enable --now ssh
# Ubuntu 24.04+ 的 socket 按需激活单元,保持启用即可
sudo systemctl enable ssh.socket 2>/dev/null || true
# 若启用了 ufw,放行 SSH(未启用时添加规则也无害)
sudo ufw allow OpenSSH || true
echo '--- 验证 ---'
systemctl is-enabled ssh
systemctl is-active ssh
sudo ss -ltnp | grep ':22' || true
ip -4 addr | grep inet
set -e
echo "[1/6] Resolve newest available Portainer CE image tag"
IMAGE=""
for tag in 2.45.3 lts latest; do
echo "--- trying portainer/portainer-ce:${tag}"
if docker pull "portainer/portainer-ce:${tag}"; then
IMAGE="portainer/portainer-ce:${tag}"
break
fi
done
if [ -z "$IMAGE" ]; then
echo "ERROR: no usable Portainer CE image tag"
exit 1
fi
echo "IMAGE=${IMAGE}"
echo "[2/6] Image metadata"
docker image inspect "$IMAGE" --format 'id={{.Id}} created={{.Created}} labels={{json .Config.Labels}}'
echo "[3/6] Create persistent volume"
docker volume create portainer_data
echo "[4/6] Start Portainer container"
docker rm -f portainer >/dev/null 2>&1 || true
docker run -d \
-p 8000:8000 \
-p 9443:9443 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
"$IMAGE"
echo "[5/6] Wait for HTTPS UI on 9443"
OK=0
for i in $(seq 1 45); do
code=$(curl -ksS -m 3 -o /dev/null -w '%{http_code}' https://127.0.0.1:9443/api/status 2>/dev/null || true)
if [ "$code" = "200" ]; then
OK=1
echo "UI ready (HTTP 200 after ${i} checks)"
break
fi
sleep 2
done
if [ "$OK" != "1" ]; then
echo "WARN: UI not responding yet, check docker logs portainer"
fi
echo "[6/6] Final status"
docker ps --filter name=portainer --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
echo "--- /api/status ---"
curl -ksS https://127.0.0.1:9443/api/status || true
echo
echo "--- recent container logs ---"
docker logs portainer 2>&1 | tail -25
目录
[0. 先装 SSH,再配端口转发](#0. 先装 SSH,再配端口转发) [1. 登录并体检虚拟机](#1. 登录并体检虚拟机) [2. 安装 Docker Engine](#2. 安装 Docker Engine) [3. 自启 / 免 sudo / 加速](#3. 自启 / 免 sudo / 加速) [4. 验证 Docker](#4. 验证 Docker) [5. 部署 Portainer CE](#5. 部署 Portainer CE) [6. 打通宿主机 9443](#6. 打通宿主机 9443) [7. 首次初始化 Web UI](#7. 首次初始化 Web UI) [8. 常用运维命令](#8. 常用运维命令) [9. 踩坑与解决记录](#9. 踩坑与解决记录) [10. 本次结果快照](#10. 本次结果快照)
实操记录 · 2026-11-12
Ubuntu 26.04 安装 Docker 与 Portainer CE
从一台全新安装、默认没有 SSH 的 Ubuntu 虚拟机开始:先在 VirtualBox 本地控制台装好 OpenSSH 并设为开机自启,再经 NAT 端口转发连入,安装 Docker Engine 并部署 Portainer 社区版;所有命令均在本次环境中验证通过。
系统:Ubuntu 26.04.1 LTS (resolute) x86_64 SSH:127.0.0.1:2222 → 10.0.2.15:22 Docker 29.7.2 Portainer CE 2.45.3 LTS Web UI:https://127.0.0.1:9443
0全新虚拟机:先装 SSH,再配端口转发
最容易漏掉的前提: 刚装好的 Ubuntu(尤其 Desktop 版,或安装 Server 时未勾选 OpenSSH)默认没有 SSH 服务 ,22 端口无人监听。此时就算配好端口转发,宿主机连接也只会报 Connection refused。所以第一步必须在 VirtualBox 虚拟机窗口里,用安装系统时创建的账户本地登录操作;装好并启用 SSH 之后,才能进行后面所有远程步骤。
0.1 在虚拟机本地控制台安装 OpenSSH 并设为开机自启
虚拟机网卡是 NAT,安装系统时即可联网,直接在虚拟机窗口内执行:
VirtualBox 虚拟机窗口内执行(不是宿主机)复制
sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable ssh # 关键:设置开机自启
sudo systemctl start ssh # 立即启动服务
# 以上两条可合并为:sudo systemctl enable --now ssh
sudo ufw allow OpenSSH # 若启用了 ufw,放行 22 端口
sudo ufw status verbose
systemctl is-enabled ssh # 期望输出 enabled
systemctl is-active ssh # 期望输出 active
sudo ss -ltnp | grep ':22' # 期望看到 0.0.0.0:22 监听
ip -4 addr # 确认 NAT 地址为 10.0.2.15
三个细节: ① Ubuntu / Debian 的 SSH 服务单元名是 ssh(ssh.service),不是 sshd;systemctl enable ssh 不能省,否则虚拟机重启后 SSH 不会自动起来。② Ubuntu 24.04 起另有 ssh.socket 按需激活单元,可用 sudo systemctl enable --now ssh.socket 启用;本次实测 Ubuntu 26.04 上 ssh.service 与 ssh.socket 同时 enabled + active、22 端口正常监听,两者并不冲突。③ 本次环境 ufw 为 inactive,无需放行;若你的机器上 ufw 是 active,务必执行 sudo ufw allow OpenSSH(或 sudo ufw allow 22/tcp)。
同目录下的 install_ssh.sh 是这段命令的配套脚本,可在虚拟机本地窗口执行;它不能 通过 ssh_run.py 下发,因为此刻还没有 SSH 通道。
0.2 配置 NAT 端口转发
客户机 IP 为 10.0.2.15,宿主机不能直接访问;只有 0.1 的 SSH 服务在 22 端口监听后,转发规则才有意义。虚拟机关机状态下,在「设置 → 网络 → 网卡 1 (NAT) → 端口转发」添加 SSH 规则:
| 名称 | 协议 | 主机 IP | 主机端口 | 子系统 IP | 子系统端口 |
|---|---|---|---|---|---|
| Rule 1 | TCP | 127.0.0.1 | 2222 | 10.0.2.15 | 22 |
等价的命令行写法(宿主机 PowerShell,需关机状态):
宿主机 PowerShell / CMD复制
& "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifyvm ubuntuserver --natpf1 "Rule 1,tcp,127.0.0.1,2222,10.0.2.15,22"
说明: 开机后先用 ssh -p 2222 afeng124@127.0.0.1 测试连通,通了再往下走。本次环境这条 2222 规则已存在,直接可用;后面第 6 步再补一条 9443 给 Portainer 使用,而且可以在虚拟机运行中动态添加。
1登录并体检虚拟机
确认第 0 章的 SSH 服务已启动、端口转发已配置后,用任意 SSH 客户端(MobaXterm / Windows Terminal 均可)连接,密码在交互提示时输入,不要写进脚本或文档:
宿主机终端复制
ssh -p 2222 afeng124@127.0.0.1
进入系统后先确认版本、资源和网络可达性,这决定了后面用哪个软件源:
虚拟机内执行复制
head -6 /etc/os-release
uname -m
free -h
df -h /
sudo -n true 2>/dev/null && echo "sudo 免密" || echo "sudo 需要密码"
for u in https://download.docker.com/linux/ubuntu/gpg \
https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg \
https://archive.ubuntu.com/ubuntu/ ; do
echo "$(curl -sS -m 8 -o /dev/null -w '%{http_code}' "$u" 2>/dev/null) $u"
done
本次实测的关键结论: download.docker.com 与 hub.docker.com 连接被重置(HTTP 000),但阿里云的 docker-ce 镜像仓库、GPG 密钥和 Ubuntu 源都返回 200。因此 Docker 官方 CE 包走阿里云镜像站,并额外配置容器镜像加速器。
另外,Ubuntu 24.04 起 APT 源改为 deb822 格式,位于 /etc/apt/sources.list.d/ubuntu.sources;用 grep /etc/apt/sources.list 查不到内容是正常现象,不代表源丢失。
2安装 Docker Engine(官方 CE 仓库 + 阿里云镜像)
2.1 安装依赖并导入 Docker 官方 GPG 密钥
虚拟机内执行复制
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg apt-transport-https
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL --retry 3 https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg -o /tmp/docker-repo-key
sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg /tmp/docker-repo-key
sudo chmod a+r /etc/apt/keyrings/docker.gpg
rm -f /tmp/docker-repo-key
2.2 添加 docker-ce 仓库(自动匹配系统代号)
虚拟机内执行复制
. /etc/os-release
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu ${VERSION_CODENAME} stable" \
| sudo tee /etc/apt/sources.list.d/docker.list
sudo apt-get update
本次生成的源内容为:
/etc/apt/sources.list.d/docker.list复制
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu resolute stable
2.3 安装引擎与插件
虚拟机内执行复制
sudo apt-get install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
为什么不用系统自带的 docker.io? Ubuntu 源里虽然也有 29.1.3 版,但官方 CE 仓库能持续拿到最新安全更新,且 docker compose、buildx 插件版本配套更完整。本次共下载约 102 MB,受网络速率影响耗时约 5 分钟,属正常。
3配置开机自启、免 sudo 与镜像加速
虚拟机内执行复制
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
id $USER
新建 /etc/docker/daemon.json,配置容器镜像加速(本次实测 daocloud、1ms.run、xuanyuan.me 三个节点可用)和日志轮转:
/etc/docker/daemon.json复制
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
虚拟机内执行复制
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<'JSON'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
JSON
sudo systemctl daemon-reload && sudo systemctl restart docker
docker info --format 'server={{.ServerVersion}} driver={{.Driver}} cgroup={{.CgroupDriver}}'
重要: 加入 docker 组后,已打开的旧 SSH 会话不会立即生效 。请重新登录一次(或执行 newgrp docker),之后才能不加 sudo 直接运行 docker 命令。
**安全提示:**docker 组成员等价于拥有宿主机 root 能力,只把账号授予可信使用者;生产环境请勿把 2375/2376 或 docker.sock 暴露到网络。
4验证 Docker
普通用户新会话执行复制
docker version
docker compose version
docker buildx version
systemctl is-active docker && systemctl is-enabled docker
docker run --rm hello-world
docker pull alpine:latest && docker run --rm alpine:latest echo "alpine container OK"
本次实际输出摘要:
Docker Engine29.7.2
Docker Composev5.5.0
buildx / containerd0.36.1 / 2.3.3
存储 / Cgroupoverlayfs / systemd
服务状态active + enabled
测试容器hello-world、alpine 均成功
通过标准: docker run --rm hello-world 打印 "Hello from Docker!",且 docker pull alpine:latest 能下载成功 ------ 说明引擎、权限和镜像加速三条链路都正常。
5以 Docker 方式部署 Portainer CE
Portainer 社区版(Community Edition)以单个容器运行,通过挂载宿主机 /var/run/docker.sock 管理本机 Docker。GitHub 最新正式版为 2.45.3 LTS (2026-10-21 发布),本次直接固定该精确标签;官方文档默认使用滚动标签 portainer/portainer-ce:lts。
虚拟机内执行复制
docker volume create portainer_data
docker run -d \
-p 8000:8000 \
-p 9443:9443 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:2.45.3
| 端口 | 用途 | 本次处理 |
|---|---|---|
| 9443 | HTTPS Web UI / API,首次启动自动生成自签证书 | 已发布,供宿主机访问 |
| 8000 | Edge Agent 反向隧道,仅边缘计算场景需要 | 按官方命令一并发布 |
| 9000 | 旧版 HTTP 端口(仅兼容用途) | 未发布,减少暴露面 |
验证容器与版本:
虚拟机内执行复制
docker ps --filter name=portainer
curl -ksS https://127.0.0.1:9443/api/status
docker logs portainer 2>&1 | tail -20
本次返回: {"Version":"2.45.3","InstanceID":"..."},容器 Up,重启策略 always,数据卷 portainer_data 挂载于 /var/lib/docker/volumes/portainer_data/_data;Web 首页 HTML 含 data-edition="CE",确认是社区版。
6打通宿主机到 9443 的访问
VirtualBox 的 controlvm natpf1 可以在虚拟机运行中直接追加 NAT 转发规则,不必关机:
宿主机 PowerShell(VBoxManage)复制
& "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" controlvm ubuntuserver natpf1 "portainer9443,tcp,127.0.0.1,9443,10.0.2.15,9443"
& "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" showvminfo ubuntuserver --machinereadable | Select-String Forwarding
期望看到两条规则:
输出复制
Forwarding(0)="Rule 1,tcp,127.0.0.1,2222,10.0.2.15,22"
Forwarding(1)="portainer9443,tcp,127.0.0.1,9443,10.0.2.15,9443"
持久化确认: 本次已检查 D:\vm\ubuntuserver\ubuntuserver.vbox,新增规则确实写入了配置文件,虚拟机重启后仍然有效。若使用图形界面补规则,则需关机后在「设置 → 网络 → 端口转发」新增同样的 9443 条目。
也可以完全不改 VirtualBox,改用 SSH 本地隧道临时访问(与 NAT 规则二选一,避免主机端口冲突):
宿主机终端复制
ssh -p 2222 -L 9443:127.0.0.1:9443 afeng124@127.0.0.1
最后在宿主机浏览器打开 https://127.0.0.1:9443 ;自签证书会触发安全警告,选择「高级 → 继续访问」即可。本次宿主机实测 /api/status 与首页 / 均返回 HTTP 200。
7首次初始化 Portainer Web UI
- 首次启动会生成一次性 setup token,在虚拟机里查看:
虚拟机内执行复制
docker logs portainer 2>&1 | grep -A1 setup_token
- 把
setup_token=...后面的值粘贴到网页初始化页面。 - 创建管理员账号,密码至少 12 位;该账号拥有对宿主机 Docker 的完整控制权,请妥善保管。
- 环境选择 Local,即可进入仪表盘管理容器、镜像、卷、网络和 Stack。
**token 过期怎么办:**若打开页面时提示令牌失效或超时,重启容器会生成新令牌,重新取一次即可(数据不会丢失):
虚拟机内执行复制
docker restart portainer && sleep 5 && docker logs portainer 2>&1 | grep -A1 setup_token
8常用运维命令
| 目的 | 命令 |
|---|---|
| 确认 / 设置 SSH 开机自启 | systemctl is-enabled ssh / sudo systemctl enable ssh |
| 查看 Docker 服务 | systemctl status docker |
| 查看 Portainer 状态 / 日志 | docker ps --filter name=portainer / docker logs -f portainer |
| 重启 Portainer | docker restart portainer |
| 查看资源占用 | docker system df / df -h / |
| 升级 Docker 引擎 | sudo apt-get update && sudo apt-get install --only-upgrade -y docker-ce docker-ce-cli containerd.io |
| 升级 Portainer(数据保留) | docker stop portainer && docker rm portainer → docker pull portainer/portainer-ce:lts → 用第 5 步相同的 docker run 参数重建(仅改镜像标签) |
| 修改镜像加速 | 编辑 /etc/docker/daemon.json 后执行 sudo systemctl restart docker |
| 卸载 Portainer | docker rm -f portainer;确认不再需要数据时再 docker volume rm portainer_data |
升级要点: Portainer 的配置、账号与 Stack 定义都保存在 portainer_data 卷里,重建容器时只要卷名和挂载参数不变就不会丢数据;升级前建议在 Settings 里下载一次备份。
9踩坑与解决记录(本次实测)
| 现象 | 原因 | 处理方式 |
|---|---|---|
新装虚拟机 ssh -p 2222 ... 报 Connection refused / 连接超时 |
系统默认没有 openssh-server,或漏了 systemctl enable ssh 导致重启后服务未启动;ufw 为 active 时也可能拦截 22 |
在 VirtualBox 本地控制台执行 sudo apt install -y openssh-server 与 sudo systemctl enable ssh;ufw 开启时再执行 sudo ufw allow OpenSSH |
服务名写成 sshd 提示 Unit not found |
Ubuntu / Debian 的单元名是 ssh |
改用 sudo systemctl enable ssh / systemctl status ssh |
download.docker.com 返回 000 / 连接被重置 |
当前网络无法直连 Docker 官方下载站 | 改用阿里云 docker-ce 镜像站配置官方仓库,GPG 密钥也从该镜像获取 |
hub.docker.com 不通,但 docker pull 需要拉镜像 |
Registry 访问受阻 | 在 daemon.json 配置多个 registry-mirrors,实测 hello-world、alpine 均拉取成功 |
grep /etc/apt/sources.list 没有输出 |
Ubuntu 26.04 使用 deb822 格式 *.sources |
查看 /etc/apt/sources.list.d/ubuntu.sources,无需修复 |
| 官方仓库是否有 resolute(26.04)的包 | 新系统代号可能尚未同步 | 预检 dists/resolute/stable/binary-amd64/Packages.gz,确认有 29.x 版 docker-ce 后再安装 |
| 加入 docker 组后仍提示 permission denied | 旧会话的用户组缓存未刷新 | 重新登录 SSH,或执行 newgrp docker |
| 浏览器提示证书不安全 | Portainer 9443 使用自签证书 | 本机环境选择继续访问;生产环境可在 UI 的 SSL 设置中换成自有证书 |
| Portainer 初始化提示 token 无效 | 首次设置令牌有时效 | docker restart portainer 后重新取日志中的 setup_token |
| APT 下载 102 MB 耗时约 5 分钟 | 网络速率约 326 kB/s | 属正常,等待即可,勿中断 apt 进程 |
10本次结果快照与脚本清单
| 检查项 | 结果 |
|---|---|
| 操作系统 | Ubuntu 26.04.1 LTS (resolute) x86_64,3.3 Gi 内存 / 25 G 磁盘 |
| SSH 前置(实测) | openssh-server 1:10.2p1-2ubuntu3.8;ssh.service 与 ssh.socket 均 enabled + active,22 端口监听;ufw 为 inactive |
| Docker | 29.7.2(客户端与服务端一致),overlayfs,cgroup systemd,active + enabled |
| 插件 | Compose v5.5.0、buildx 0.36.1、containerd.io 2.3.3 |
| 权限 | afeng124 在 docker 组,新会话免 sudo |
| 镜像加速 | 3 个节点写入 /etc/docker/daemon.json,日志轮转 100m × 3 |
| Portainer | CE 2.45.3 LTS,容器 portainer,卷 portainer_data,restart=always |
| Web UI | https://127.0.0.1:9443(宿主机 HTTP 200,/api/status 返回 2.45.3) |
以下脚本已与本文档放在同一目录 D:\work\2026\ubuntu_2604\,可按需复用:
- install_ssh.sh --- 全新虚拟机本地控制台安装并启用 SSH(须在 VirtualBox 窗口内执行,不能经
ssh_run.py下发) - ssh_run.py --- 通过口令登录虚拟机并远程执行脚本(支持
--sudo、--file) - probe_vm.sh / probe_sources.sh / probe_repo.sh --- 环境、网络、仓库预检
- install_docker.sh / configure_docker.sh / verify_docker.sh / final_check.sh --- Docker 安装、配置与验证
- portainer_probe.sh / install_portainer.sh --- Portainer 端口预检与部署
用 ssh_run.py 在本目录复现后续流程(口令通过环境变量临时提供,不写入任何文件)。注意:第 0 章的 SSH 安装必须先在虚拟机本地控制台完成,否则下面第一条命令也连不上:
宿主机终端(先进入 D:\work\2026\ubuntu_2604)复制
VM_PASS='登录口令' python ssh_run.py --file probe_vm.sh
VM_PASS='登录口令' python ssh_run.py --sudo --file install_docker.sh
VM_PASS='登录口令' python ssh_run.py --sudo --file configure_docker.sh
VM_PASS='登录口令' python ssh_run.py --file verify_docker.sh
VM_PASS='登录口令' python ssh_run.py --file install_portainer.sh
文档安全说明: 虚拟机登录口令与一次性 setup token 均未写入本文档,请通过交互输入或 docker logs 现场获取。 整理于 2026-11-12 · 基于 VirtualBox + Ubuntu 26.04.1 LTS 实机操作记录








