InstaBox + Docker
常用指令速查表

我的机器 my-machine

记不住没关系,记住套路即可。手机上随手查:点任意命令旁的「复制」就能粘贴使用,顶部搜索框可以按关键词过滤。

🧩

命令的拼法套路:工具 子命令 对象 参数 —— 先想清楚「对什么对象、做什么动作」,命令基本就拼出来了。任何命令后面加 --help,就是它自己的说明书。

没有匹配的命令,换个关键词试试,或点 ✕ 清除搜索。
01

命令是怎么拼出来的

以这条为例,拆开就是四层。看懂一条,其余的都是同一个套路。

ANATOMY
npx instabox@latest exec my-machine -- docker ps
│   │               │    │                   │  └─ 在机器里真正执行的命令
│   │               │    │                   └──── 分隔符:后面是“给机器的命令”
│   │               │    └──────────────────────── 目标机器名
│   │               └───────────────────────────── 动作:在机器里执行命令
│   └───────────────────────────────────────────── instabox 这个工具
└───────────────────────────────────────────────── npx:临时下载并运行这个工具
npx
临时下载并运行这个工具,免安装
instabox@latest
工具本体,@latest 表示用最新版
exec
动作:在机器里执行命令
my-machine
目标机器名,对哪台机器做这个动作
--
分隔符:后面是「给机器的命令」,不再是 instabox 自己的参数
docker ps
在机器里真正执行的命令
💡

规律:先想清楚「对什么对象、做什么动作」,命令基本就拼出来了。

02

InstaBox 常用命令

在本机 PowerShell 里跑。全局安装后不用每次加 npx instabox@latest 前缀:npm install -g instabox。

命令作用
instabox ls看我的机器和状态
instabox ssh <机器名>进入机器的 Shell
instabox exec <机器名> -- <命令>不进 Shell,直接在机器里跑一条命令
instabox create --name N新开一台机器
instabox suspend <机器名> / resume暂停 / 恢复
instabox stop <机器名> / start停机 / 开机
instabox rm <机器名>删除机器(快照不删)
instabox port publish <机器名> <端口>把端口变成公开 HTTPS 地址
instabox port unpublish <机器名> <端口>关闭公开地址
instabox cp [-p] 本地文件 <机器名>:/路径传文件进机器(反过来是拉出来,-p 自动建目录)
instabox snapshot save <机器名> --name N存快照(会先停机,文件保留)
instabox snapshot ls看已有快照
instabox usage看额度与用量
🔑

为什么 instabox ssh 机器名 一句话就能进:普通 SSH 要自己备齐地址、端口、用户名、密钥;instabox 用你本机的登录态向服务器证明身份,服务器确认权限后发临时凭证,CLI 在底下替你把真正的 SSH 连接拨好。方便的代价是信任全押在登录态上 —— 本机别借人乱用,登录态和 API key 按密码级别保管。

💻

换一台电脑登录:账号跟着人走,不跟电脑走。新电脑装好 Node.js 后跑 npx instabox@latest,按提示打开网址、用当初注册的同一个账号输设备码确认;再 npm install -g instabox 装全局版,新开窗口用 instabox ls 验证即可。机器和快照都在账号名下,换电脑不丢;若手滑登成新账号会看到空账号,退出来换回原账号即可。

03

Docker 常用命令

在机器里跑(先 instabox ssh 进去)。主线记住一条链:写配方(Dockerfile)→ 产镜像(build)→ 开容器(run / compose up)。

容器

命令作用
docker ps正在运行的容器
docker ps -a所有容器(含已停止)
docker run -d --name 名字 -p 宿主机端口:容器端口 镜像名后台启动一个容器
docker run hello-world官方问候容器(测试安装用)
docker logs 名字看容器输出日志(-f 持续跟踪,Ctrl+C 退出跟踪,容器不受影响)
docker exec -it 名字 bash进入正在运行的容器内部(-it = 给一个能打字的终端)
docker stop 名字 / docker start 名字停止 / 启动容器
docker restart 名字重启容器
docker rm 名字删除容器(先 stop,-f 强制)
docker stats看各容器 CPU / 内存占用(Ctrl+C 退出)
⚠️

在容器里改的文件只活在这个容器身上,容器一删就没,镜像不变。想把改动存下来 → 写进 Dockerfile 做成镜像;想让数据在容器删除后还活着 → 用数据卷(volumes)。清理顺序永远是先删容器、再删镜像。

镜像

