0%

Docker使用手记(1) Win11下的初次使用

  1. 基本概念
  2. docker-desktop安装与配置
    1. 启动虚拟化
    2. 开启WSL2
    3. 更新WSL2内核
    4. 文件路径
    5. 加速下载:设置国内镜像
  3. 存储控制与空间清理
  4. 常用命令行操作
    1. 查看信息
    2. 操作镜像
      1. Hello World - 拉取镜像,创建实例
      2. 列出和删除镜像
      3. 定制镜像
    3. 操作容器
      1. Hello World - 创建容器,列出容器,删除容器
      2. 运行持久化
      3. 后台运行和进入容器
      4. 终止和重新启动容器
      5. 指定名称
      6. 暴露端口
  5. Docker + VSCode构建一个python开发环境
    1. 创建包含python开发环境的容器
    2. 使用VS Code一键在容器中运行脚本
  6. 使用Nginx镜像快速搭建一个静态服务器
    1. 挂载磁盘目录

基本概念

容器(container),镜像(image),卷(volume)

用面向对象的思想来理解,容器就是实例,镜像就是类,卷就是数据存储区。比如ubuntu:18.04就是一个简单的镜像,用这个镜像可以按照需求创建不同的容器,一堆的容器可以共用一个卷作为某一存储区。

docker-desktop安装与配置

下载地址:https://www.docker.com/products/docker-desktop

下载后直接安装,启动的时候如果电脑条件没准备好,会有相应的提示:https://docs.docker.com/desktop/windows/troubleshoot/#virtualization

启动虚拟化

打开任务管理器-性能-CPU,查看是否开启虚拟化。如果未开启需要在BIOS界面开启。

image-20211210210109206

开启WSL2

如果不使用WSL2作为后端或者已开启WSL2,可以跳过这一步。

搜索“启用或关闭Windows功能”,找到“虚拟机平台”和“适用于Linux的Windows子系统”,勾上,确定。

image-20211210210300864

更新WSL2内核

WSL安装 | 第四步

启动Docker Desktop之后如果遇到如下提示,则需要安装WSL2内核的更新。

image-20211210211442094

下载.msi安装后重启Docker Desktop即可解决。

文件路径

docker桌面如果使用WSL2作为后端,会在wsl中创建两个发行版用来存放数据,这两个发行版的数据文件默认存放路径为:

1
2
C:\Users\[用户名]\AppData\Local\Docker\wsl\data\ext4.vhdx
C:\Users\[用户名]\AppData\Local\Docker\wsl\distro\ext4.vhdx

安装镜像或者在容器中安装一些库时,会存放在data下面的数据文件中,即便删除了容器或镜像也依旧存在着,需要手动清理(见后续章节)。

加速下载:设置国内镜像

https://vuepress.mirror.docker-practice.com/install/mirror

打开Docker Desktop,设置,Docker Engine,在json文件中添加registry-mirrors字段以设置国内加速镜像地址。

1
2
3
4
5
6
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
]
}

可以通过docker info查看是否添加镜像成功。

存储控制与空间清理

释放win10子系统WSL2的磁盘空间

随着不同镜像的定制和安装,.vhdx文件会越来越大,而且不随着镜像的删除而变小。

1
C:\Users\[username]\AppData\Local\Docker\wsl\data\ext4.vhdx

清理空间的手段有两种,一是针对现有文件进行压缩,另一种则是初始化归零。后者直接在Docker Desktop中-Troubleshoot-Clean / Purge data完成,前者需要通过wsldiskpart命令实现。下面介绍前者——文件压缩方法。

首先通过Docker Desktop清空所有数据,得到最原始的磁盘大小,接着拉取一个镜像,可以看到磁盘体积变大。

image-20211212010433743

此时把镜像删除,磁盘体积并没有发生更改。关闭Docker Desktop,接着在命令行关闭wsl并启动diskpart。

1
2
wsl --shutdown
diskpart

接着在弹出的命令窗口中进行如下操作:

