工作需要使用 Kubernetes,那么前置还要学习一下 Docker。
学习资料是阮一峰的教程。以及 Docker 官方文档。以及狂神说的 b 站视频教程。
1. 环境配置的问题
想要运行一个应用程序,就需要一些环境配置。但是在开发过程中,经常会有在某一个机器上可以跑了,但是在另一个机器上不行的问题。为了解决这个问题,就产生了一些技术。
1.1 虚拟机
虚拟机就是为了解决这个问题而产生的一个技术,虚拟机简单来书就是在一个操作系统上模拟另一个操作系统。在虚拟机上搭建环境,就可以保证环境一致。
当然虚拟机具有一些缺点:
- 资源占用:虚拟机只要开着就会占用资源,并不会因为虚拟机上只使用一小部分资源就只占用这一小部分资源。
- 各种冗余步骤:因为虚拟机就是一个完整地系统,因此会需要类似用户登录等各种冗余操作。
- 启动慢:虚拟机也要和普通操作系统一样启动,花费时间较长。
1.2 Linux 容器
为了解决虚拟机的各种问题,Linux 容器(Linux Containers,LXC)出现了。LXC 并没有新弄一个操作系统,而仅仅是对进程进行隔离,对于某个容器来说,它接触到的资源都是虚拟的,从而实现与底层系统的隔离。
容器的优点:
- 启动快:由于并不需要启动一个操作系统,容器会比虚拟机的启动快很多,只是启动一个进程罢了。
- 资源占用小:没有操作系统占用资源,容器占用的就是需要的资源,而且多个容器之间可以共享资源。相比之下虚拟机的资源是独享的。
- 体积小:顾名思义。
2. Docker 是什么
Docker 是一种对容器的封装,提供一些接口,让容器的使用更简单。
Docker 会打包应用程序以及相关的依赖,生成一个文件。运行这个文件就会生成一个虚拟容器。程序就在这个虚拟容器上运行。
2.1 Docker 简化后的流程
有了 Docker 之后,开发部署使用的流程就可以简化为:
开发应用 -> 用 Docker 打包应用和环境,生成镜像 -> 上传到 Docker 仓库 -> 下载镜像,直接使用
复制代码
2.2 Docker 的好处
由于能够把应用进行隔离,应用之间不会互相影响。因此可以在一个服务器上运行多个容器,能够最大化服务器的使用效率。
3. Docker 的用途
- 提供一个一次性环境:比如测试软件,单元测试,等等。
- 提供弹性的云服务:容器可以随开随关,因此很适合动态扩容和缩容。
- 组件微服务架构:一台机器上可以运行多个容器,因此可以跑多个服务,因此可以模拟出微服务架构。
4. Docker 安装以及踩坑
遇到的坑:
- wsl2 联网问题:wsl2 联网有各种问题,反正退回wsl1 就可以联网了。但是 wsl1 不支持 docker。
- 所以我用 wsl1 下载好 docker 后,又换回 wsl2。
- 但是 docker 使用过程中难免会用到网络,因此还是坑。
- 使用 docker desktop for windows 倒是可以,但就失去意义了。
- 所以我干脆换成虚拟机了,没有任何问题。
5. Docker 使用
-
docker 的使用需要 root 权限,为了避免每次都要输 sudo,可以把用户加入 docker 用户组。docker 用户组具有和 root 一样的权限。
-
docker 是 server-client 架构,使用前需要启动服务。
# service 用法 $ sudo service docker start # 或者 systemctl 用法 $ sudo systemctl start docker 复制代码
6. image 文件(又称镜像/映像)
docker 把应用以及依赖打包成 image 文件,这个 image 文件使用一个独立的文件系统。通过这个 image 文件可以生成容器实例。一个 image 文件可以有多个实例。可以把 image 文件看成类,容器看做实例。
和类一样,image 文件可以继承其他的 image 文件。再加上自己的设置。
一般来说都是使用别人做好的 image 文件,拿来修改。
自己制作好的 image 文件可以上传到 Docker hub,以供别人使用。
7. 容器文件
正如之前所说,容器文件是 image 文件的实例。容器文件也是一个文件,容器文件可能运行完就结束,也可能一直运行需要手动关闭。关闭并不代表删除,终止后的容器文件会占用空间,根据需要可以手动删除。
8. Dockerfile 文件
Dockerfile 是一个文本文件,用于生成 image 文件。
在 Dockerfile 同目录下还可以建立一个 .dockerignore 文件,类似于 .gitinore,总之就是不打包这里声明的文件。
Dockerfile 的写法如下:
# FROM 表示,继承自哪个 image,这里继承的是官方的 node image,那么该 image 文件生成的容器实例是可以运行 node 命令的。image 版本为 8.4
FROM node:8.4
# COPY 表示,把主机的 . 目录里的文件复制到 image 文件的 /app 目录里(当然,要先忽略掉 .dockerignore 里的文件)
COPY . /app
# WORKDIR 表示,接下来的工作目录是 /app
WORKDIR /app
# RUN 表示,先通过 npm install 安装所有的依赖后,再打包进 image 文件。
RUN ["npm", "install"]
# EXPOSE 表示把容器的 3000 端口暴露出来,以便和容器外部交互(本文后面会有例子)
EXPOSE 3000
# CMD 命令,表示在容器生成后执行的命令,这是和 RUN 的区别,一个在 image ,一个在 容器
CMD node 01.js
复制代码
创建好 Dockerfile 文件后,就可以用 docker image build
生成 image 文件了。
# 不加标签(默认 latest)
$ docker image build -t koa-demo .
# 加标签
$ docker image build -t koa-demo:0.0.1 .
# 注意最后的那个 . ,表示 Dockerfile 文件所在位置
复制代码
8.1 image 列表
docker image ls
或者 docker images
皆可
8.2 image 下载
可以直接从 registry 里下载 image 文件。
可以在浏览器里打开 dockerhub 再搜索 image,也可以直接在命令行用 docker search 搜索。
搜索完毕确认要下载的 image 后,使用 docker pull <image>:[tag]
下载镜像文件,不声明 tag 则下载最新版 latest。
8.2.1 联合文件系统 (Union File System)
下载 image 文件的时候,会发现每次都是下载很多个小文件。这是因为 image 文件的资源可以共用。如果之后下载了其他的 image 文件,有一些小文件和之前下载的小文件重合的时候,就无需再下载。
8.3 image 删除
- 删除单个容器
docker rmi -f <REPOSITORY 或者 IMAGE ID>
- 删除多个容器
docker rmi -f <image1> <image2> <image3>
- 删除所有容器
docker rmi -f $(docker images -aq)
9. 容器生成以及运行
docker container run
表示这个命令。
$ docker container run -p 8000:3000 -it koa-demos:first-try /bin/bash
复制代码
解释一下这个命令:
- 首先是 docker container run,这个就是命令没什么说的
- -p 参数,表示端口的对应情况。这里的 8000:3000 的意思是主机的 8000 端口对应着容器的 3000 端口。之前再 Dockerfile 里写的
EXPOSE 3000
,意思就是容器的 3000 端口是暴露出来的,因此这里可以通过主机 8000 的端口连接过去。 - -it 参数,表示容器的 shell 对应主机的 shell,表示在主机窗口输入的命令会传到容器。
- koa-demos:first-try 指的是容器的名字,冒号后跟上标签名
- /bin/bash 指的则是容器启动后执行的第一个命令,这里执行 bash,保证容器运行后能够使用 shell。
以上命令执行完毕后,会发现终端的前缀改变了,我们进入了容器的 bash 终端里。(如果不加 -it 参数就不会进入这里)
root@xxxxxx:/app#
复制代码
此时我们已经在容器中了,使用 docker container ls
可以看到该容器正在进行中(当然由于这个终端窗口被容器占用了,你得换一个终端窗口)。
9.1 退出容器
- 如果是在 run 之后立刻进入容器:
- 如果想要退出时关闭这个容器,可以在另一个终端中使用
docker container kill <id>
命令,也可以在容器中输入 exit(或者快捷键 ctrl+d)。 - 如果只是想退出,并不想关闭容器,则按住 ctrl + p + q。
- 如果想要退出时关闭这个容器,可以在另一个终端中使用
- 如果容器在后台运行中,通过 exec 进入容器:
- 使用 exit 和 ctrl+p+q 是一样的。想要关闭容器,就要使用 stop 或者kill 才行。
- 如果容器在后台运行中,通过 attach 进入容器:
- 此时和 run 一样, exit 会在退出时关闭容器,ctrl+q+p 不会
9.2 重新进入容器
上一节提到了 exec 和 attach 命令,这两个命令都可以用于进入正在运行的容器的。两者之间的区别在于:
- exec 是进入容器后,开启一个伪终端。这也是为什么通过 exec 进入后 exit 并不会终止容器了,因为推出的只是新建的这个终端。之前那个终端还在那儿呢。比如说在容器中执行一个不断输出日志的脚本。如果使用 exec 进入该容器,是看不到日志的,因为日志输出在另外一个终端上。
- 注意 exec 本身作用是在容器中运行一个命令,只是可以用来进入容器罢了。所以要加 -it 和 /bin/bash
- attach 则是直接和容器的输入输出流进行对应。此时输入 exit 会在退出的时候关闭容器。
9.3 容器删除
可以在使用 docker container rm <id>
手动删除。
如果需要的话,也可以在创建容器的时候添加 –rm 参数,表示容器关闭后自动删除。一般用于测试容器使用。
$ docker container run --rm -p 8000:3000 -it koa-demos:firt-try /bin/bash
复制代码
10. 一些其他命令
10.1 docker container start
如果使用 docker container run <image>
,就会生成一个新的容器。如果想使用之前运行过已经终止的容器,则使用 docker container start <containerID>
。
10.2 docker container stop
关闭容器
- 如果使用
docker container kill <containerID>
,则会发出一个 SIGKILL 信号,强制终止容器的运行,所有正在进行的数据都会丢失。 - 如果使用
docker container stop <containerID>
,则会向容器发出一个 SIGTERM 信号,容器收到后会做一些收尾清理工作,再终止运行,或者不理会该信号。
10.3 docker container cp
拷贝文件
如果想拷贝容器的文件到外面,则可以使用这个命令。用法如下:
$ docker container cp <containerID>:[path/to/file] [path/in/host]
复制代码
10.4 后台启动容器
docker container run -d
-d 参数表示后台启动,这里有坑。如果一个容器启动后,没有前台进程,docker 会终止掉这个容器。 因此如果运行 docker container run -d ubuntu
,然后用 docker container ls
会发现看不到该容器,只有 -a 能看到关闭后的该容器。
10.5 日志
使用 docker logs -f -t <containerID>
可以看到该容器运行后进行过的操作。
-t 指的是查看时间戳
-f 指的是跟踪实时输出,比如说使用脚本一直输出,然后使用日志查看的时候,就能够看到结果会一直更新。
10.6 查看进程
docker top <containerID>
10.7 查看元数据
docker inspect <ID>
11. Docker 的底层原理
- Docker 采用的是 client-server 结构。其中 daemon 进程在主机上,客户端通过 socket 来访问这个进程。
- server 接受到客户端的命令后,便会执行该命令。
11.1 Docker 的架构
- 其中注意 repository 和 registry 的区别。repository 是实际存储 image 的地方。而 registry 只是一个 pointer 的集合。在使用
docker images
的时候,第一列就是 REPOSITORY。