命令作用
docker images本地镜像列表
docker pull 镜像名下载镜像(默认从 Docker Hub 拉)
docker build -t 名字:标签 .按当前目录 Dockerfile 打镜像(最后的 . = 配方在当前目录,别漏)
docker tag 旧名 新名给镜像再起一个名字(推送前改名用)
docker push 镜像名把镜像推到仓库
docker rmi 镜像名删除镜像(被容器引用时先删容器);只想腾磁盘时才删,留着下次 run 不用重下

镜像默认从 Docker Hub(hub.docker.com)拉:nginx 的完整名其实是 docker.io/library/nginx,library 是官方命名空间。其他仓库还有 GitHub 的 ghcr.io、Google 的 gcr.io,企业内部常用自建 Harbor。养成 -t 名字:版本号 的习惯(如 mynginx:v1),以后才分得清哪版是哪版。

Compose 与清理

命令作用
docker compose up -d按 compose.yaml 一次拉起一组服务
docker compose down停掉并清掉这组服务(镜像不删)
docker compose ps / logs看这组服务的状态 / 日志(一组一起看)
docker compose exec 服务名 命令在组内某个服务里执行命令,如 exec cache redis-cli ping(返回 PONG = 活着)
docker system df看 Docker 占了多少磁盘
docker system prune清理无用容器 / 网络 / 镜像(会问确认,慎用)

compose 自动帮你办的事:建专属网络、服务之间用服务名互访(web 连缓存直接用 cache:6379,不用查 IP)、容器统一命名、一条 up 全起、一条 down 全清。看 ps 输出时注意区别:0.0.0.0:8080->80 是对外映射了;只有 6379/tcp 表示只在组内互通、没暴露给宿主机 —— 数据库类服务就该这么藏着。

概念对照

镜像image
= 模板(像安装光盘)
容器container
= 用模板跑起来的实例(像装好并开机的系统)
端口映射-p 8080:80
= 把机器的 8080 端口转发进容器的 80 端口,格式是 外:内
判断口诀rule
定制单个服务的内容 → 改 Dockerfile;协调多个服务的协作 → 改 compose.yaml
🚨

一条红线:日志里 Redis 那句 does not require authentication 是在提醒它没密码。练习时没映射端口、外面连不进来,没事;但永远不要把没密码的 Redis 端口 publish / 映射到公网,这是真实世界里被入侵的重灾区。另外,容器日志时间是 UTC,比本地晚 8 小时,对时间点时别被绕晕。

04

不知道命令时怎么办

按这个顺序来,基本不用死记硬背。

  1. 命令 --help,如 docker run --help、instabox port --help —— 最快
  2. 命令 子命令 --help,逐层缩小
  3. 官方文档:instabox.sh/docs、docs.docker.com
  4. 直接把报错原文贴给 Muse,让它拆给你听 —— 看报错、改拼写(如 cat 网址 应为 curl、dockr 是拼写错误)就是日常排障的常态
📂

什么时候要关心当前目录:只有命令里出现相对路径时才需要 —— 比如 docker build . 的那个点、docker compose up 要找当前目录的 compose.yaml。纯粹的 run / tag / push / pull 跟 daemon 打交道,在哪个目录敲都一样;回家目录用 cd ~(即 /data/home)。

05

InstaBox 这台机器的注意点

  • 家目录在 /data/home,重要文件放 /data 下,快照才会带走
  • 免费档空闲 30 分钟自动暂停;恢复后没设重启策略的 Docker 容器不会自动起来,用 docker start 拉回
  • port publish 的地址是公开的、无鉴权,练习完记得 unpublish
  • instabox exec 机器名 -- 命令 有个怪癖:命令里带 -a 这类参数会被它误解析成自己的参数而报错。两个绕法:命令用引号包成一整串 -- 'docker ps -a',或者直接 instabox ssh 进去敲(练 Docker 时推荐后者)
🌐

publish 的地址能 CNAME 到 Cloudflare 提速吗?不能。那串子域名本身编码了「哪台机器、哪个端口」,网关靠它认人路由,换成你的域名它不认识,大概率直接报 “This URL is not published”;而且 CNAME 只是 DNS 别名,解析到的还是同一批 IP、同一条线路,本身不加速。这类地址是临时调试入口,机器暂停就失效,天生不适合长期绑域名。绑域名长期跑的服务,应该放在自己能控制的机器上(如 Oregon 的 VPS)再走 Cloudflare。

06

Dockerfile 速记

管「一个镜像怎么做出来」,是构建期的事:docker build 从上到下逐行执行,每执行一条就给镜像叠一层。文件名就叫 Dockerfile,没有扩展名。

