虚拟化相关

包含虚拟机和docker相关知识点

虚拟化网络设置

docker网络

Docker网络剖析

官方文档给出了5种网络驱动模型,如下:

  • bridge:网桥类型网络;其实就是五大虚拟化网络模型中的NAT网络,只不过这里叫做bridge网桥类型.需要映射接口才能让外部访问到容器

    默认bridge不支持容器名dns

    自定义bridge支持容器名dns

  • host:该模式仅在linux可用,理解成容器变成了宿主的一个应用了.其原理是容器使用宿主机的网络空间,因此会直接看到并使用宿主机所有网络功能

  • None:啥网络都没有

image-20230913180418322

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#docker命令
# 1. 创建自定义bridge网络
docker network create my-custom-bridge
# 2. 使用自定义bridge启动容器(支持容器名DNS)
docker run -d --network my-custom-bridge --name app1 nginx

# 示例:host模式启动容器(--network host)
docker run -d --network host --name host-app nginx

# 示例:none模式启动容器(--network none)
docker run -d --network none --name none-app busybox sleep 3600

# 验证:容器无外网/内网IP
docker exec none-app ip addr
# 输出仅能看到lo(回环)接口,无eth0等网络接口

虚拟机网络

(以virtualBox为例)

  • NAT模式 相当于虚拟了一层NAT,但是各个虚拟机之间隔离

  • NAT网络模式 虚拟了一层NAT,各个虚拟机之间不隔离

    VMware等虚拟机的NAT模式不太一样 虚拟了一层NAT,各个虚拟机之间可以互相访问,虚拟机与宿主机之间也可以互访

  • 桥接 相当于虚拟机放到了宿主机局域网里(适配器选项需要和宿主当前联网使用的适配器一致).结果是虚拟机和宿主处于同一网段,由路由器统一分配ip

  • 内部网络模式 相当于仅仅用交换机网线把虚拟机们连接起来了.(需要提前设置系统设置,防止虚拟机启动时一直卡在DHCP服务里,同时还需要给虚拟机手动设置ip),虚拟机间互通,但不能访问宿主,不能访问网络

  • 仅主机模式 仅允许虚拟机之间以及与宿主的互访

image-20230913171649452

docker

优势

  • 应用隔离
  • 环境配置
  • 安装部署
  • 持续集成
  • 持续发布
  • DevOps

docker和虚拟机的区别

image-20240607160742708image-20240607161009694

特性 普通虚拟机 Docker
跨平台 通常只能在桌面级系统运行,例如 Windows/Mac,无法在不带图形界面的服务器上运行 支持的系统非常多,各类 windows 和 Linux 都支持
性能 性能损耗大,内存占用高,因为是把整个完整系统都虚拟出来了 性能好,只虚拟软件所需运行环境,最大化减少没用的配置
自动化 需要手动安装所有东西 一个命令就可以自动部署好所需环境
稳定性 稳定性不高,不同系统差异大 稳定性好,不同系统都一样部署方式

虚拟机使用Hypervisor虚拟化技术

Docker 通常用来做什么

  • 应用分发、部署,方便传播给他人安装。特别是开源软件和提供私有部署的应用
  • 快速安装测试/学习软件,用完就丢(类似小程序),不把时间浪费在安装软件上。例如 Redis / MongoDB / ElasticSearch / ELK
  • 多个版本软件共存,不污染系统,例如 Python2、Python3,Redis4.0,Redis5.0
  • Windows 上体验/学习各种 Linux 系统

概念理解

  • 打包:就是把你软件运行所需的依赖、第三方库、软件打包到一起,变成一个安装包
  • 分发:你可以把你打包好的“安装包”上传到一个镜像仓库,其他人可以非常方便的获取和安装
  • 部署:拿着“安装包”就可以一个命令运行起来你的应用,自动模拟出一摸一样的运行环境,不管是在 Windows/Mac/Linux。
  • 镜像:可以理解为软件安装包,可以方便的进行传播和安装。
  • 容器:软件安装后的状态,每个软件运行环境都是独立的、隔离的,称之为容器。

安装

windows和mac需要安装Docker Desktop,在使用docker命令的时候要启动Docker Desktop

windows安装需要开启 Windows 虚拟化和 Linux 子系统(WSL2),如下图,不然启动会报错(新版本似乎不会报错了)

img

镜像加速源

6月6日大多数镜像源失效

镜像加速器 镜像加速器地址
Docker 中国官方镜像 https://registry.docker-cn.com
DaoCloud 镜像站 http://f1361db2.m.daocloud.io
Azure 中国镜像 https://dockerhub.azk8s.cn
科大镜像站 https://docker.mirrors.ustc.edu.cn
阿里云 https://ud6340vz.mirror.aliyuncs.com
七牛云 https://reg-mirror.qiniu.com
网易云 https://hub-mirror.c.163.com
腾讯云 https://mirror.ccs.tencentyun.com

设置加速源

"registry-mirrors": ["https://registry.docker-cn.com","https://dockerhub.azk8s.cn"]

img

一览

DockerCheatSheet-ByGeekHour

image-20240607160521824

基本知识

dockerhub找镜像相关

dockerhub

搜到一个docker镜像后

其中可能会有介绍或how to use的说明

还可能会罗列很多版本信息,可以根据版本进行下载

image-20240608185749764

alpine 轻量级,瘦身版

镜像构建

Docker中包含Dockfile,镜像,容器三种概念.

其中镜像是用于构建容器的”菜谱”

镜像是按层次结构来构建的,每一层都是基于上一层的(底层是增量存储),通过编写Dockfile可以构建镜像.所以需要先指定一个基础镜像,然后在这个镜像的基础上添加其他我们需要的东西

使用docker image history 镜像名/镜像id可以查看Dockerfile写的每一个指令(显示出来的需要从下往上看)

Dockerfile语法

  • FROM 基础镜像:表示新建的iamge(镜像)需要在node环境中进行,那么就需要node镜像。

  • WORKDIR 工作目录:它可以指定容器启动后的默认工作目录,如声明了工作目录/app,即node代码在该目录下进行

  • COPY 源路径 目标路径:作用是将当前目录的所有代码都复制到工作目录下(源路径为相对于Dockerfile文件的路径,目标路径为镜像内的路径)

  • CMD:后面跟着一句shell 命令,该命令在容器运行的时候会得到执行,即当容器运行的时候,就会启动一个监听端口为3000的服务

    1
    2
    3
    4
    5
    6
    #CMD命令后可接方括号
    CMD ["node","/index.js"]
    #第一个参数表示可执行程序的名字,其他参数表示该可执行程序需要的参数(注意一切路径基于镜像)

    #CMD命令也可不接方括号
    CMD node /index.js
    • CMD -c追加命令会替换CMD指定的命令
    • ENTRYPOINT -c追加命令会追加到ENTRYPOINT指定的命令的后面(除此之外用法与CMD完全一样)

    CMD和ENTRYPOINT的区别可以参考此处

  • ENV key value:设置环境变量key的值为value,如:ENV MY_NAME John Doe