1
2
3
4
5
6
7
8
9
10
DISKPART> select vdisk file="C:\Users\[username]\AppData\Local\Docker\wsl\data\ext4.vhdx"
# DiskPart 已成功选择虚拟磁盘文件。
DISKPART> attach vdisk readonly
# 100 百分比已完成
# DiskPart 已成功连接虚拟磁盘文件。
DISKPART> compact vdisk
# 100 百分比已完成
# DiskPart 已成功压缩虚拟磁盘文件。
DISKPART> detach vdisk
# DiskPart 已成功分离虚拟磁盘文件。

或者直接进行压缩操作。(可能会提示:“虚拟磁盘服务错误: 所请求的操作需要以只读方式 连接虚拟磁盘。”)

1
2
3
4
5
DISKPART> select vdisk file="C:\Users\[username]\AppData\Local\Docker\wsl\data\ext4.vhdx"
# DiskPart 已成功选择虚拟磁盘文件。
DISKPART> compact vdisk
# 100 百分比已完成
# DiskPart 已成功压缩虚拟磁盘文件。

压缩完成后,重新启动Docker Desktop,再去检查文件。看到磁盘文件体积相较于压缩前有所减小,但整体仍大于一开始创建时的体积,猜测可能是存放了一些软件运行时状态导致的,无伤大雅。

image-20211212012743761

常用命令行操作

Docker从入门到实践 | gitbook

Docker从入门到实践 | 国内镜像

查看信息

查看docker信息

1
docker info

查看wsl信息

1
2
3
4
wsl -l -v
NAME STATE VERSION
* docker-desktop Running 2
docker-desktop-data Running 2

查看系统空间占用

1
2
3
4
5
6
docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 24 0 1.992GB 1.992GB (100%)
Containers 1 0 62.82MB 62.82MB (100%)
Local Volumes 9 0 652.2MB 652.2MB (100%)
Build Cache 0B 0B

操作镜像

Hello World - 拉取镜像,创建实例

获取镜像。从默认的镜像仓库library/ubuntu获取标签为18.04的镜像。

1
docker pull ubuntu:18.04

运行镜像。这行命令以ubuntu:18.04作为镜像,创建了一个容器;-it是复合命令,意为交互式终端,-i表示交互式,-t表示终端,若只有-i,则不显示终端,若只有-t,则显示终端后无法输入;--rm表示运行结束后删除容器,不加这个参数则需要手动删除;bash表示容器创建后执行的命令,也可以用sh命令。

1
docker run -it --rm ubuntu:18.04 bash

创建容器后,将在命令行进入linux的终端,此时输入cat /etc/os-release,可以查看系统信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\> docker run -it ubuntu:18.04 bash
root@06277a511296:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.6 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.6 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

列出和删除镜像

列出镜像。一个镜像可以对应多个标签(TAG),如下面例子最下方两个所示,唯一标识符是ID。

只列出ID,只需加上-q参数。

1
2
3
4
5
6
7
8
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 5f515359c7f8 5 days ago 183 MB
nginx latest 05a60462f8ba 5 days ago 181 MB
mongo 3.2 fe9198c04d62 5 days ago 342 MB
<none> <none> 00285df0df87 5 days ago 342 MB
ubuntu 18.04 329ed837d508 3 days ago 63.3MB
ubuntu bionic 329ed837d508 3 days ago 63.3MB

上方镜像列表有一行为<none>,意为“悬虚镜像(dangling image)”,没有实际价值,可以通过命令docker image prune删除。

关于中间层镜像、筛选列出、以特定格式显示等更丰富的功能,后续用到再补充。

删除镜像:根据列出的镜像ID、名称等进行删除,可以搭配列出使用。

1
2
3
docker image rm redis  # 删除名称包含redis的镜像
docker image rm 5f515359c7f8 05a60462f8ba fe9198c04d62 # 根据ID删除多个镜像
docker image rm $(docker image ls -q redis) # 删除筛选出来(名称包含redis)的镜像

定制镜像

定制镜像前,先来做这么一个实践:拉取一个nginx的镜像,启动作为web服务器,并修改其中首页的内容。

1
2
docker pull nginx  # 拉取镜像
docker run --name webserver -d -p 80:80 nginx # 创建并运行容器

执行完以上两步,打开浏览器输入127.0.0.1,可以看到nginx的首页。

image-20211212003422469

可以通过如下命令查看首页的页面代码:

1
2
docker exec -it webserver /bin/bash  # 进入容器操作终端
cat /usr/share/nginx/html/index.html # 查看nginx默认首页页面代码

使用以下命令修改首页内容。

1
/bin/echo '<h1>Hello, World</h1>' > /usr/share/nginx/html/index.html

此时再进入浏览器,刷新页面后看到内容发生了改变。

image-20211212003518270


将以上的步骤记录下并保存到Dockerfile中,即完成了镜像的定制。创建一个空目录,添加名为Dockerfile的文本文件,在其中写入:

1
2
FROM nginx
RUN /bin/echo '<h1>Hello, World</h1>' > /usr/share/nginx/html/index.html

接着在该目录下打开终端,输入如下命令构建镜像。注意最后的.表示当前路径,作为构建的上下文(context),不可遗漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker build -t nginx:my-build .

[+] Building 0.9s (6/6) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 121B
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [internal] load metadata for docker.io/library/nginx:latest
=> [1/2] FROM docker.io/library/nginx
=> [2/2] RUN /bin/echo '<h1>Hello, World</h1>' > /usr/share/nginx/html/index.html
=> exporting to image
=> => exporting layers
=> => writing image sha256:6800734708deddc231dbd96b22cd5d9b782ea6d80e53a350156588bf1385d91d
=> => naming to docker.io/library/nginx:my-build

查看镜像列表,可以看到我们刚刚创建的镜像,名为nginx,标签是my-build

1
2
3
4
5
6
docker image ls -a

REPOSITORY TAG IMAGE ID CREATED SIZE
nginx my-build 6800734708de 4 minutes ago 141MB
nginx latest f652ca386ed1 9 days ago 141MB
ubuntu 18.04 5a214d77f5d7 2 months ago 63.1MB

运行一个容器,并进入浏览器查看,即可看到,通过定制化后的镜像运行的容器,首页内容已经是我们想要的样式。

1
docker run -d -p 8080:80 nginx:my-build

image-20211212003253766

关于定制镜像的更多内容请参考教程:https://vuepress.mirror.docker-practice.com/image/build/

操作容器

Hello World - 创建容器,列出容器,删除容器

创建容器。根据某个镜像创建容器,接着执行一条命令,执行完退出容器。

1
docker run ubuntu:18.04 /bin/echo "Hello World"

列出容器:

1
2
3
4
5
6
7
docker container ls  # 列出正在运行的容器
docker container ls -a # 列出所有容器,包括不在运行的

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
72c82c8a430b ubuntu:18.04 "bash" 10 seconds ago Exited (0) 10 seconds ago vigilant_solomon
009cab741133 ubuntu:18.04 "bash" 13 seconds ago Exited (0) 13 seconds ago musing_kalam
fc3a75dbaff4 ubuntu:18.04 "bash" 17 seconds ago Exited (0) 16 seconds ago modest_tharp

删除容器。根据列出的容器ID或名称,删除一个或多个容器。

1
docker container rm 72c82c8a430b 009cab741133 fc3a75dbaff4

删除没有在运行的容器

1
docker container prune

运行持久化

run命令用于创建容器后执行一条命令,持久化运行有两种情形,一是执行的命令存在无限循环,二是使用了交互式终端-it

后台运行和进入容器

后台运行只需要在启动时加上参数-d,配合交互式终端保持持久化,命令如下:

1
2
3
docker run -dit ubuntu:18.04
docker run -dit ubuntu:18.04 /bin/bash
5ffeb933aabec239fc79b9331af8aa4f1e85d4661a3ee6e46e00ecb39335887f

返回一个ID,可以通过如下命令重新进入运行的容器中:

1
docker exec -it 5ffeb933aab /bin/bash

终止和重新启动容器

1
2
3
4
5
docker container stop 5ffeb933aab
5ffeb933aab

docker container start 5ffeb933aab
5ffeb933aab

指定名称

--name

1
docker run --name my_container -d ubuntu:18.04

暴露端口

暴露指定端口:-p + hostPort:containerPort

1
docker run --name my_container -p 80:80 -d ubutntu:18.04

更详细的用法用到再补充。

Docker + VSCode构建一个python开发环境

创建包含python开发环境的容器

拉取镜像

1
docker pull python:slim

创建容器