DOCKERFILE · 我的第一份(~/docker-lesson3)
FROM nginx:latest                                  # 基础镜像(第一行)
COPY index.html /usr/share/nginx/html/index.html   # 拷文件进镜像
指令干什么
FROM指定基础镜像,必须是第一行(构建时)
COPY把文件拷进镜像(构建时)
RUN构建时执行一条命令,如装软件
WORKDIR设定工作目录
ENV设定环境变量(构建时定下、运行时生效)
EXPOSE声明这个镜像监听哪个端口(只是声明,不等于对外发布)
CMD容器启动时默认执行什么(运行时)
⚡

层与缓存:每条指令是一层,层带缓存。只改了 index.html 重新 build,只有 COPY 那层重做,前面的层直接复用,所以第二次特别快。经验法则:不常变的写前面(装依赖),常变的写后面(拷代码)。

compose.yaml 对照

管「一组容器怎么一起跑」,是运行期的事。它不关心镜像内部怎么做,只描述要哪几个服务、用什么镜像、端口怎么映射、谁和谁连。

COMPOSE.YAML · 第四课(~/docker-lesson4)
services:
  web:                    # 服务名,同时也是组内的“通讯录名字”
    image: mynginx:v1     # 用哪个镜像(也可写 build: 现场按 Dockerfile 构建)
    ports:
      - "8080:80"         # 对外映射
  cache:
    image: redis:latest   # 没写 ports,所以只在组内互通
  • 常见字段:volumes(挂数据)、environment(传环境变量)、restart(自动重启策略)、depends_on(声明启动顺序依赖)
  • 起 compose 前先看端口有没有被别的容器占着(如 8080 被 web3 占着时,先 docker stop web3 && docker rm web3)
  • ✓进阶 · Zephyr 镜像:官方镜像先跑通 → 自制瘦镜像(只装 ARM 工具链)→ 编出 hello_world 的 zephyr.bin(待实操)
07

私有镜像仓库(registry)实操流程

镜像默认从 Docker Hub 拉,也可以自己搭一个私有仓库:仓库本身就是个官方镜像。完整闭环是 搭仓库 → 推镜像 → 删本地 → 从仓库拉回来跑。这课对当前目录没要求,在哪敲都行。

BASH · 机器内
# 1. 起仓库(-v 让仓库数据存进 /data,容器删了镜像还在)
docker run -d -p 5000:5000 -v /data/home/registry-data:/var/lib/registry --name registry registry:2
curl http://localhost:5000/v2/            # 返回 {} = 活着
curl http://localhost:5000/v2/_catalog    # 看仓库里有哪些镜像

# 2. 推送:名字第一段带“仓库地址”才知道往哪推
docker tag mynginx:v1 localhost:5000/mynginx:v1
docker push localhost:5000/mynginx:v1
curl http://localhost:5000/v2/mynginx/tags/list   # 看某个镜像的标签

# 3. 拉回:删本地 → 从仓库 pull → digest 指纹一致才算真闭环
docker pull localhost:5000/mynginx:v1
  • 没带仓库前缀的名字(如 mynginx:v1)只是本地名,哪也推不了;docker pull nginx 默认去 Docker Hub。判断门道:名字第一段带不带 “.” 或端口,决定去哪个仓库
  • 想推到 Docker Hub 公共仓库同理:注册账号 docker login 后,docker tag mynginx:v1 你的用户名/mynginx:v1 再 docker push 你的用户名/mynginx:v1
  • 指纹(digest)认内容不认标签:镜像按内容算指纹存(内容寻址),push 的摘要、删除时的 ID、拉回后的 ID 一致(如 4c641273c0b7…),就保证一字节不差;标签能改名,指纹改不了
  • 仓库容器删掉没关系,数据在 /data/home/registry-data 里,下次同样命令再起仓库,镜像都还在 —— 这其实已经摸了一半数据卷
🔒

自建仓库默认无鉴权、走明文 HTTP:只在机器内部练,不要 publish 到公网。哪天想让别的机器来拉,先给它加鉴权和 HTTPS —— 不然等于把仓库敞开在公网上,跟没密码的 Redis 一个道理。

08

数据卷 volumes

让数据活得比容器久。容器自己的文件系统是临时的,存数据只有两种正经办法:

方式写法说明
命名卷-v mydata:/data数据存在 Docker 管的目录里(docker volume inspect 看 Mountpoint),适合数据库等只给容器用的数据
绑定挂载-v /data/home/目录:/data就是宿主机上明明白白的目录,适合网页、配置等要直接看和改的文件
BASH · 机器内
docker volume create mydata
docker run --rm -v mydata:/data alpine sh -c 'echo "saved in volume" > /data/note.txt'
docker run --rm -v mydata:/data alpine cat /data/note.txt   # 换个全新容器也能读到 = 数据在卷里,不在容器里
docker volume ls / inspect 名字 / rm 名字
💡