依据Dockerfile构建新镜像使用命令:docker build -t my-docker-demo:1.0 . ,.表示dockerfile所在的目录

实例化该镜像,并运行

docker run -d -p 3000:3000 --name myContainerDemo my-docker-demo:1.0

进入容器shell命令行

docker exec -it myContainerDemo /bin/bash

操作完成后:可以将运行的容器提交为镜像

  1. 停止容器:

    docker stop <container_name>

  2. 提交容器为镜像

    docker commit <container_name> <image_name>

    其中,<container_name> 是正在运行的容器的名称,<image_name> 是要创建的新镜像的名称。

  3. 创建一个新的容器,并使用 --entrypoint--command 选项来指定新的启动指令:

    如:docker run -it --rm --entrypoint /bin/bash --command echo hello world <image_name>

    这将创建一个新的容器,该容器启动时将运行 /bin/bash 程序,并执行 echo hello world 命令。

拉取命令: docker pull 镜像名 如果不指定架构,那么会默认拉取符合自身架构的镜像

arm架构的mac下使用docker pull命令,如果需要拉amd64架构的镜像来通过Rosetta使用,需要使用命令docker pull --platform linux/amd64 镜像名

linux下,docker下的镜像,容器,虚拟网络,卷,插件都记录在/var/lib/docker

目录挂载

现存问题

  • 使用 Docker 运行后,我们改了项目代码不会立刻生效,需要重新buildrun,很是麻烦。
  • 容器里面产生的数据,例如 log 文件,数据库备份文件,容器删除后就丢失了。

容器的特点:容器中的数据不会持久化,新建一个容器,它通常以一个干净的文件系统开始,我们可以创建文件,修改文件,但当我们停止容器时,容器中的所有数据都会丢失掉

想要持久化容器中的数据的话,需要通过目录挂载

几种挂载方式用于解决上述的问题

  • bind mount 直接把宿主机目录映射到容器内,适合挂代码目录和配置文件。可挂到多个容器上

    用法: -v 宿主机绝对路径:容器内绝对路径 如: -v D:/code:/app

  • volume 由容器创建和管理,创建在宿主机,所以删除容器不会丢失,官方推荐,更高效,Linux 文件系统,适合存储数据库数据。可挂到多个容器上

    用法 -v 逻辑卷名:容器内绝对路径(具名挂载) 如 -v db-data:/app

    如果写成 -v 容器内绝对路径 匿名挂载,会自动创建和容器id同名的逻辑卷

  • tmpfs mount 适合存储临时文件,存宿主机内存中。不可多容器共享。

文档参考

目录挂载注意项

Docker 的两种挂载方式的差异

  1. Bind Mount (本地目录)

    • 格式:-v /本机/路径:/容器/路径

    • 行为:强行覆盖。不管容器里原来有什么,都以本机目录为准。本机是空的,容器里看到的就是空的。

    • 这就是导致 很多容器 跑不起来的原因,因为代码被“盖”没了。

      两种常见方式

    • 只挂载文件(最安全,推荐) 这样就像是在桌子上(容器目录)放了一张纸(配置文件),不会遮挡其他饭菜(代码)

    • 挂载到子目录(如果程序支持) 如果程序允许从子目录读取配置,您可以挂载到一个不冲突的子目录。例如 LiteLLM 并没有 /app/custom_config 这个代码目录,那您可以挂载到这里,然后通过参数配置路径位置(前提是程序允许)

  2. Named Volume (命名卷)

    • 格式:-v my-data-volume:/容器/路径 (注意这里没有 / 开头的路径)
    • 行为:初次初始化。如果这个卷是新的(空的),Docker 会把容器里对应路径下的原有文件复制到卷里
    • 如果是这种方式,容器里的文件确实会“反映”出来。如果使用的是本地路径挂载,不适用此规则。

docker命令

  • docker run [参数] 镜像名/镜像ID 运行镜像中运行指令

    docker run -d --name container1 image

    -d表示后台启动

    --name 容器名表示给容器起名(不指定名字的话会随机起名)

    -p 宿主机端口:容器端口 映射容器内端口到宿主机,如-p 9090:8080把容器内的8080端口暴露到宿主机的9090端口.默认映射针对的是tcp,如果要针对udp:-p 5000:5000/udp

    --network 虚拟网络名/网络模式指定容器所属的虚拟网络,也可以指定使用的网络模式

    --network-alias 容器在虚拟网络中的别名 指定容器在虚拟网络中的别名(该别名在代码中可以直接作为ip地址来使用,能正确路由到对应容器)

    --rm代表在容器停止后自动删除容器

    --volumes-from 容器名指定容器名中所有的volumes都挂载过来准备启动的容器中

    -c 要执行的命令 指定在容器启动时要执行的命令 (可以省略到-c,直接将要执行的命令接到镜像名后面),如果Dockerfile中有CMD命令带的指令,该-c指定的命令会替换CMD的命令,如果Dockerfile中有ENTRYPOINT,则-c指定的命令则会是追加执行命令

  • docker build -t 镜像名:版本

    如:docker build -t test:v1

    -t 镜像名:版本 设置镜像名字和版本号

    -f Dockerfile文件 可以指定不叫Dockerfile的Dockerfile文件

  • docker network create 虚拟网络名 创建虚拟网络

  • docker exec -it 容器名/容器id /bin/bash使用/bin/bash与容器交互的方式进入容器

其他命令

docker ps 查看当前运行中的容器
docker images 查看镜像列表
docker rm container-id 删除指定 id 的容器
docker stop/start container-id 停止/启动指定 id 的容器
docker rmi image-id 删除指定 id 的镜像
docker volume ls 查看 volume 列表
docker network ls 查看网络列表

查看容器的ip地址,使用这句命令:docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名/容器id

多容器通信

创建虚拟网络

要想多容器之间互通,比如从 Web 容器访问 Redis 容器,我们只需要把他们放到同个网络中就可以了。

演示

  1. 创建一个名为test-net的网络

    docker network create test-net

  2. 运行 Redis 在 test-net 网络中,别名redis

    docker run -d --name redis --network test-net --network-alias redis redis:latest

  3. 修改代码中通过别名redis来访问Redis容器

    如js代码:

    1
    2
    3
    4
    const redis = require('redis');
    let rds = redis.createClient({url: "redis://redis:6379"});
    rds.on('connect', () => console.log('redis connect ok'));
    rds.connect();
  4. 运行 Web 项目,使用同个网络

    docker run -p 8080:8080 --name test -v D:/test:/app --network test-net -d test:v1

这样两个容器之间就连接上了,但是如果管理的容器非常多呢,这时候就需要用到Docker-Compose

Docker-Compose