1
docker run -dit --name py python:slim

进入终端查看

1
2
3
4
5
docker exec -it py bash
python -V
# Python 3.10.1
pip -V
# pip 21.2.4 from /usr/local/lib/python3.10/site-packages/pip (python 3.10)

使用VS Code一键在容器中运行脚本

python程序安装完,如何调用其中的解译器运行我们的.py脚本?

方法一:直接在容器中创建脚本,编写代码(不建议这么干,因为除了脚本还可能有数据,并不推荐向容器中写入数据);

方法二:挂载一个容器外部的代码文件夹,编写完代码进入容器运行代码;

很显然方法二是一个很不错的方案,但是仍有一点欠缺,能否在编写完脚本代码后通过一个简单的操作,将代码文件在容器内的解译器中编译执行?很显然,这个操作是存在的,借助Visual Studio Code Remote - Containers扩展,“在容器中打开当前文件夹”,即可在VS Code中编写完代码后,直接点击一个运行按钮来调用容器内的python解译器来执行脚本。

参考:https://code.visualstudio.com/docs/remote/containers#_quick-start-open-an-existing-folder-in-a-container

安装完扩展Visual Studio Code Remote - Containers后,创建一个文件夹,写入一个main.py,输入以下代码:

1
2
3
4
5
def main():
print('Hello World')

if __name__ == '__main__':
main()

接着在文件目录添加文件Dockerfile,写入:

1
FROM python:slim

接着打开左侧的“远程资源管理器”或按下快捷键Ctrl + Shift + P,找到Open Folder in Container,点击后选择脚本代码所在的文件夹,然后选择From ‘Dockerfile’,VSCode就会自动检测目录下的Dockerfile并以此重新构建一个镜像并创建卷(如果不存在名为vscode的卷的话),接着创建一个容器并启动它,此时的左下角就会有如下提示

image-20211214221805518

在VSCode中打开终端,可以看到终端路径已经是容器中的路径了。运行脚本,就相等于在容器中运行。

image-20211214221856967

使用Nginx镜像快速搭建一个静态服务器

知识点:挂载静态文件路径和配置文件路径

参考:https://vuepress.mirror.docker-practice.com/appendix/repo/nginx/

拉取nginx镜像:

1
docker pull nginx

nginx的配置文件入口:/etc/nginx/nginx.conf

nginx服务器配置文件目录:/etc/nginx/conf.d/,其中的所有.conf文件都会被nginx.conf引用(include)进来;

nginx默认的静态目录:/usr/share/nginx/html/

挂载磁盘目录

使用--mount关键字

1
docker run -d -p 3303:80 --name webserver --mount type=bind,source=D:\dist,target=/usr/share/nginx/html,readonly --mount type=bind,source=D:\nginx\conf.d,target=/etc/nginx/conf.d nginx

以上命令:创建一个名为webserver的容器;绑定容器的80端口到本机的3303;挂载本机D:\dist目录为容器的/usr/share/nginx/html目录,容器权限为只读;挂载本机D:\nginx\conf.d目录为容器的/etc/nginx/conf.d目录,默认权限为读和写;使用的镜像名称为nginx。

挂载目录也可以挂载单个文件,docker会根据sourse指定的路径判断是文件还是文件夹。

以上挂载的配置可以通过docker inspect webserver查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
docker inspect webserver
....
"Mounts": [
{
"Type": "bind",
"Source": "D:\\dist",
"Target": "/usr/share/nginx/html",
"ReadOnly": true
},
{
"Type": "bind",
"Source": "D:\\nginx\\conf.d",
"Target": "/etc/nginx/conf.d"
}
],
...

除了使用--mount关键字,还可以用-v关键字,写法更简便

1
docker run -d -p 3303:80 --name webserver -v D:\dist:/usr/share/nginx/html:ro -v D:\nginx\conf.d:/etc/nginx/conf.d nginx

查看挂载配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker inspect webserver
....
"Mounts": [
{
"Type": "bind",
"Source": "D:\\nginx\\conf.d",
"Destination": "/etc/nginx/conf.d",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "D:\\dist",
"Destination": "/usr/share/nginx/html",
"Mode": "ro",
"RW": false,
"Propagation": "rprivate"
}
],
...