绑定挂载即时生效:宿主机改文件,容器里立刻变(实测首页 Version 1 → Version 2,没重打镜像、没重启容器)。分工:调试用挂载图方便,交付用 Dockerfile 的 COPY 把文件焊进镜像。compose 里写法:服务下 volumes: - mydata:/data,文件末尾再加顶层 volumes: { mydata: } 声明。仓库那课的 -v /data/home/registry-data 就是绑定挂载。

09

自动重启 --restart

让容器挂了自己爬起来。四种策略:

策略说明
no默认,不重启
on-failure[:N]进程以非 0 退出才拉,最多 N 次
always总是拉
unless-stopped总是拉,除非被手动停过 —— 最常用,推荐
BASH · 自崩演示
# 进程按自己的逻辑退出 = 无人值守的猝死,策略会拉起它
docker run -d --name crash --restart always alpine sh -c 'sleep 3; exit 1'
docker inspect -f '{{.RestartCount}}' crash   # 实测 8 秒涨到 7
docker rm -f crash                            # 失控演示容器一步强拆
  • 只对“进程自己死”生效:程序自崩、自退出 → 策略拉起,RestartCount 增长(上方演示实测)
  • docker stop / docker kill 不触发重启:这是操作员亲手下的停止指令,Docker 视为“手动让他停的”,策略不违抗
  • 容器内部 kill -9 1 杀不掉 PID 1:内核对命名空间 init 进程有信号保护,内部信号不投递;宿主机侧的 docker kill 不受此限
  • InstaBox 场景:机器空闲暂停后恢复 ≈ Docker 重启一轮,容器设 unless-stopped 能自己回来;compose 里写一行 restart: unless-stopped
  • ✓进阶 · Zephyr 镜像:官方镜像先跑通 → 自制瘦镜像(只装 ARM 工具链)→ 编出 hello_world 的 zephyr.bin(待实操)
10

进阶 · Zephyr 编译镜像

把 west 编译环境做成镜像:换机器、换 VPS 都一条命令开工,不用再装一遍工具链。先划清边界——这一章只管编译:InstaBox 没有 USB,ST-Link 插不上,west flash 用不了;容器产出 zephyr.bin 后用 instabox cp 拉回本机再烧。

两条路线:先用现成的,再自己做

路线说明
ghcr.io/zephyrproject-rtos/ci官方镜像(另有带 VNC 的 zephyr-build),含 SDK 全架构工具链,开箱能编 —— 先拿它跑通一次 west build 验证流程
自制 zephyr-dev本章的 Dockerfile:只装 ARM 一条工具链,体积小、版本自己钉死 —— 长期用这条

先定版本:Zephyr 与 SDK 必须配对

Zephyr 版本配套 SDK
4.4 / 4.51.0.1
4.30.17.4
4.20.17.2
3.7 LTS0.16.x;官方镜像用 v0.26-branch 标签
💡

不用背这张表:你工程里 zephyr/SDK_VERSION 文件写着的就是答案。在工作区里跑 west sdk install -t arm-zephyr-eabi 也会自动按它装配对的 SDK——本章把这一步改成构建镜像时用安装包做,道理相同。

Dockerfile:只装 ARM 的瘦镜像(~/zephyr-lesson)

