在现代软件开发和部署中,Docker 作为一种容器技术,极大地简化了运行环境的搭建和应用程序的部署。与传统的虚拟机(VM)不同,Docker 容器利用宿主机的操作系统内核,而不是虚拟出一整套硬件和操作系统,从而实现轻量级和高效的资源利用。
安装 Windows 11 Windows 下的 Docker Desktop 运行于 WSL 上,会默认安装 WSL,并创建两个仅供 Docker 使用的 Linux 发行版(distro): docker-desktop
和 docker-desktop-data
。其中,docker-desktop
用于运行 Docker engine,docker-desktop-data
用于存储 containers 和 images。
1 2 3 4 5 6 7 wsl --list -v NAME STATE VERSION * Ubuntu Running 2 docker-desktop Running 2 docker-desktop-data Running 2 wsl -d docker-desktop
WSL 发行版使用 ext4.vhdx
虚拟硬盘文件进行存储,在 %LOCALAPPDATA%/Docker/wsl
目录下。Docker Desktop 创建的 Linux 发行版也存储在此虚拟存储硬盘中,其中,docker-desktop
存储在 distro/ext4.vhdx
,docker-desktop-data
存储在 data/ext4.vhdx
。
1 2 3 wsl --unregister docker-desktop-data wsl --unregister docker-desktop
可将 docker-desktop-data 迁移至其它盘或其它机器。
1 2 3 4 wsl --export docker-desktop-data D:\docker\docker-desktop-data.tar wsl --export docker-desktop D:\docker\docker-desktop.tar
1 2 3 4 5 6 7 8 9 wsl --import docker-desktop-data D:\docker\data D:\docker\docker-desktop-data.tar --version 2 wsl --import docker-desktop D:\docker\docker-desktop D:\docker\docker-desktop.tar --version 2 wsl --unregister docker-desktop wsl --unregister docker-desktop-data
注意,在导出或导入前,需使用 wsl --shutdown
关闭所有 WSL 发行版,以确保虚拟磁盘未被使用。
配置 Docker 的配置文件为 daemon.json
,在 C:\Users\用户名\.docker
目录下,可以直接编辑此文件,也可以在 Docker Desktop 中修改。
1 2 3 4 5 6 { "registry-mirrors" : [ "https://docker.mirrors.ustc.edu.cn" ] }
基本命令 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 docker login -u <user-name> -p <password> [host] docker pull <contain-name> docker push <image-name[:tag]> docker search <contain-name> docker images <image-name> docker rmi <image-name> docker build -t <image-name[:tag]> <dockerfile-path> docker run <image-name> docker start <contain> docker stop <contain> docker restart <contain> docker ps docker rm <contain> docker exec -it <contain> /bin/bash docker inspect <contain> docker logs <contain> docker volume create <volume-name> docker volume ls docker volume inspect <volume-name> docker volume rm <volume-name> docker volume prune docker network create [--driver=<bridge | host | overlay | ipvlan| macvlan | none>] [--gateway=<getway>] [--subnet=<subnet>] <network-name> docker network ls docker network rm <network-name> docker network prune docker network connect <network-name> <contain> docker network disconnect <network-name> <contain> docker network inspect <network>
注:上面命令中 contain
可以是 contain-id
也可以是 contain-name
。
docker run 1 2 3 4 5 6 7 –-name:容器名,不指定则采用默认 -e:配置信息 -p:端口映射,":" 前为主机端口,之后为容器端口。使宿主机本地的应用程序能够访问容器内的服务 -d:后台运行容器,保证在退出终端后容器继续运行 -v:主机和容器的目录映射关系,":" 前为主机目录或数据卷,之后为容器目录。需要注意的是 docker-compose 中 volumes 支持相对路径,但 docker run -v 不支持,需写为绝对路径(可使用环境变量) --network: 指定容器使用的网络模式 --restart:指定容器重启策略
执行 docker run
时,如果本地没有镜像,则会自动 pull
拉取。
注意:$(pwd)
、`pwd
`、$PWD
在 PowerShell 中返回的是 Windows 中反斜杠路径风格 D:\project\docker-data\wordpress
,Git Bash 中虽然能返回左斜杆,但是路径不对,需要再加一个 /
,即 //d/project/docker-data/wordpress
。
1 2 docker run --name wordpress -v /$(pwd ):/var/www/html -p 8080:80 -d wordpress
实践中为了方便管理、复用以及自动化,常将 docker run
保存为 *.sh
*.ps1
Bash 或 PowerShell 脚本。
1 2 3 4 5 6 7 8 9 10 11 12 # bash run-mysql.sh or powershell run-mysql.ps1 docker run \ --name mysql \ -d \ -p 3306:3306 \ --restart unless-stopped \ -v /home/mysql/log:/var/log/mysql \ # 日志文件挂载 -v /home/mysql/data:/var/lib/mysql \ # 数据文件挂载 -v /home/mysql/conf/my.cnf:/etc/mysql/my.cnf \ # 配置文件挂载 -e TZ=Asia/Shanghai \ # 时区设置 -e MYSQL_ROOT_PASSWORD=123456 \ # MySQL root 用户密码 mysql:5.7.38 # 使用的 MySQL 镜像版本
1 2 3 4 5 # run-nginx.sh docker run --name nginx -d -p 80:80 \ -v /home/user/nginx/html:/usr/share/nginx/html:ro \ # 静态资源文件挂载,ro 即 read-only 只读,表示容器内部只能读取宿主机文件不能修改 -v /home/user/nginx/conf.d:/etc/nginx/conf.d:ro \ # 配置文件挂载 nginx
docker build docker build
命令用于根据 Dockerfile 创建 Docker 镜像。用来实现基础镜像的扩展和应用程序的发布。在实践中,通过 docker build
将应用程序代码(COPY
)、依赖(RUN
)和运行时环境(FROM
)打包成一个镜像,方便应用程序的部署,还可以与 CI/CD 配合使用,实现自动化的镜像构建和部署。
1 docker build -t image-name:tag dockerfile-path
Dockerfile Dockerfile 是 docker build
的描述文件,文本内容包含了一条条构建镜像所需的指令。常用指令有:
1 2 3 4 5 6 7 FROM: 指定基础镜像 WORKDIR: 设置工作目录。后续的指令(如 RUN、CMD、ENTRYPOINT 等)都将在这个目录下执行 ENV: 设置环境变量 COPY: 从构建主机复制文件到镜像中 RUN: 镜像构建时执行的命令 CMD: 指定容器默认执行的命令 EXPOSE: 指定容器暴露的端口
注:COPY
和 WORKDIR
指令会自动创建目录,无需 RUN mkdir -p /test
。
以打包部署前端应用程序镜像为例,其 Dockerfile
和 default.conf
配置如下所示:
1 2 3 4 FROM nginx:alpineCOPY dist /usr/share/nginx/html/ COPY ./default.conf /etc/nginx/conf.d/default.conf EXPOSE 80
1 2 3 4 5 6 7 8 9 10 11 server { listen 80; listen [::]:80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } }
1 2 docker build -t geki:1.0.0 . docker run --name geki -p 8080:80 -d geki:1.0.0
构建缓存 Build Cache Docker 镜像是分层构建的,每个 Dockerfile 指令(如 FROM
、COPY
、RUN
等)都会创建一个新的镜像层(Layer),这些层是只读的,且会被缓存。首次构建时,所有层均会执行,生成缓存,后续构建,如果 Docker 发现某一层已经构建过,并且前面的层没有发生变化,就会直接复用缓存,避免重复执行。
1 2 3 4 5 6 7 8 9 FROM node:18 -alpineWORKDIR /white-page ENV NODE_ENV=productionCOPY package.json pnpm-lock.yaml ./ RUN npm config set registry https://registry.npmmirror.com/ && npm i -g pnpm && pnpm install COPY . . RUN npm run build EXPOSE 3000 CMD ["node" , ".output/server/index.mjs" ]
注意:设置 .dockerignore 文件,否则 COPY . .
会拷贝 node_modules,不但慢,而且不利于缓存。
固定不变的内容放前面,容易变化的内容放后面,避免缓存层失效:
1 2 3 4 5 6 COPY package.json package-lock.json ./ RUN npm install COPY . .
合并 RUN
命令,减少层数:
1 RUN apt-get update && apt-get install -y curl git
利用 .dockerignore 文件,避免不必要的文件影响 COPY
指令的缓存:
1 2 3 node_modules .git .dockerignore
多阶段构建 在 Dockerfile 中,多阶段构建(multi-stage build)是指使用多个 FROM
语句来构建镜像,每个 FROM
代表一个独立的构建阶段(Stage),主要意义是减少最终镜像的体积,并确保最终运行环境只包含必要的文件。AS <stage>
定义阶段名,,--from=<stage>
用来跨阶段复制文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 FROM node:18 -alpine AS builderWORKDIR /white-page ENV NODE_ENV=productionCOPY package.json pnpm-lock.yaml ./ RUN npm config set registry https://registry.npmmirror.com/ && \ npm i -g pnpm && \ pnpm install COPY . . RUN npm run build FROM node:18 -alpineWORKDIR /white-page ENV NODE_ENV=productionCOPY --from=builder /white-page/.output ./.output EXPOSE 3000 CMD ["node" , ".output/server/index.mjs" ]
数据卷 Docker Volumes (数据卷) 用于解决容器的数据持久化和数据共享(宿主机到容器,容器间)问题。Docker Volumes 有三种类型:Volumes (卷)、Bind mounts (绑定挂载)、tmpfs mounts (临时挂载)
Volumes 分为命名卷(Named Volumes)和匿名卷(Anonymous Volumes)。Linux 下默认存储在 /var/lib/docker/volumes/<卷名>
,Windows 下的 Docker Volumes 则存储在 WSL 2 虚拟机的文件系统中。
命名卷的生命周期独立于容器,即使容器被删除,卷仍然存在,直到手动删除 (docker volume rm
),而匿名卷的生命周期与容器绑定,当容器删除时,匿名卷也会被删除,除非使用 docker run --rm
运行临时容器。
1 2 3 4 5 wsl cd /mnt/wsl/docker-desktop-data/version-pack-data/community/docker/volumescd \\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes
匿名卷不指定卷名称,仅指定容器内的挂载路径,Docker 会自动分配随机卷名。-v 容器目录/文件
1 2 docker run -v /app/data my-image
1 2 3 4 5 6 docker volume create my-named-volume docker run -v my-named-volume:/app/data my-image docker run -v my-named-volume:/app/data:ro my-image
命名卷适合数据持久化(比如数据库存储、日志存储)和跨容器数据共享(比如 Nginx 直接托管 Nuxt 静态资源,做动静分离)。“卷”虽然初始化配置稍微复杂一点,灵活性也不如“绑定挂载”,但它不依赖宿主机的文件系统,直接由 Docker 管理,具有更高的可移植性、安全性和 I/O 性能。
直接将宿主机的目录或文件挂载到容器中 -v 主机目录/文件:容器目录/文件
。绑定挂载直接依赖宿主机的目录,Docker 不会管理它的生命周期,数据不会随容器删除而丢失。
1 docker run -d -v /home/user/data:/app/data my-image
绑定挂载灵活,简单,但依赖宿主机文件系统(ext4、xfs、NTFS…),可移植性差,如果主机文件发生变化,会影响容器运行,而且由于额外的抽象层,I/O 性能也可能不如 Docker 直接管理的卷。
绑定挂载通常用在本地开发环境中将本地代码目录挂载到容器以及共享配置(比如共享宿主机 Nginx 配置),实现热更新,方便开发调试。在生产环境中,应用程序代码(如 NodeJS、Nuxt 的业务代码)和配置(如 Nginx 配置)会通过自定义构建镜像,COPY
进镜像中,而不是通过数据卷挂载。数据卷通常用于存储动态数据,如数据库、日志等,而非静态的应用程序代码。
1 2 3 4 5 docker run -d -p 80:80 \ -v /$(pwd )/dist:/usr/share/nginx/html \ -v /$(pwd )/default.conf:/etc/nginx/conf.d/default.conf \ --name nginx-test nginx:alpine
1 2 3 4 5 FROM nginx:alpineCOPY dist /usr/share/nginx/html/ COPY ./default.conf /etc/nginx/conf.d/default.conf EXPOSE 80
注意:挂载主机目录,docker run -v
相对路径是相对于命令执行的目录,即当前 shell 终端所在的路径(pwd),docker-composer.yml 中相对路径则相对于 docker-compose.yml 文件所在的目录。
只适用于 Linux,数据存储在内存中,不会写入磁盘,容器停止后数据会丢失。适用于存储临时数据(如 session、缓存)。
1 docker run -d --tmpfs /app/data:rw,size=64m my-image
TODO:Docker Volumes 数据的迁移、备份、恢复
网络 Docker 安装完成后会自动创建三个网络,其中 name 为 bridge 的网络使用 bridge 驱动,是容器的默认网络。
1 2 3 4 5 6 7 C:\Users\tracy>docker network ls NETWORK ID NAME DRIVER SCOPE 95f596f81598 bridge bridge local 065201a9720e host host local 20ae04041c1c none null local C:\Users\tracy>docker network inspect bridge
1 docker network create [--driver=<bridge | host | overlay | ipvlan| macvlan | none>] [--gateway=<gateway>] [--subnet=<subnet>] [network-name]
Docker 的几种网络模式:
1 2 3 4 5 bridge 桥接模式,是默认的网络驱动程序,用于多个容器在同一个宿主机上通信 host 主机模式,不创建任何网络接口,直接使用宿主机的 ip 地址与外界进行通信,不再需要额外的 NAT 转换。(host 模式下,无需指定 -p 端口映射) none 无网络模式,不为容器进行任何网络配置。容器没有网卡、ip、路由等信息,只有一个lo接口,无法与外界通信 overlay 模式,网络基于 Linux 网桥和 Vxlan,实现跨主机的容器通信 macvlan 模式,用于跨主机通信场景
默认情况下,Docker 容器会使用 bridge
网络,各个容器可以通过桥接网络(即 docker0
网桥)访问彼此,但只有通过明确的链接或自定义网络,容器才能知道彼此的存在。
1 2 docker network create mynetwork
1 2 3 4 docker network create --driver=bridge test-net docker run --network test-net
1 2 docker network connect test-net [container-name]
1 2 3 4 5 6 7 8 9 10 11 12 13 version: "3" services: s1: networks: - test s2: networks: - test networks: test:
Docker Compose Docker Compose 是 Docker 提供的单机容器编排工具,用来定义和运行多个容器。Docker Compose 使用一个名为 docker-compose.yml
的 YAML 文件来配置应用程序的服务,这个文件中定义了一系列 docker volume
、docker network
、docker build
和 docker run
的组合,从而使得管理和编排多个容器变得更加方便。
docker-compose.yaml
配置说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 service-name: image: 指定镜像 container_name: 设置容器名称,即 -name environment: 设置环境变量,即 -e volumes: 挂载宿主机目录或命令卷到容器,即 -v ports: 端口映射,即 -p restart: 设置重启策略,no,always,no-failure,unless-stopped,即 -restart depends_on: 设置依赖关系和启动顺序 networks: 指定已定义网络 build: 指定构建配件 context: 指定 Dockerfile 文件所在的路径,即 path dockerfile: 指定 Dockerfile 的名称,即 -f volumes: 定义数据卷,即 docker volume create volume-name networks: 定义网络
docker-compose
常用命令:
1 2 3 4 5 6 7 8 9 docker-compose build docker-compose [-p <compose-name>] [-f <configuration-file>] up -d docker-compose [-p <compose-name>] down docker-compose ps docker-compose start docker-compose stop docker-compose restart docker-compose logs
常用的 docker-compose,lnmp , wordpress ,更多查看此项目 awesome-compose 。
私有镜像仓库 1 2 3 4 5 6 docker run -d -p 5000:5000 --name registry registry:2 docker login localhost:5000 docker login localhost:5000
1 2 3 4 5 6 7 8 9 10 11 12 docker build -t <image-name>:<tag> . docker tag <image-name>:<tag> <registry-domain>/<image-name>:<tag> docker build -t <registry-domain>/<image-name>:<tag> . docker push <registry-domain>/<image-name>:<tag> docker pull <registry-domain>/<image-name>:<tag> docker run --name <contain-name> -p 8080:80 -d <registry-domain>/<image-name>:<tag>
注:registry-domain
不能带 http://
或 https://
,另外,直接在 docker build
命令中指定镜像的完整名称和标签(比如,docker build -t localhost:5000/tracyblog:1.0.0 .
),这样在构建镜像时就已经给镜像打上了标签,docker tag
步骤可省略。
1 2 3 curl <registry-domain>/v2/_catalog curl <registry-domain>/v2/<image-name>/tags/list
1 2 3 mkdir authdocker run --entrypoint htpasswd httpd:2 -Bbn abc 123 > auth/htpasswd
注意:Windows PowerShell 中使用 >
重定向符号时,默认会将输出保存为 UTF-16 LE (带 BOM) 编码。而 htpasswd 工具生成的密码文件要求是 ASCII 或 UTF-8 (无 BOM) 编码。PowerShell 中应使用以下方式生成。
1 docker run --rm --entrypoint htpasswd httpd:2 -Bbn abc 123 | Set-Content -Encoding ASCII auth/htpasswd
1 2 3 4 5 6 7 8 9 10 11 docker run -d ` --restart always ` --name registry ` -p 5000:5000 ` -e REGISTRY_AUTH=htpasswd ` -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd ` -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" ` -v "${PWD} /data:/var/lib/registry" ` -v "${PWD} /auth:/auth" ` registry:2
注意:上述命令在 Windows Git Bash 上执行时,需添加 /
,即 /${PWD}
或 /$(pwd)
。另外,Git Bash 会自动将 REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
转为 REGISTRY_AUTH_HTPASSWD_PATH=C:/Program Files/Git/auth/htpasswd
,改为 ./auth/htpasswd
则不转换。
1 2 3 4 5 6 7 8 9 10 docker run -d \ --restart always \ --name registry \ -p 5000:5000 \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_PATH=./auth/htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ -v "/${PWD} /data:/var/lib/registry" \ -v "/${PWD} /auth:/auth" \ registry:2
1 2 3 4 5 docker exec -it registry sh env | grep REGISTRY_AUTH_HTPASSWD_PATH docker exec -it registry sh -c "cat /auth/htpasswd"
访问 http://localhost:5000/v2/
、curl -u abc:123 http://localhost:5000/v2/
,或者 docker login
测试登录。
docker-compose.yml 是更好的选择,不用考虑 Windows 路径规范和自动路径转换的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 version: '3.8' services: registry: restart: always image: registry:2 ports: - 5000 :5000 environment: REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm volumes: - ./data:/var/lib/registry - ./auth:/auth