docker 快速入门实操教程
docker,启动!
如果安装好docker不知道怎么使用,不理解各个名词的概念,不太了解各个功能的用途,这篇文章应该会对你有帮助。
前置条件:已经安装docker并且docker成功启动。
最终目的:使用docker替换本地安装的程序并迁移数据(redis、MySQL)。
理解概念
docker官方提供了一个分发平台DockerHub,可以从上面拉取已经提供好的镜像直接构建容器运行。
这个过程会涉及到docker的一些概念,在刚接触的时候比较抽象,这里以烘焙出一个蛋糕为例子说明一下:
- Dockerfile: 蛋糕的配方。配方上详细列出了需要的材料(如面粉、糖、鸡蛋)以及烘焙的步骤(如先将面粉和糖混合,然后加入鸡蛋搅拌)。
- 镜像(Image): 按照配方做出了一个半成品蛋糕,这就是蛋糕的"镜像" 。这个蛋糕可以被任何人复制,每一个复制品都会和原蛋糕一模一样。
- 容器(Container): 将半成品蛋糕烘焙后,得到一个可食用的蛋糕。可以根据同一个镜像制作出很多个完全一样的蛋糕,也可以在烘焙时自己加一些材料。每个蛋糕都是独立的,和其他蛋糕没有关联。
所以从DockerHub拉取镜像并且跑起来的过程就可以理解为:
- 镜像提供者编写好了配方(
Dockerfile
),将其制成(构建
)了半成品蛋糕(镜像
)。 - 用户购买(
拉取
)这个半成品蛋糕。 - 烘焙(
创建
)后得到了一个可食用的蛋糕(容器
),食用蛋糕(运行容器
)。 - 通常创建容器和运行容器都会归拢在同一步:创建并运行。
还有另外两个概念,在使用过程中不会接触到,但是对编写Dockerfile会很有帮助:
- 层(Layers): 根据配方制作蛋糕的每一个步骤(如把面粉和糖混合在一起,这是第一层;加入鸡蛋搅拌,这就是第二层)。层这个概念在使用时并不重要,甚至不会接触到,可以在自己编写Dockerfile时再去深入理解。
- 缓存(Cache): 现在有两个配方,配方中都有一个步骤(
层
)是购买A型号烤箱,根据第一个配方制作时已经买好了该烤箱,那么在根据第二个配方制作时就可以复用该烤箱以节省时间,这就是缓存的概念。
创建容器
每一步都提供了docker desktop(简称桌面版)的操作截图和终端命令,桌面版界面友好适合上手,终端命令的可操作性更强。
从DockerHub拉取MySQL镜像到本地,这一步可能会因为网络原因失败或者很慢,可以配置其他镜像源或者使用代理,网上教程很多。
如果不想使用桌面版,也可以选择通过终端拉取
bash
docker pull mysql:latest
其中 Tag
中的 latest
,这表示这是最新的版本,如果有需要,也可以去找指定的版本拉取。
docker pull
这一步并不是必须的,如果本地没有你指定的镜像,在 docker run
时会自动拉取。
拉取完成后,通过 docker run
创建容器并运行。MySQL的容器直接启动会失败,提示需要配置密码,所以需要先进行一些配置。
该操作对应的终端命令
bash
docker run --name mysql_8.3.0 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:latest
# --name 容器名称
# -p 宿主机端口:容器内部端口
# -e 环境变量
# -d 后台运行
容器名称:可以随意定义,但是不能重复,这里使用的是 镜像名_版本号
的方式。
端口映射:将宿主机的 3306
端口映射到容器的 3306
端口,宿主机上的其他进程通过该端口才能访问到容器内的服务。如果不配置端口映射,则只能在容器内部访问服务或通过虚拟网络让容器可以相互通信。所以即使redis是开箱即用的,通常也还是会为其配置端口映射。
如果宿主机已经运行了MySQL并且占用了 3306
端口,需要先停掉宿主机的MySQL服务或者选择其他未被占用的端口。
环境变量:配置 MYSQL_ROOT_PASSWORD
的作用则是指定root用户的密码,这是由MySQL的镜像创建者约定好的,不同的镜像配置项会有所不同。
至此,容器已经成功创建并且运行起来了。
启停容器
容器已经创建好后就不再适用于 docker run
命令了, docker run
命令主要是用于创建新的容器并运行,如果需要启动已经存在的容器,则使用 docker start
命令。
终端命令
bash
# 启动容器
docker start mysql_8.3.0
# 停止容器
docker stop mysql_8.3.0
# 重启容器
docker restart mysql_8.3.0
发现问题
现在MySQL容器已经成功运行了,但是遇到了如下几个问题
- 如何获取到镜像中程序具体的版本
- 如何将数据持久化到本地,并且让电脑和宿主机比较方便的交换文件
- 容器没有持续运行/自动关闭
查看镜像内软件版本号
因为MySQL镜像的 Tag
是 latest
,并没有明确指出具体的版本号,所以需要手动查看,两种方案:
1、进入容器终端查看版本
进入容器的终端
执行该软件查看版本的指令,比如MySQL的就是
bash
mysql -V
终端命令
bash
docker exec mysql_8.3.0 mysql -V
# exec 在容器中执行命令
但是问题就来了,如果需要版本号是为了给容器命名,这种方案需要先运行容器,将容器删除,再重新创建容器,很麻烦。
2、通过镜像中的指令查看
通常Dockerfile中会指明版本号,我们可以直接点开镜像查看
终端命令
bash
docker inspect mysql:latest
这个方式虽然比较方便,但是需要进行推测,并非一定正确。
数据持久化保存
如果数据不持久化保存的话,当容器关闭后,数据也就丢失了。
docker为持久化提供了三种方式:
-
bind mount
把宿主机目录映射到容器内,双向文件传递。适合文件变动比较频繁的场景,比如存放代码、配置文件,传递文件到容器内,从容器内取出文件等。
-
volume(卷)
由容器创建和管理,创建在宿主机中,官方推荐,更高效,Linux 文件系统。宿主机上并不能操作卷中的数据,适合存储不需要关心的数据,如数据库数据。
-
tmpfs mount
适合存储临时文件,存宿主机内存中。不可多容器共享。
MySQL镜像指定了一个 volume
卷用于持久化数据,但是如果创建容器时没有主动指定卷的名字,其就是一个匿名卷,匿名卷会随机生成一个名称,当挂载该卷的容器被删除后,该卷也会被删除。
在docker中,如果只指定了名字则是 volume
,如果指定了具体目录就是 bind mount
。
所以在创建容器时指定一下卷的名字,同时为了方便传递文件到容器中(如将sql文件),还通过 bind mount
方式映射了一个宿主机的本地目录。
终端命令
bash
docker run --name mysql_8.3.0 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -v D:\mount:/pc_mount -v=mysql_volume:/var/lib/mysql -d mysql:latest
# -v 挂载目录
在挂载目录时,如果你指定的目录不存在于容器中,则会自动创建,这里的 /pc_mount
目录就会自动被创建。
指向的 /var/lib/mysql
目录也可以通过Dockerfile查看,当指定的 volume
路径和Dockerfile指向的路径一致,则该卷就不会被挂载为匿名卷了。
这样容器中的数据就会一直保存在宿主机中,即使容器被删除也不影响。
保持容器运行
当在桌面版运行ubuntu等容器时,会发现容器启动后就停止了,进入 Exited
状态,如果想要容器持续运行,就需要需要在容器内部执行一个持续运行的进程。这里桌面版已经不能满足需求了,需要用到终端执行。
终端命令
bash
docker run -it --name ubuntu_22.04 ubuntu:latest
# -i 能够接受用户输入
# -t 分配一个虚拟的终端或控制台
通过 -t
指令可以让容器持续运行不会关闭, -i
指令可以让打开的控制台能够接受用户输入。
迁移数据
现在需要将宿主机中MySQL数据迁移到容器中,打算采用navicat的数据迁移工具,那么就需要同时运行两个数据库,端口同为3306会冲突,宿主机上MySQL端口改起来并不方便,容器创建后端口也不能修改,那么就可以使用数据挂载的方式。
迁移方案:
- 停止运行端口为3306的MySQL容器。
- 新创建一个MySQL容器,端口指定为3305(或其他任意未被占用的端口),指定
volume
卷的名称和端口3306的容器一致。 - 迁移数据。
- 删除端口为3305的容器,运行端口为3306的容器,发现数据迁移成功。
虚拟网络
每个docker容器都运行在自己的环境中,互不干扰,所以上述内容中都依赖宿主机的端口映射进行容器通信。但是有些时候我们只要让这个项目能在宿主机上访问到,并不在意其所依赖的服务是否能够被宿主机操作和管理。就可以通过docker提供的虚拟网络实现容器之间的通信,再映射项目入口到宿主机即可。
桌面版并没有为虚拟网络提供较好的GUI支持,所以通过命令行完成虚拟网络的配置。
查看已存在的虚拟网络
bash
docker network ls
默认已经存在了三个虚拟网络,这是由docker创建的,对应着不同的网络驱动类型,驱动类型的区别如下:
- Bridge网络: 默认值。容器在独立的网络空间运行,可以相互通信并访问外部网络。容器内服务能通过端口映射被外部访问。
- Host网络: 容器共享宿主机的网络空间,不再需要端口映射,直接使用宿主机的端口。这种模式提供了最高的网络性能,但是失去了隔离性。
- None网络: 容器拥有自己的网络空间,但不配置任何网络接口。它只有本地回环接口,没有任何外部网络访问能力,提供了最高的网络隔离性。
创建名为 test_net
的网络
bash
docker network create test_net
在该网络下创建两个容器
bash
docker run --name redis_temp --network=test_net -d redis:latest
docker run --name redisinsight -p 8001:8001 --network test_net -d redislabs/redisinsight:latest
创建了redis容器,但是并没有为其映射端口。所以现在在宿主机中并不能访问到这个redis容器。
创建了redisInsight容器并且映射了8001端口,这是一个redis的GUI工具,用于测试是否可以通过工具访问到该redis容器。
访问 http://localhost:8001/ 进入redisInsight的主页,添加一个redis数据库。
在docker内部的DNS服务会自动将容器名称解析为容器对应的IP地址(即容器名称就是域名),所以主机地址填写容器名称即可。
更改已存在的容器的连接的网络
bash
docker network connect 网络名称 容器名称
连接成功,这样既可以操作容器内的redis数据,又不会占用宿主机自身的redis应用抢占端口。
结语
至此,你已经掌握了docker的基础操作,后续还有编写 Dockerfile、构建镜像、Docker-Compose等操作,为了避免篇幅过长,打算单开一个文章。