DOCKERFILE · ZEPHYR-DEV
FROM ubuntu:24.04                                  # 基础镜像和 SDK 双钉版,不用 latest
ARG SDK_VERSION=1.0.1                               # 与 Zephyr 4.4/4.5 配对;用 4.3 就改 0.17.4
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install --no-install-recommends -y \
      git cmake ninja-build gperf ccache dfu-util device-tree-compiler \
      wget python3-dev python3-venv python3-tk xz-utils file make \
      gcc gcc-multilib g++-multilib libsdl2-dev libmagic1 \
    && rm -rf /var/lib/apt/lists/*              # 主机依赖照官方清单,一层装完清缓存
ENV VIRTUAL_ENV=/opt/venv
ENV PATH=${VIRTUAL_ENV}/bin:${PATH}               # venv 写进 PATH,省掉每次 activate
RUN python3 -m venv ${VIRTUAL_ENV} && pip install --no-cache-dir west
WORKDIR /opt
RUN wget -q https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${SDK_VERSION}/zephyr-sdk-${SDK_VERSION}_linux-x86_64_minimal.tar.xz \
    && tar -xf zephyr-sdk-${SDK_VERSION}_linux-x86_64_minimal.tar.xz \
    && rm zephyr-sdk-${SDK_VERSION}_linux-x86_64_minimal.tar.xz \
    && cd zephyr-sdk-${SDK_VERSION} && ./setup.sh -t arm-zephyr-eabi -h -c   # minimal 包 + 只装 ARM 工具链
ENV ZEPHYR_TOOLCHAIN_VARIANT=zephyr
ENV ZEPHYR_SDK_INSTALL_DIR=/opt/zephyr-sdk-${SDK_VERSION}
WORKDIR /workdir                                    # 源码运行时挂进来,不焊进镜像
CMD ["bash"]
⚠️

体积账先算清:minimal 包下载只要 71MB,只装 ARM 后 SDK 落地约 2.0GB(setup.sh 已实测跑通,产物 gcc 为 14.3.0);全量 gnu 包光下载就 2.1GB、全架构落地更大。InstaBox 的 /data 只有 20GB,还要装 west update 拉的源码(约 4–5GB),所以只装 ARM 不是抠门,是刚需——F103 / F407 / H743 本来也都是 Cortex-M。

构建与使用

BASH · 机器内(~/zephyr-lesson)
docker build -t zephyr-dev:1.0.1 .
docker images zephyr-dev        # 先看一眼体积,心里有账
# 一次性编译(CI 式):编完容器即删,产物留在挂载的工程目录里
docker run --rm -v /data/home/zephyrproject:/workdir zephyr-dev:1.0.1 \
  west build -p always -b nucleo_f103rb zephyr/samples/hello_world
# 交互开发:进容器慢慢敲,退出即删
docker run -it --rm -v /data/home/zephyrproject:/workdir zephyr-dev:1.0.1
ls /data/home/zephyrproject/build/zephyr/zephyr.bin   # 回到宿主机确认产物在
BASH · 容器内第一次进工作区时
west update                     # 拉模块源码(已有工作区且没换版本可跳过)
west zephyr-export              # 注册 Zephyr 的 CMake 包
west packages pip --install     # Python 依赖跟 Zephyr 版本走,装在工作区侧
  • 源码不进镜像,只进工具链:工程目录用绑定挂载进 /workdir,改代码不用重打镜像——这正是第八课的分工:源码用挂载图方便,工具链用 RUN 焊进镜像图稳定
  • 自制板同理:你的板级目录放在应用工程里随工程一起挂载,编译时把 -b nucleo_f103rb 换成自己的板名即可,镜像不用动
  • 产物属主坑:容器默认 root,build/ 会是 root 属主;InstaBox 里本来就用 root 不明显,拿到本机或 VPS 上跑时加 --user $(id -u):$(id -g),免得宿主机删不动产物
  • Python 依赖别焊死在镜像里:它跟 Zephyr 版本绑定,换版本就在工作区重跑 west packages pip --install,镜像只管 west 本体和工具链
  • Dockerfile 若放工程根目录构建,同目录加一份 .dockerignore(写上 build/ 和 .git/),不然构建上下文会把几个 GB 的产物一起打包送进去
🎯

本章完成标准:docker run 里 arm-zephyr-eabi-gcc --version 显示 Zephyr SDK 1.0.1;hello_world 为 nucleo_f103rb 编译通过、zephyr.bin 在宿主机可见;删掉镜像重打一遍,工程和产物都还在;最后把镜像按第七课的方法推一份进自建 registry(localhost:5000/zephyr-dev:1.0.1)收尾。

11

学习进度

截至 2026-10-29:基础课程已实操通过;新增进阶章「Zephyr 编译镜像」待实操。

已完成 7 / 8
  • ✓第一课:hello-world + nginx 容器、端口映射、port publish
  • ✓第二课:logs / exec 进容器 / stats / 先容器后镜像的清理顺序
  • ✓第三课:Dockerfile 打出自有镜像 mynginx:v1(~/docker-lesson3)
  • ✓第四课:compose 拉起 web + cache(redis)一组服务(~/docker-lesson4)
  • ✓进阶 · 仓库:自建 registry,mynginx 推送 → 删本地 → 拉回,指纹 4c641273c0b7 一致
  • ✓第五课 · 数据卷:命名卷跨容器读回数据;绑定挂载改宿主机文件即时生效
  • ✓第六课 · 自动重启:自崩进程被策略拉起(RestartCount 涨到 7);stop/kill 不触发、容器内杀 PID 1 被内核挡下,均实测确认
  • ✓进阶 · Zephyr 镜像:官方镜像先跑通 → 自制瘦镜像(只装 ARM 工具链)→ 编出 hello_world 的 zephyr.bin(待实操)
已复制,去终端粘贴吧