由Docker官方开源的项目

  • 用于定义和运行多容器Docker应用程序的工具
  • 使用YAML文件(docker_compose.yaml)来配置应用程序的服务
  • 一条命令docker compose up即可创建或启动所有服务

服务之间的关联关系就是Docker Compose需要解决的问题

如果两个容器不在同一个网络,需要将它们连接到同一个网络。通常使用docker-compose时,它们会自动在同一个网络中

如果项目依赖更多的第三方软件,我们需要管理的容器就更多,每个都要单独配置运行,指定网络非常麻烦。
这里,我们使用 docker-compose 把项目的多个服务集合到一起,一键运行。

安装 Docker Compose

  • 如果你是安装的桌面版 Docker,不需要额外安装,已经包含了。
  • 如果是没图形界面的服务器版 Docker,你需要单独安装 安装文档
  • 运行docker-compose检查是否安装成功

编写脚本

参考文档

要把项目依赖的多个服务集合到一起,我们需要编写一个docker-compose.yml文件,描述依赖哪些服务

[[数据序列化格式盘点#yaml|yaml格式参考]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "3.7"
services:
app:
build: ./
ports:
- "80:8080"
volumes:
- ./:/app
environment:
- TZ=Asia/Shanghai
redis:
image: redis:5.0.13
volumes:
-redis:/data
environment:
- TZ=Asia/Shanghai

volumes:
redis:

volumes部分定义了一个名为redis的volume。Volumes是用来在Docker容器之间共享数据的一种机制。在这个文件中,redis这个volume被用于存储Redis容器的数据,以便在容器重启或重新部署时保留数据
定义一个volume时,您可以选择使用具体的目录地址或者名称。在这种情况下,redis这个volume没有指定具体的目录地址,而是只给出了一个名称redis:/data。这意味着Docker会自动为这个volume创建一个挂载点,并将其用于存储容器中的数据。这种方式更加灵活,因为Docker会自动管理这个volume的位置和生命周期,而不需要您手动指定目录地址.若redis下的volumes下写为- ./redis_data:/data这就是手动指定目录地址的方式

容器默认时间不是背景时间,增加TZ=Asia/Shanghai可以改为上海时区

docker-compose.yml 文件所在目录,执行:docker-compose up就可以跑起来了。

相关命令

命令参考

  • 在后台运行只需要加一个 -d 参数docker-compose up -d

  • 查看运行状态:docker-compose ps

  • 停止运行:docker-compose stop

  • 停止运行并删除旧容器(但数据卷会保留): docker-compose down

    如果修改了docker-compose.yml文件,使用上面指令后再重新启动容器才能生效

  • 重启:docker-compose restart

  • 重启单个服务:docker-compose restart service-name

  • 进入容器命令行:docker-compose exec service-name sh

  • 查看容器运行log:docker-compose logs [service-name]

发布和部署

镜像仓库介绍

镜像仓库用来存储我们 build 出来的“安装包”,Docker 官方提供了一个 镜像库,里面包含了大量镜像,基本各种软件所需依赖都有,要什么直接上去搜索。

我们也可以把自己 build 出来的镜像上传到 docker 提供的镜像库中,方便传播。
当然你也可以搭建自己的私有镜像库,或者使用国内各种大厂提供的镜像托管服务,例如:阿里云、腾讯云

  • 首先你要先 打开官网,登录账号

  • 创建一个镜像库

    image.png
  • 命令行登录账号: (下面的username均应填入实际的账号名)
    docker login -u username

  • 新建一个tag(利于版本控制,便于回滚与追踪)
    docker tag test:v1 username/test:v1

  • 推上去
    docker push username/test:v1

  • 部署试下
    docker run -dp 8080:8080 username/test:v1

    也可直接在docker-compose中直接使用这个镜像username/test:v1

阿里云容器托管: docker 官方的镜像托管有时候上传和下载都太慢了,如果你想要更快的速度,可以使用阿里云的免费镜像托管(镜像仓库100个 不限时长) 点击此处跳转阿里云容器镜像服务的镜像仓库

备份和迁移数据

容器中的数据,如果没有用挂载目录,删除容器后就会丢失数据。
参考如何挂载目录
如果你是用bind mount直接把宿主机的目录挂进去容器,那迁移数据很方便,直接复制目录就好了
如果你是用volume方式挂载的,由于数据是由容器创建和管理的,需要用特殊的方式把数据弄出来。

备份和导入Volume的流程

备份:

  • 运行一个 ubuntu 的容器,挂载需要备份的 volume 到容器,并且挂载宿主机目录到容器里的备份目录。
  • 运行 tar 命令把数据压缩为一个文件
  • 把备份文件复制到需要导入的机器

导入:

  • 运行 ubuntu 容器,挂载容器的 volume,并且挂载宿主机备份文件所在目录到容器里
  • 运行 tar 命令解压备份文件到指定目录

备份 MongoDB 数据演示

  • 运行一个 mongodb,创建一个名叫mongo-data的 volume 指向容器的 /data 目录
    docker run -p 27018:27017 --name mongo -v mongo-data:/data -d mongo:4.4

  • 运行一个 Ubuntu 的容器,挂载mongo容器的所有 volume,映射宿主机的 backup 目录到容器里面的 /backup 目录,然后运行 tar 命令把数据压缩打包到用于导出目录
    docker run --rm --volumes-from mongo -v d:/backup:/backup ubuntu tar cvf /backup/backup.tar /data/

    --rm代表在容器停止后自动删除容器

最后你就可以拿着这个 backup.tar 文件去其他地方导入了。

本质其实就是用一个ubuntu镜像的临时容器映射了宿主机的两个目录(分别是要备份的文件目录以及用于导出备份文件的目录),容器中/data目录下的内容压缩放到容器下backup目录,执行完毕后到宿主机中的d:/backup就可以找到备份文件backup.tar

恢复 Volume 数据演示

  • 运行一个 ubuntu 容器,挂载 mongo 容器的所有 volumes,然后读取 /backup 目录中的备份文件,解压到 /data/ 目录
    docker run --rm --volumes-from mongo -v d:/backup:/backup ubuntu bash -c "cd /data/ && tar xvf /backup/backup.tar --strip 1"

本质就是将压缩包解压回容器中的/data

–strip 1 表示解压时去掉前面1层目录,将文件提取到当前目录而不是保留原始目录结构

CMD与ENTRYPOINT

CMD示例

1
2
FROM ubuntu
CMD ["echo", "Hello, Docker!"]

运行时:

1
2
3
4
5
docker run myimage
#输出: Hello,Docker!
#允许被覆盖:
docker run myimage "Overridden message"
# 输出: Overridden message

ENTRYPOINT示例

强制执行固定命令

1
2
FROM ubuntu
ENTRYPOINT ["echo", "Always run this"]

运行时:

1
2
3
4
5
6
7
8
docker run myimage
# 输出: Always run this

docker run myimage "Additional message"
# 输出: Always run this Additional message

docker run --entrypoint /bin/bash myimage -c "ls"
# 强制运行 bash,而不是 echo

CMD 和 ENTRYPOINT 组合

1
2
3
FROM ubuntu
ENTRYPOINT ["echo"]
CMD ["Default message"]

运行结果:

1
2
3
4
5
docker run myimage
# 输出: Default message

docker run myimage "Overridden message"
# 输出: Overridden message

使用 ENTRYPOINT 制作工具化容器

工具型容器常用 ENTRYPOINT 设计成不可替换的行为。例如:

制作 curl 工具容器

1
2
FROM curlimages/curl
ENTRYPOINT ["curl"]

运行时:

1
docker run mycurl -X GET https://example.com

Alias创建别名

1
alias mytool="docker run --rm -v $(pwd):/data my-image"

添加到 ~/.bashrc 或 ~/.zshrc 中,使其在每次启动终端时生效。

如果经常使用docker容器中的命令,也可以创建一个函数来简化操作,如:

1
2
3
binarychat() {
docker run -v $(pwd):/app -e OPENAI_API_KEY="glhf_xxxxxxxxxxxxxxxxxxxxx" binarychat "$@"
}

一个C++开发环境的Dockerfile

Dockerfile -> 镜像 ->容器

编写Dockerfile

目录下新建Dockerfile文件

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
# 基础镜像
FROM ubuntu:20.04
# 基础镜像设置为ARM架构的Ubuntu镜像
#FROM arm64v8/ubuntu:20.04

#这个LABEL元数据用于标识镜像的版本或其他相关信息
LABEL key="1.0"
#指定维护者或创建者的联系信息
LABEL maintainer="zeroko12@foxmail.com"


# 下面命令正确地设置了时区为亚洲/上海(Asia/Shanghai)。这将确保容器在中国的时区中运行,并且在构建过程中不会有交互式提示。
# 以下是命令的说明:
# ENV DEBIAN_FRONTEND=noninteractive: 这个命令设置环境变量 DEBIAN_FRONTEND 的值为 noninteractive,以避免在包管理操作期间出现交互式提示。
# ENV TZ=Asia/Shanghai: 这个命令设置环境变量 TZ 的值为 Asia/Shanghai,即将时区设置为中国的上海时区。
# RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone: 这个命令在容器中运行,并执行两个操作:
# ln -snf /usr/share/zoneinfo/$TZ /etc/localtime: 它将 /usr/share/zoneinfo/$TZ 软链接到容器内的 /etc/localtime,以将时区设置为指定的时区。
# echo $TZ > /etc/timezone: 它将指定的时区值写入 /etc/timezone 文件,以持久化保存时区设置。
# 这样,当您构建和运行 Docker 镜像时,容器将使用亚洲/上海时区进行操作。
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 更新
RUN apt-get update

# 该命令用于在一个 Docker 容器中安装多个软件包,以支持软件开发和调试的常见需求。让我逐个解释这些软件包的作用:
# build-essential: 提供构建软件所需的基本工具和库,包括编译器、构建工具和标准 C 库等。
# cmake: 一个跨平台的构建工具,用于管理和构建 C/C++ 项目。
# git: 版本控制系统,用于跟踪和管理代码的变更。
# openssh-server: SSH 服务器,用于远程登录和文件传输。
# gdb: GNU 调试器,用于调试程序。
# pkg-config: 用于获取编译和链接软件包所需的元数据信息。
# valgrind: 用于内存调试、性能分析和程序错误检测的工具。
# systemd-coredump: 用于管理系统崩溃时生成的核心转储文件。
# -y表示当系统要求我们安装软件包时接受 "是"。
RUN apt-get install -y build-essential cmake git openssh-server gdb pkg-config valgrind systemd-coredump

#如果要配置vcpkg(不确定可行性)
RUN git clone https://github.com/microsoft/vcpkg.git /usr/local/vcpkg \
&& /usr/local/vcpkg/bootstrap-vcpkg.sh
# 设置vcpkg环境变量
ENV VCPKG_ROOT /usr/local/vcpkg
ENV PATH ${VCPKG_ROOT}:${PATH}

构建镜像

执行下面命令构建docker镜像

docker build -t 镜像名 .

docker build: 这是 Docker 命令的一部分,用于构建 Docker 镜像。

  • -t 镜像名: -t 参数用于指定构建的镜像的标签(tag)。可以根据需要自行更改。
  • .: 这表示 Dockerfile 的路径,. 表示当前目录。Dockerfile 是一个包含构建指令的文本文件,它定义了如何构建 Docker 镜像。

使用这个命令,您可以在当前目录中的 Dockerfile 中定义的环境中构建一个名为 镜像名 的 Docker 镜像。Docker 将按照 Dockerfile 中指定的指令执行构建过程,并生成一个可用的镜像。

确认镜像构建成功: docker images 如果可以看到前面指定的镜像名,则构建成功

容器中运行镜像

在容器中运行镜像: docker run --name cpp_tutorials_container -v /Users/hejun/project/cpp_tutorials:/cpp_tutorials -it 镜像名

  • docker run: 这是 Docker 命令的一部分,用于在容器中运行镜像。

  • --name 容器名: --name 参数用于指定容器的名称。可以根据需要自行更改。

  • -v /Users/hejun/project/cpp_tutorials:/cpp_tutorials: -v 参数用于将主机的目录(/Users/hejun/project/cpp_tutorials)与容器内部的目录(/cpp_tutorials)进行挂载(映射)。这样可以在容器内访问主机上的文件和目录。

    这使得容器内的操作可以访问主机上的文件和目录,方便在开发环境中共享和处理代码。

  • -it: -i 和 -t 参数一起使用,以交互模式运行容器,并分配一个终端(TTY)以便与容器进行交互。

  • 镜像名: 这是要运行的镜像的名称。 使用这个命令,您可以在一个新的容器中运行名为指定镜像名的镜像。容器将会创建,并在交互模式下启动,您可以在容器的终端中执行命令和操作。

查看容器

docker ps

进入正在运行的容器

docker start -i 容器名

运行docker配置项

image-20241113135736292
  • Ports 左边为在宿主机上访问的端口,右边一般不需要填写,因为这是应用程序默认暴露的端口

  • Volumes 卷,可以将容器的目录挂到容器的应用目录下,通常:

    如果是使用这种方式,修改文件可以立即生效,最多也就是重启容器就可以生效

    • Host path 本地的路径
    • Container path 容器内的路径
  • Environment variables 环境变量,表示可以配置docker环境中的环境变量

    注意,环境变量的设置需要停止并删除现有容器,然后重新创建容器

    • Variable 环境变量的名
    • Value 环境变量的值

常用docker命令盘点

docker内网络测试

  • 使用docker exec执行内置命令: docker exec 容器哈希/容器名 具体的命令

  • 创建一个临时容器(针对极简镜像测试网络),共享网络命名空间来测试: docker run -it --rm --network container:容器哈希 alpine sh

    也可以直接通过临时容器一句话连指令一起发:docker run --rm --network container:bb3995 alpine ping -c 2 8.8.8.8可以确定容器是否有基本的网络连接

docker容器内访问主机ip地址可以通过域名http://host.docker.internal

docker查看当前全部容器的ip地址

1
2
# 查看所有容器的IP地址
docker inspect $(docker ps -q) --format='{{.Name}}: {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

docker查看指定容器的信息格式化显示

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看容器基本信息表格
docker inspect 容器名 --format='容器名: {{.Name}}
容器ID: {{.Id}}
创建时间: {{.Created}}
状态: {{.State.Status}}
IP地址: {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}
网关: {{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}
MAC地址: {{range .NetworkSettings.Networks}}{{.MacAddress}}{{end}}
镜像: {{.Config.Image}}
启动命令: {{json .Config.Cmd}}
环境变量:
{{range .Config.Env}} {{.}}
{{end}}'

docker容器之间的网络连接

查看所有网络可以通过docker network ls

  • 最常见的是各个容器都将自己的端口映射到宿主机的某个端口,互相之间通过http://host.docker.internal:端口号的方式来互相访问

    这种方式虽然每个容器都是独立的网络(不填写network的情况下名字将是容器名_default)

  • 另一种方式是将多个容器指定到一个网络中,network指向同一个名字即可

    相互之间可以直接通过容器内子网的ip地址进行互相访问

各平台部署性能损耗对比

部署环境(Docker) 核心运行机制 CPU / 内存损耗 磁盘 I/O 损耗(核心场景) 网络损耗(核心模式) 性能定位 核心适用场景 极致优化建议
Linux 原生系统(Ubuntu 24.04+/RHEL 9.3+) 直接基于 Linux 6.6 + 内核,Namespace+Cgroupv2,无虚拟化层,overlay2 默认启用 fast diff 特性 <1%(可忽略) 1. 容器内 overlay2(fast diff):3-5%
2. 宿主机 bind mount:<3%
3. Volume(本地 / CSI):<2%
1. 默认 bridge(nftables):延迟 + 1-3,吞吐量 < 3%
2. host 模式:几乎 0 损耗
3. 容器间 link:<2%
准原生(无感知) 生产环境(核心业务 / 高并发 / 高 I/O)、开发 / 测试 1. 启用 Cgroupv2+systemd 统一管理
2. 核心业务用 host 网络 + 本地 Volume
3. 大文件场景禁用 copy-up,使用 direct-lvm
macOS(Docker Desktop 26.2+,Apple Silicon/macOS 14+) 基于 Apple 虚拟化框架 + VirtioFS(默认),轻量 Linux VM,文件共享通过 virtiofsd 原生加速 3-6% 1. VirtioFS 共享目录(默认):8-15%
2. 容器内独立目录:5-10%
3. 旧版 gRPC-FUSE(已淘汰):50%+
1. 默认 bridge:延迟 + 3-5,吞吐量 8-10%
2. host 模式:延迟 + 2-4,吞吐量 5-8%
3. 容器互联:<3%
低损耗(轻微感知) 开发 / 测试环境(本地联调),不建议生产 1. 升级 macOS 14+,确认 VirtioFS 默认启用(26.2+)
2. 减少跨系统文件读写,优先容器内目录
3. 启用 Resource Saver 模式降低空闲 CPU 占用
Windows WSL2(2.0.9+,Windows 11 23H2+) 复用 WSL2 轻量 Hyper-V VM,Linux 5.15 + 内核,支持 VirtioFS 文件共享(2026 年默认) <1.5%(接近原生) 1. WSL2 内部目录(/home/):<3%
2. VirtioFS 共享 Windows 目录:8-12%
3. Volume:<2%
1. 默认 bridge(WSLg 优化):延迟 + 1-2,吞吐量 < 3%
2. host 模式:几乎 0 损耗
3. 宿主机互通:<1.5%
接近原生(无感知) Windows 端开发 / 测试、轻量生产(非超高 I/O) 1. 所有项目放在 WSL2 内部目录
2. 启用 WSL2 动态内存 + VirtioFS
3. 关闭 Windows Defender 对 WSL 目录实时扫描
Windows Server 2025(进程隔离) 基于 Windows Server 2025 内核,Namespace+Job Objects,无虚拟化层,默认进程隔离 <1%(可忽略) 1. 容器内原生目录:<3%
2. 宿主机挂载(SMB 4.1+):3-6%
3. Volume:<2%
1. 默认 NAT(2025 优化版):延迟 + 1-2,吞吐量 < 3%
2. host 模式:几乎 0 损耗
3. 容器集群:<2%
准原生(无感知) Windows 原生业务生产环境(.NET 8+/Windows 服务) 1. 确保容器镜像与宿主机版本完全匹配(2025 LTSC)
2. 核心业务用 host 网络 + 本地 Volume
3. 启用容器 CPU / 内存硬限制(Cgroupv2)
Windows Server 2025(Hyper-V 隔离) 基于 Hyper-V 2025 轻量 VM,每个容器独立内核,安全隔离增强版 8-12% 1. 容器内目录:12-18%
2. 宿主机挂载:18-22%
3. 固定磁盘:比动态磁盘降低 3-5% 损耗
1. 默认 NAT:延迟 + 6-8,吞吐量 8-12%
2. host 模式:延迟 + 4-6,吞吐量 6-10%
3. 隔离网络:+2%
中等损耗(明显感知) 高安全隔离需求(金融 / 政务)、多租户场景 1. 仅必要场景使用,避免高 I/O/ 高并发
2. 使用 Gen2 VM + 固定大小 VHDX
3. 启用 Hyper-V 性能计数器优化调度
Windows Server 2025(Linux 容器) 基于 Hyper-V 2025 运行 Linux 内核 VM,跨内核虚拟化层,无进程隔离选项 6-9% 1. 容器内目录:12-18%
2. 宿主机挂载:18-25%
3. Volume:10-15%
1. 默认 bridge:延迟 + 5-8,吞吐量 10-15%
2. host 模式:延迟 + 3-5,吞吐量 8-10%
3. 端口映射:+2%
中等损耗(明显感知) 仅 Windows Server 端临时 Linux 业务调试 1. 替换为 WSL2+Docker(性能提升 40%+)
2. 减少跨系统文件读写
3. 若必须使用,启用 Hyper-V 嵌套虚拟化

上面的Windows Server 2025(进程隔离)只用于启动windows容器

90%+的容器为 Linux 容器,Windows容器几乎仅用于微软系 Windows 原生业务

仅考虑运行Linux容器的排名如下:

排名 部署环境(仅运行 Linux 容器) 核心 CPU / 内存损耗 磁盘 I/O 核心损耗(主流场景) 网络核心损耗(bridge 模式) 性能定位 核心适用场景
1 Linux 原生(Ubuntu24.04+/RHEL9.3+) <1%(可忽略) Volume<2%/bind mount<3% 延迟 + 1-3ms / 吞吐量 < 3% 准原生(无感知) 生产核心业务、高并发 / 高 IO、开发测试
2 Windows WSL2 2026+(Docker 26.2+) <1.5%(接近原生) WSL2 内部目录 < 3%/VirtioFS 共享 8-12% 延迟 + 1-2ms / 吞吐量 < 3% 接近原生(无感知) Windows 端开发测试、Linux 轻量生产
3 macOS 14+(Docker 26.2+) 3-6%(低损耗) VirtioFS 共享 8-15%/ 容器内目录 5-10% 延迟 + 3-5ms / 吞吐量 8-10% 低损耗(轻微感知) macOS 端本地开发测试(无生产价值)
4 Windows Server 2025(原生 Docker 跑 Linux 容器) 6-9%(中等损耗) 容器内目录 12-18%/ 宿主机挂载 18-25% 延迟 + 5-8ms / 吞吐量 10-15% 中等损耗(明显感知) 临时 Linux 业务调试(强烈不推荐生产)

windows和windows server对docker的支持情况

对比维度 Windows 11 23H2+(WSL2) Windows Server 2025
系统核心定位 桌面端(开发 / 测试为主,兼顾轻量生产) 服务端(纯生产级,聚焦稳定性 / 高可用 / 企业隔离)
Docker 核心支持模式 仅支持「WSL2+Linux 容器」(主流),Windows 容器为轻量兼容版(无生产级优化) 双模式全支持:
1. 原生进程 / Hyper-V 隔离的 Windows 容器(生产级,专属特性)
2. WSL2+Linux 容器(适配 Linux 生态)
专属 Docker 特性 无生产级特性,仅做开发适配(如 WSLg、文件无缝共享) 企业级特性:
1. Cgroupv2 硬限制
2. SMB 4.1 + 挂载优化
3. Hyper-V 企业级隔离
4. 容器集群适配
底层虚拟化组件 Hyper-V 为桌面轻量版(资源调度偏向桌面体验,无企业级性能优化) Hyper-V 为服务端企业版(支持 Gen2 VM、固定磁盘、性能计数器优化,适配生产高负载)
Docker 优化重点 开发易用性(如 VirtioFS 无缝共享、动态内存) 生产稳定性(如版本强匹配、CPU / 内存硬限制、隔离网络)
默认运行的容器类型 99% 场景跑Linux 容器(WSL2),Windows 容器极少用 分场景:
1. Windows 原生业务跑进程隔离 Windows 容器(主流)
2. Linux 业务跑 WSL2+Linux 容器(适配)

部分工业软件、硬件配套服务,依赖Windows 专属的硬件驱动(如工业采集卡、打印机驱动、串口驱动),Linux 无对应的驱动支持,只能通过 Windows 容器运行,同时映射宿主机硬件

Docker Desktop

防止Docker Desktop占用C盘

wsl实例迁移

Docker 会自动创建两个专属 WSL 实例: wsl存储机制

  • docker-desktop:运行 Docker 守护进程、CLI 等系统组件(占用几百 MB);
  • docker-desktop-data:存储所有 Docker 镜像、容器、卷(你的 16GB 核心);

Docker 专属实例C:\Users\<你的用户名>\AppData\Local\Docker\wsl\data\ext4.vhdx

这两个实例默认在 C 盘,即使 Docker 本体装在 D 盘,数据仍在 C 盘

Docker Desktop 的可视化设置改不了这个路径,只能通过 export/import 指令迁移

Docker 的所有数据(镜像、容器、卷、网络配置)都存储在 docker-desktop-data 这个 WSL 实例的虚拟磁盘(.vhdx)中

迁移脚本

下面迁移的方式已经被淘汰

  • 4.30 以前
    • docker-desktop → 引擎
    • docker-desktop-data → 镜像 / 容器数据
  • 4.30+ 新安装
    • ❌ 不再创建 docker-desktop-data
    • ✅ 改为 Docker 自己管理 VHD 磁盘

新版本直接在Settings → Resources → Advanced改位置就可以!!!

以下是迁移 Docker 专属 WSL 实例的一键 PowerShell 脚本,脚本包含管理员权限检查、自动停止服务、路径自定义、异常处理,新手可直接使用,全程无需手动输零散命令

使用前必看(关键注意事项)

  1. 脚本运行前:必须关闭 Docker Desktop(托盘里右键退出,确保完全停止);

  2. 请修改脚本中 $targetRootPath 变量为你想迁移到的 D 盘路径(默认是 D:\Docker\WSL,可自定义);

  3. 脚本需以管理员身份运行(否则会权限不足);

  4. 迁移过程中会生成临时 tar 备份文件(迁移完成后可自动删除,也可保留);

    脚本里的 $keepTarFile 设为 $true,迁移后会保留 D 盘的 tar 文件

  5. 迁移完成后,C 盘 %USERPROFILE%\AppData\Local\Docker\wsl 目录下的旧文件会被注销,16GB 空间会释放。

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
<#
.SYNOPSIS
一键迁移Docker专属WSL实例(docker-desktop + docker-desktop-data)从C盘到指定路径
.AUTHOR
编程助手
.NOTES
1. 运行前必须关闭Docker Desktop
2. 必须以管理员身份运行PowerShell
3. 迁移完成后C盘的Docker WSL数据会被注销,释放空间
#>

# ====================== 自定义配置(请修改这里的路径)======================
# 迁移目标根路径(建议放在D盘,无需手动创建,脚本会自动建)
$targetRootPath = "D:\Docker\WSL"
# 是否保留迁移后的tar备份文件(true=保留,false=删除,默认删除节省空间)
$keepTarFile = $false
# ==========================================================================

# 检查是否以管理员身份运行
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Host "❌ 错误:请以【管理员身份】运行此脚本!" -ForegroundColor Red
Read-Host "按任意键退出..."
exit 1
}

# 定义子路径(无需修改)
$tarDataPath = Join-Path -Path $targetRootPath -ChildPath "docker-desktop-data.tar"
$tarDistroPath = Join-Path -Path $targetRootPath -ChildPath "docker-desktop.tar"
$importDataPath = Join-Path -Path $targetRootPath -ChildPath "data"
$importDistroPath = Join-Path -Path $targetRootPath -ChildPath "distro"

try {
Write-Host "`n===== 步骤1:停止所有WSL实例和Docker相关服务 =====" -ForegroundColor Cyan
# 停止所有WSL实例
wsl --shutdown
# 验证WSL是否已停止
$wslStatus = wsl -l -v
if ($wslStatus -match "Running") {
Write-Host "⚠️ 警告:部分WSL实例未停止,再次强制关闭..." -ForegroundColor Yellow
wsl --shutdown
}
Write-Host "✅ WSL实例已全部停止" -ForegroundColor Green

Write-Host "`n===== 步骤2:创建目标目录 =====" -ForegroundColor Cyan
# 检查并创建目标目录
if (-not (Test-Path -Path $targetRootPath)) {
New-Item -Path $targetRootPath -ItemType Directory -Force | Out-Null
Write-Host "✅ 已创建目标目录:$targetRootPath" -ForegroundColor Green
} else {
Write-Host "✅ 目标目录已存在:$targetRootPath" -ForegroundColor Green
}

Write-Host "`n===== 步骤3:导出Docker WSL实例(备份) =====" -ForegroundColor Cyan
# 导出docker-desktop-data(核心数据盘,占用最大)
Write-Host "正在导出docker-desktop-data实例..." -ForegroundColor Gray
wsl --export docker-desktop-data $tarDataPath
if (-not (Test-Path -Path $tarDataPath)) {
throw "❌ docker-desktop-data导出失败!"
}

# 导出docker-desktop(系统盘)
Write-Host "正在导出docker-desktop实例..." -ForegroundColor Gray
wsl --export docker-desktop $tarDistroPath
if (-not (Test-Path -Path $tarDistroPath)) {
throw "❌ docker-desktop导出失败!"
}
Write-Host "✅ 两个Docker WSL实例导出完成" -ForegroundColor Green

Write-Host "`n===== 步骤4:注销C盘旧的Docker WSL实例(释放空间) =====" -ForegroundColor Cyan
# 注销旧实例(删除C盘数据)
wsl --unregister docker-desktop-data
wsl --unregister docker-desktop
Write-Host "✅ 已注销C盘旧的Docker WSL实例,C盘空间已释放" -ForegroundColor Green

Write-Host "`n===== 步骤5:导入Docker WSL实例到目标路径 =====" -ForegroundColor Cyan
# 导入docker-desktop-data
Write-Host "正在导入docker-desktop-data到$importDataPath..." -ForegroundColor Gray
wsl --import docker-desktop-data $importDataPath $tarDataPath --version 2

# 导入docker-desktop
Write-Host "正在导入docker-desktop到$importDistroPath..." -ForegroundColor Gray
wsl --import docker-desktop $importDistroPath $tarDistroPath --version 2
Write-Host "✅ 两个Docker WSL实例导入完成" -ForegroundColor Green

Write-Host "`n===== 步骤6:清理临时文件 =====" -ForegroundColor Cyan
# 删除临时tar文件(如果设置为不保留)
if (-not $keepTarFile) {
if (Test-Path -Path $tarDataPath) {
Remove-Item -Path $tarDataPath -Force
}
if (Test-Path -Path $tarDistroPath) {
Remove-Item -Path $tarDistroPath -Force
}
Write-Host "✅ 已删除临时tar备份文件" -ForegroundColor Green
} else {
Write-Host "✅ 已保留临时tar备份文件:`n- $tarDataPath`n- $tarDistroPath" -ForegroundColor Yellow
}

# ====================== 已删除所有验证相关代码 ======================
Write-Host "`n🎉 迁移流程执行完成!`n" -ForegroundColor Green
Write-Host "📌 手动验证步骤:" -ForegroundColor Cyan
Write-Host "1. 执行 wsl -l -v 确认 docker-desktop-data 和 docker-desktop 实例存在"
Write-Host "2. 检查 C:\Users\ADMIN\AppData\Local\Docker\wsl 目录已清空"
Write-Host "3. 启动Docker Desktop验证镜像/容器是否正常" -ForegroundColor Yellow

}
catch {
Write-Host "`n❌ 迁移失败:$($_.Exception.Message)" -ForegroundColor Red
Read-Host "按任意键退出..."
exit 1
}

Read-Host "`n所有操作完成,按任意键退出..."
exit 0

手动验证步骤:

  1. 执行 wsl -l -v 确认 docker-desktop-data 和 docker-desktop 实例存在
  2. 检查 C:\Users\ADMIN\AppData\Local\Docker\wsl 目录已清空
  3. 启动Docker Desktop验证镜像/容器是否正常

假如启动Docker Desktop后,容器不在,执行导入,如下:

1
2
3
4
5
6
7
# 挂载docker_data.vhdx到WSL2的块设备,并指定挂载点为/var/lib/docker
wsl --mount "D:\Docker\WSL\data\disk\docker_data.vhdx" --vhd --mount-point "/var/lib/docker" --type ext4

# 彻底关闭所有WSL实例
wsl --shutdown
# 重启Docker Desktop核心服务
Restart-Service "Docker Desktop Service"

日志占用C盘

删除C盘现有Docker日志: 打开路径 C:\Users\ADMIN\AppData\Local\Docker\logs,直接删除文件夹内所有文件 / 子文件夹(日志无核心数据,可放

清理容器残留日志
1
2
# 清理所有未使用的容器、镜像、卷、网络,以及容器日志缓存
docker system prune -a -f --volumes
  • -a:清理所有未使用的镜像;
  • --volumes:清理未使用的卷(含卷日志);
  • -f:强制清理,无需确认。
迁移日志目录到 D 盘

根源解决日志写 C 盘

找到 Docker 的 daemon.json 配置文件

默认路径:C:\Users\ADMIN\.docker\daemon.json(如果没有,手动创建);

如果找不到:打开 Docker Desktop → 设置(Settings)→ Docker Engine → 右侧编辑框就是 daemon.json 的内容。

修改 daemon.json,配置日志存储路径 + 日志驱动

替换 或 添加以下内容(注意 JSON 格式,逗号分隔,无语法错误):

1
2
3
4
5
6
7
8
9
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m", // 单个容器日志最大100MB
"max-file": "3", // 最多保留3个日志文件(超过自动删除旧的)
"path": "D:\\Docker\\logs" // 日志存储到D盘指定目录
},
"data-root": "D:\\Docker\\data" // 额外:把Docker所有数据(含日志)根目录迁到D盘
}

✅ 关键说明:

  • path:指定日志文件存储到D:\Docker\logs(脚本会自动创建,无需手动建);
  • data-root:把 Docker 的核心数据根目录也迁到 D 盘,彻底杜绝 C 盘写入;
  • max-size/max-file:提前限制日志大小(第三步详细说)。

重启 Docker Desktop 使配置生效

保存 daemon.json 后 → 点击 Docker Desktop 右上角「Apply & Restart」→ 等待 Docker 重启完成。

限制日志大小

防止后续日志再次涨满

上面daemon.json中的max-size(单文件最大)和max-file(保留文件数)是全局配置,所有容器都会遵守:

  • max-size: "100m":单个容器的日志文件最大 100MB;
  • max-file: "3":超过 100MB 后,会生成新日志文件,最多保留 3 个(总大小≤300MB / 容器);
  • 可根据需求调整(比如"500m"/"5")。

K8S

Docker Compose和Kubernetes(通常简写为k8s)都是用于容器编排的工具,但它们的用途不同,适用于不同的环境。

  • Docker Compose是一个工具,允许您定义和运行多个容器的Docker应用程序。它主要用于开发和测试环境,您需要在单个主机上运行多个互连的容器。Docker Compose使用YAML文件定义应用程序所需的服务、网络和卷。
  • Kubernetes是一个容器编排平台,自动化部署、扩展和管理容器化应用程序。它设计用于生产环境,在这种环境中,您需要在一个机器集群上运行容器。Kubernetes提供自动扩展、自愈和滚动更新等功能,以确保应用程序的可靠性和可扩展性。
  • Docker Compose更适用于本地开发和测试
  • Kubernetes更适用于在生产环境中部署和管理容器化应用程序。

Lima

Apache2.0开源地址

Lima是一个在macOS(以及Linux等系统)上快速启动和管理Linux虚拟机的工具

核心目标是: 为macOS用户提供一个接近无缝的Linux环境,特别专注于运行容器

Lima的主要特点:

自动配置:它自动处理了虚拟机中很多繁琐的配置,比如文件共享(你的 macOS 用户目录会自动挂载到虚拟机中)、端口转发等。

在 macOS 上,由于 Docker 依赖 Linux 内核特性,它无法直接运行。因此,需要一个 Linux 环境作为“底座”。Lima 就是这个“底座”的创建和管理工具之一

  1. Docker Desktop for Mac:这是 Docker 官方的解决方案。它内部也使用了一个轻量级的 Linux 虚拟机(以前是 HyperKit,现在也可以是 Virtio-FS),但这个虚拟机对用户是隐藏的。你只需要安装 Docker Desktop,就能直接使用 docker命令。
  2. Lima + Docker:这是一种替代方案。Lima 负责创建和管理 Linux 虚拟机,并在其中安装 Docker 引擎。然后,你需要配置你的 macOS 终端,让本地的 docker命令知道如何与虚拟机内部的 Docker 引擎通信(通过设置 DOCKER_HOST环境变量)。

p.s. 若公司需要避免 Docker Desktop 的许可费用,那么 Lima(或基于 Lima 的 Colima) 是一个非常好的替代方案

模板系统:Lima 提供了多种“模板”,可以一键创建预装了不同容器引擎的虚拟机,例如:

  • template://docker:创建一个已经安装好 Docker 的虚拟机。
  • template://k8s:创建一个单节点的 Kubernetes 集群。
  • template://podman:创建一个预装 Podman 的虚拟机。

核心是虚拟机:Lima 本身不直接运行容器,它的工作是创建和管理一个轻量级的 Linux 虚拟机(默认使用 QEMU),然后在这个虚拟机里运行你指定的容器引擎。

WSL

WSL(Windows Subsystem for Linux)

WSL 是微软推出的Windows 子系统 Linux,简单说就是在 Windows 系统中直接运行 Linux 环境(无需虚拟机 / 双系统),既能用 Windows 的图形界面,又能调用 Linux 的命令行、工具链和服务,是开发(尤其是 Docker、后端、前端)的核心工具

核心价值

  • 无需单独装 Linux 系统 / 虚拟机,Windows 里直接运行 bash/zshgit/npm/docker 等 Linux 工具;
  • 与 Windows 系统深度集成:可直接访问 C 盘(/mnt/c)、D 盘(/mnt/d),文件互通;
  • 性能接近原生 Linux(WSL2 版本),启动速度秒开,资源占用远低于虚拟机。

WSL1 vs WSL2

特性 WSL1 WSL2(推荐)
底层架构 翻译层(直接调用 Windows 内核) 轻量化虚拟机(基于 Hyper-V)
性能 文件 IO 快(访问 Windows 盘) 整体性能高,Linux 兼容性全
虚拟磁盘 无(文件直接存 Windows 目录) 有(.vhdx 动态扩展虚拟磁盘)
Linux 内核特性 支持有限(无 Docker 完整功能) 完整支持(Docker 必须用这个)
存储路径 %LOCALAPPDATA%\Packages\XXX 同左(但核心是 .vhdx 文件)

常用指令

基础管理指令(最常用)

指令 作用 示例(对应你的场景)
wsl --list --verbose 查看所有 WSL 实例(名称、状态、版本),简写 wsl -l -v wsl -l -v → 看到你运行中的实例
wsl --shutdown 停止所有 WSL 实例(迁移 / 清理前必做) wsl --shutdown → 你的实例都会变成 Stopped
wsl -d <实例名> 进入指定 WSL 实例的命令行(d=distribution) wsl -d Ubuntu → 进入 Ubuntu;wsl -d docker-desktop-data → 进入 Docker 数据盘
wsl --terminate <实例名> 停止单个 WSL 实例(比 shutdown 精准) wsl --terminate docker-desktop-data → 只停 Docker 数据盘
wsl --set-default <实例名> 设置默认进入的 WSL 实例(去掉 * 前缀) wsl --set-default docker-desktop → 默认进 Docker 实例

安装 / 卸载指令

指令 作用 示例
wsl --install 一键安装 WSL(默认装 Ubuntu),自动启用 Hyper-V 等依赖 wsl --install → 新手快速部署
wsl --install -d <发行版> 安装指定发行版(如 CentOS、Debian) wsl --install -d Debian → 装 Debian
wsl --unregister <实例名> 注销 WSL 实例(删除该实例所有数据,释放 C 盘空间) wsl --unregister docker-desktop-data → 删 Docker 数据盘(迁移前必做)

迁移 / 备份指令

指令 作用
wsl --export <实例名> <tar路径> 导出 WSL 实例为 tar 备份文件(迁移前备份)
wsl --import <新实例名> <存储路径> 导入 tar 文件为新 WSL 实例(迁移核心)

高级指令(优化 / 排查)

指令 作用 示例
wsl --set-version <实例名> 2 将 WSL1 实例升级为 WSL2(Docker 必须) wsl --set-version Ubuntu 2 → 升级 Ubuntu 到 WSL2
wsl --help 查看所有 WSL 指令(忘记指令时用) wsl --help → 输出完整指令列表
optimize-vhd -Path <vhdx路径> -Mode Full 压缩 WSL 虚拟磁盘(释放未使用空间) optimize-vhd -Path D:\Docker\WSL\data\ext4.vhdx -Mode Full

WSL存储机制

所有 WSL2 实例的 .vhdx 虚拟磁盘默认存在:

  • 普通发行版(如 Ubuntu)C:\Users\<你的用户名>\AppData\Local\Packages\<发行版包名>\LocalState\ext4.vhdx
  • Docker 专属实例C:\Users\<你的用户名>\AppData\Local\Docker\wsl\data\ext4.vhdx(16GB 核心文件);

⚠️ 关键:Docker Desktop 的可视化设置改不了这个路径,只能通过 export/import 指令迁移。