my-machine
记不住没关系,记住套路即可。手机上随手查:点任意命令旁的「复制」就能粘贴使用,顶部搜索框可以按关键词过滤。
命令的拼法套路:工具 子命令 对象 参数 —— 先想清楚「对什么对象、做什么动作」,命令基本就拼出来了。任何命令后面加 --help,就是它自己的说明书。
以这条为例,拆开就是四层。看懂一条,其余的都是同一个套路。
npx instabox@latest exec my-machine -- docker ps
│ │ │ │ │ └─ 在机器里真正执行的命令
│ │ │ │ └──── 分隔符:后面是“给机器的命令”
│ │ │ └──────────────────────── 目标机器名
│ │ └───────────────────────────── 动作:在机器里执行命令
│ └───────────────────────────────────────────── instabox 这个工具
└───────────────────────────────────────────────── npx:临时下载并运行这个工具
npxinstabox@latestexecmy-machine--docker ps规律:先想清楚「对什么对象、做什么动作」,命令基本就拼出来了。
在本机 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 验证即可。机器和快照都在账号名下,换电脑不丢;若手滑登成新账号会看到空账号,退出来换回原账号即可。
在机器里跑(先 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),以后才分得清哪版是哪版。
| 命令 | 作用 |
|---|---|
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 表示只在组内互通、没暴露给宿主机 —— 数据库类服务就该这么藏着。
一条红线:日志里 Redis 那句 does not require authentication 是在提醒它没密码。练习时没映射端口、外面连不进来,没事;但永远不要把没密码的 Redis 端口 publish / 映射到公网,这是真实世界里被入侵的重灾区。另外,容器日志时间是 UTC,比本地晚 8 小时,对时间点时别被绕晕。
按这个顺序来,基本不用死记硬背。
命令 --help,如 docker run --help、instabox port --help —— 最快命令 子命令 --help,逐层缩小cat 网址 应为 curl、dockr 是拼写错误)就是日常排障的常态什么时候要关心当前目录:只有命令里出现相对路径时才需要 —— 比如 docker build . 的那个点、docker compose up 要找当前目录的 compose.yaml。纯粹的 run / tag / push / pull 跟 daemon 打交道,在哪个目录敲都一样;回家目录用 cd ~(即 /data/home)。
/data/home,重要文件放 /data 下,快照才会带走docker start 拉回port publish 的地址是公开的、无鉴权,练习完记得 unpublishinstabox exec 机器名 -- 命令 有个怪癖:命令里带 -a 这类参数会被它误解析成自己的参数而报错。两个绕法:命令用引号包成一整串 -- 'docker ps -a',或者直接 instabox ssh 进去敲(练 Docker 时推荐后者)publish 的地址能 CNAME 到 Cloudflare 提速吗?不能。那串子域名本身编码了「哪台机器、哪个端口」,网关靠它认人路由,换成你的域名它不认识,大概率直接报 “This URL is not published”;而且 CNAME 只是 DNS 别名,解析到的还是同一批 IP、同一条线路,本身不加速。这类地址是临时调试入口,机器暂停就失效,天生不适合长期绑域名。绑域名长期跑的服务,应该放在自己能控制的机器上(如 Oregon 的 VPS)再走 Cloudflare。
管「一个镜像怎么做出来」,是构建期的事:docker build 从上到下逐行执行,每执行一条就给镜像叠一层。文件名就叫 Dockerfile,没有扩展名。
FROM nginx:latest # 基础镜像(第一行)
COPY index.html /usr/share/nginx/html/index.html # 拷文件进镜像
| 指令 | 干什么 |
|---|---|
FROM | 指定基础镜像,必须是第一行(构建时) |
COPY | 把文件拷进镜像(构建时) |
RUN | 构建时执行一条命令,如装软件 |
WORKDIR | 设定工作目录 |
ENV | 设定环境变量(构建时定下、运行时生效) |
EXPOSE | 声明这个镜像监听哪个端口(只是声明,不等于对外发布) |
CMD | 容器启动时默认执行什么(运行时) |
层与缓存:每条指令是一层,层带缓存。只改了 index.html 重新 build,只有 COPY 那层重做,前面的层直接复用,所以第二次特别快。经验法则:不常变的写前面(装依赖),常变的写后面(拷代码)。
管「一组容器怎么一起跑」,是运行期的事。它不关心镜像内部怎么做,只描述要哪几个服务、用什么镜像、端口怎么映射、谁和谁连。
services:
web: # 服务名,同时也是组内的“通讯录名字”
image: mynginx:v1 # 用哪个镜像(也可写 build: 现场按 Dockerfile 构建)
ports:
- "8080:80" # 对外映射
cache:
image: redis:latest # 没写 ports,所以只在组内互通
volumes(挂数据)、environment(传环境变量)、restart(自动重启策略)、depends_on(声明启动顺序依赖)docker stop web3 && docker rm web3)镜像默认从 Docker Hub 拉,也可以自己搭一个私有仓库:仓库本身就是个官方镜像。完整闭环是 搭仓库 → 推镜像 → 删本地 → 从仓库拉回来跑。这课对当前目录没要求,在哪敲都行。
# 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 login 后,docker tag mynginx:v1 你的用户名/mynginx:v1 再 docker push 你的用户名/mynginx:v14c641273c0b7…),就保证一字节不差;标签能改名,指纹改不了/data/home/registry-data 里,下次同样命令再起仓库,镜像都还在 —— 这其实已经摸了一半数据卷自建仓库默认无鉴权、走明文 HTTP:只在机器内部练,不要 publish 到公网。哪天想让别的机器来拉,先给它加鉴权和 HTTPS —— 不然等于把仓库敞开在公网上,跟没密码的 Redis 一个道理。
让数据活得比容器久。容器自己的文件系统是临时的,存数据只有两种正经办法:
| 方式 | 写法 | 说明 |
|---|---|---|
| 命名卷 | -v mydata:/data | 数据存在 Docker 管的目录里(docker volume inspect 看 Mountpoint),适合数据库等只给容器用的数据 |
| 绑定挂载 | -v /data/home/目录:/data | 就是宿主机上明明白白的目录,适合网页、配置等要直接看和改的文件 |
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 就是绑定挂载。
让容器挂了自己爬起来。四种策略:
| 策略 | 说明 |
|---|---|
no | 默认,不重启 |
on-failure[:N] | 进程以非 0 退出才拉,最多 N 次 |
always | 总是拉 |
unless-stopped | 总是拉,除非被手动停过 —— 最常用,推荐 |
# 进程按自己的逻辑退出 = 无人值守的猝死,策略会拉起它
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 # 失控演示容器一步强拆
docker stop / docker kill 不触发重启:这是操作员亲手下的停止指令,Docker 视为“手动让他停的”,策略不违抗kill -9 1 杀不掉 PID 1:内核对命名空间 init 进程有信号保护,内部信号不投递;宿主机侧的 docker kill 不受此限unless-stopped 能自己回来;compose 里写一行 restart: unless-stopped截至 2026-10-29:全部课程已实操通过,结课。
mynginx:v1(~/docker-lesson3)~/docker-lesson4)4c641273c0b7 一致