前言
在上一篇文章捣鼓一下组件库演示文档,要实现一个网站内的搜索功能,当时查找资料,看到下面这一行指令,命中了我的知识盲区。今天不上班,正好利用空闲时间,学习一下docker的用法。现在我们进入主题。
docker run -it --env-file=.env -e "CONFIG=$(cat ./crawlerConfig.json | jq -r tostring)" algolia/docsearch-scraper
docker简介
Docker是一个开源的应用容器引擎,也是一个软件平台,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,该容器包含了应用程序的代码、运行环境、依赖库、配置文件等必需的资源,通过打包好的标准化容器就可以实现与平台解耦的快速的自动化部署,无论部署时的环境如何,容器中的应用程序都会运行在同一种环境下。
Docker是一个CS架构的系统,Docker客户端和守护程序可以在同一系统上运行,也可以是Docker客户端在本地,Docker守护程序在服务器上。 Docker客户端通过Socket连接访问服务器上的Docker守护进程。Docker守护进程接收客户端的命令,并按照命令,管理运行在服务器上的docker容器(docker容器是一个运行时环境,可以简单理解为进程运行的集装箱)。
使用docker的好处
- 利用系统资源更高效, 启动更快
相比传统的虚拟机技术,相同配置的主机,采用docker技术可以运行更多的应用。docker容器应用,直接运行在宿主内核,无需启动完整的操作系统。相对于虚拟机的分钟级启动,docker可以做到秒级启动.
特性 | docker容器 | 虚拟机 |
---|---|---|
系统支持量 | 单机支持上千个容器 | 一般几十个 |
启动速度 | 秒级 | 分钟级 |
镜像大小 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于原生 |
- 迁移更轻松
我们知道,在安装操作系统的时候,会根据硬件的不同编译出不同的内核,而每台电脑的硬件配置几乎不会完全一样,因而直接把一台电脑的操作系统文件考到另一台一般是没法启动的。而docker容器是一个应用层抽象,运行在操作系统之上的,确切地说是操作系统内核之上(如下图左),面对的是操作系统提供的接口,属于进程级别;
docker镜像提供了除内核外的完整运行时环境,避免了开发,测试,生产因为运行时环境不一致造成bug在生产环境才被发现。由于docker确保了执行环境的一致性,使得应用的迁移更容易。docker可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云、甚至是笔记本、其运行结果是一致的。将应用从一个平台迁移到另一个平台,不用担心运行环境的变化导致应用无法正常运行。
- 拓展更容易
docker使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,基于基础镜像进一步扩展镜像也变得十分简单。另外docker社区维护了许多高质量的镜像,可将这些镜像作为基础,进一步定制开发。
知道了这么多Docker的优势,也没有必要否定虚拟机技术,因为两者有不同的使用场景,所擅长的颗粒度不同。虚拟机更擅长于彻底隔离整个运行环境。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而Docker通常用于隔离不同的应用,例如前端,后端以及数据库。
docker三个基本概念
镜像(Docker Image)
镜像是一个构建容器的只读模板,它包含了容器启动所需的所有信息,包括运行程序和配置数据。 镜像不是一个单一的文件,而是由多层构成。容器其实是在镜像的最上面加了一层读写层,在运行容器里做的任何文件改动,都会写到这个读写层。如果删除了容器,也就删除了其最上面的读写层,文件改动也就丢失了。Docker使用存储驱动管理镜像每层内容及可读写层的容器层。
仓库(Docker Registry) 存放镜像的地方。
容器(Docker Containers) 运行镜像的载体。
通俗来讲,仓库就像手机上的应用商店,镜像就是应用商店中的应用软件安装包,容器就是应用在手机上运行时的环境。
docker安装
在windows上安装docker
系统要求
Docker Desktop for Windows 支持 64 位版本的 Windows 10 Pro,且必须开启 Hyper-V(若版本为 v1903 及以上则无需开启 Hyper-V),或者 64 位版本的 Windows 10 Home v1903 及以上版本。
查看windows版本的方法是:把鼠标移动到任务栏最左侧的win图标上--鼠标右键--系统,即可查看安装的window版本,笔者的window版本是1904,可以直接安装Docker Desktop for Windows。
启动docker
双击桌面上的Docker Desktop图标,启动docker,Docker 启动之后会在 Windows 任务栏出现鲸鱼上载着集装箱的图标。等待片刻,当鲸鱼图标静止时,说明 Docker 启动成功,现在可以打开 PowerShell或cmd 使用 Docker。
Docker Desktop菜单简介
- Containers 可以查看本机运行过的docker应用
- Images 可以查看本机生成和下载过的镜像
- Volumes
这个菜单要重点说一下,Docker volumes 的作用是存储 Docker containers 的数据。因此 contaier 可以被随时终止和重启,并且应用的数据不会丢失。多个 container 也可以通过指向同一个 volume 来共享数据。
Docker volumes 保存在服务器的 Docker 存储目录下。通常用户并不需要去关心 Docker 存储目录的位置,因为可以通过 CLI 与 volumes 沟通。
定义匿名数据卷的方式:
# 定义一个匿名卷
FROM ubuntu:16.04
VOLUME /data
# 定义多个匿名卷
FROM ubuntu:16.04
VOLUME ["/data", "/command"]
任何向/data
和/command
目录中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化!
作用:
1.避免重要的数据,因容器重启而丢失,这是非常致命的。(容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失)
2.避免容器不断变大
使用 docker volume ls
打印所有 Docker volumes:
入门练习
我们把官方的入门程序,跑一遍。先从github把整个项目的压缩包下载到本地。
编译镜像文件
进入getting-started-master目录下的app目录 创建Dockerfile文件
# syntax=docker/dockerfile:1
FROM node:12-alpine
# RUN apk add --no-cache python3 g++ make
WORKDIR /app
COPY . .
RUN npm install --production
CMD ["node", "src/index.js"]
执行镜像构建命令:
-
-t
表示镜像的名字及tag,通常为name:tag或者name格式,不指定tag的话,tag默认是latest;可以在一次构建中为一个镜像设置多个tag; -
最后的
.
表示在当前目录下寻找Dockerfile文件。
docker build -t get-starting .
构建过程比较缓慢,在python3下载那里卡了很多,差不多花费了半个小时,Dockerfile文件才执行完。执行完之后,在Docker Desktop
中可以看到,get-starting镜像已经生成了。这个产物不在当前项目下,在Docker的安装目录中,在Docker Desktop
中可以查看产物。
启动镜像容器
-d: 在隔绝模式下运行容器
-p: 指定端口映射,格式为:主机端口:容器端口;让容器(container)的3000 端口映射本机(host)的 3000 端口
docker run -dp 3000:3000 get-starting
启动之后,在Docker Desktop
的容器菜单下,可以看到get-starting:latest正在运行:
访问localhost:3000,界面效果如下:
更新应用
- step1 更新源代码
随便改点东西,把按钮文案改成中文。
<InputGroup.Append>
<Button
type="submit"
variant="success"
disabled={!newItem.length}
className={submitting ? 'disabled' : ''}
>
{submitting ? '正在添加中...' : '添加记录'}
</Button>
</InputGroup.Append>
- step2 重新编译,更新镜像
第二次构建很快,因为第一次已经把项目依赖包安装好了。
docker build -t get-starting .
- step3 再次运行镜像
docker run -dp 3000:3000 get-starting
报错,端口被占用
移除旧容器,除了用指令,也可以在Docker Desktop
中操作,更方便。
# 获取 container 的 ID
docker ps
docker stop <container-id>
docker rm <container-id>
启动更新后的容器
docker run -dp 3000:3000 get-starting
可以看到,按钮名称已经被修改过来了。
docker常用命令
服务类命令
# 启动docker服务
systemctl start docker
# 停止docker服务
systemctl stop docker
# 重启docker服务
systemctl restart docker
# 查看docker服务
systemctl status docker
# 设置开机启动docker服务
systemctl enable docker
镜像类命令
# 搜索镜像
docker search tomcat
# 拉取镜像 docker pull 镜像名:版本号 不指定版本号,拉取最新版本
docker pull tomcat
# 使用dockerfile构建镜像
# docker build -t 镜像名:版本号 . 注意最后边的点 `.` 表示Dockerfile所在路径,
# 也可以指定一个git仓库的地址(只要该地址下有Dockerfile),则会利用git仓库中的dockerfile文件来构建镜像。
docker build -t my_image:1.0 .
# 查看本地镜像
docker images
构建镜像
docker build [OPTIONS] PATH | URL | -
参数 | 说明 |
---|---|
-f | 指定要使用的Dockerfile路径; |
--tag, -t | 镜像的名字及标签,通常 name|tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。 |
--build-arg=[] | 设置镜像创建时的变量; |
--cpu-shares | 设置 cpu 使用权重; |
--cpu-period | 限制 CPU CFS周期; |
--cpu-quota | 限制 CPU CFS配额; |
--cpuset-cpus | 指定使用的CPU id; |
--cpuset-mems | 指定使用的内存 id; |
--disable-content-trust | 忽略校验,默认开启; |
--force-rm | 设置镜像过程中删除中间容器; |
--isolation | 使用容器隔离技术; |
--label=[] | 设置镜像使用的元数据; |
-m | 设置内存最大值; |
--memory-swap | 设置Swap的最大值为内存+swap,"-1"表示不限swap; |
--no-cache | 创建镜像的过程不使用缓存; |
--pull | 尝试去更新镜像的新版本; |
--quiet, -q | 安静模式,成功后只输出镜像 ID; |
--rm | 设置镜像成功后删除中间容器; |
--shm-size | 设置/dev/shm的大小,默认值是64M; |
--ulimit | Ulimit配置。 |
--squash | 将 Dockerfile 中所有的操作压缩为一层。 |
--network | 默认 default。在构建期间设置RUN指令的网络模式 |
用Dockerfile构建镜像
docker build
命令可以使用 Dockerfile
来构建镜像。docker build
命令会自动读取上下文根路径下名为 Dockerfile
的文件。如果 Dockerfile
文件不在构建上下文根目录下,需要使用 -f
选项来指定 Dockerfile
文件的路径。例如:
docker build -f /path/to/Dockerfile -t 镜像名:tag版本 .
Dockerfile指令 | 含义 |
---|---|
FROM | 指定基础镜像,每个dockerfile文件的第一条指令都是FROM,如果不以任何镜像为基础,那么写法为:FROM scratch |
WORKDIR | 指定工作目录,用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指令设定工作目录。 |
COPY | 拷贝文件或者目录至 image |
ADD | COPY 指令的高级版本,会将压缩文件解压缩,支持 URL |
RUN | 在容器中执行命令,每一条指令构建一层 |
CMD | 容器启动时执行的命令 |
ENTRYPOINT | 定义容器的可执行文件,查看与 CMD 之间的 |
ENV | 设定环境变量 |
VOLUME | 指定 image 中的一个目录存储 volume。volume 将会被给予一个随机的名称,可以用 docker inspect 命令查看 |
EXPOSE | 告诉docker守护进程,容器的应用将使用指定的端口号 |
ARG | 定义可以传递给 docker build --build-arg 命令的变量,并且可以用在 Dockerfile 文件中 |
Docker的Entrypoint和CMD的区别:
Entrypoint是指定容器启动时要执行的默认命令,它在运行容器时不能被覆盖。而Cmd是指定容器启动时要执行的默认命令参数,它可以被覆盖。通常情况下,Entrypoint用于指定容器启动时要运行的应用程序,而Cmd用于指定应用程序的默认参数。通过下面的例子,看一下如何编写Dockerfile文件。
# nodejs server Dockerfile
# 基础镜像是node-v16
FROM node:16
# RUN、CMD、ENTRYPOINT、COPY和ADD命令的工作目录
WORKDIR /nodeApp
# COPY <宿主机目录或文件路径> <容器内目录或文件路径>
# 这里表示复制packagejson到nodeApp目录
COPY ./package.json .
# ADD <宿主机目录或文件路径> <容器内目录或文件路径>
# ADD ./package.json .
# 在容器中执行命令 像在命令行中输入的Shell命令一样。
RUN npm install
# 将文件从客户端复制到容器中,这里表示复制src到nodeApp下的server目录
COPY ./src ./server
# 监听的端口
EXPOSE 8000
# 容器启动时执行的命令
CMD ["node", "./server/index.js"]
为了防止某些文件被拷贝到 Docker image 中,可以在 Dockerfile
同级目录下创建一个 .dockerignore
文件,文件内容指定了那些不允许拷贝到 Docker image 中的文件,类似于 .gitignore
。
容器类指令
- 创建容器
tomcat:latest 是使用的镜像名和版本号
docker run -d --name=my_container -p 8080:8080 tomcat:latest
Docker
创建容器的命令是 docker run
。以下是一些常用的参数:
参数 | 含义 |
---|---|
-d | 后台运行容器,并返回容器ID ; |
-p | 指定端口映射,格式为:主机(宿主)端口:容器端口; |
-i | 以交互模式运行容器,通常与 -t 同时使用;使用交互模式运行容器时, 会直接进入容器内部, 退出交互模式后, 该容器自动停止运行 |
-t | 为容器重新分配一个伪输入终端,通常与 -i 同时使用; |
--name | 为容器指定一个名称; |
--dns 8.8.8.8 | 指定容器使用的DNS 服务器,默认和宿主一致; |
现在回头看看文章开头命令的含义:
docker run -it --env-file=.env -e "CONFIG=$(cat ./crawlerConfig.json | jq -r tostring)" algolia/docsearch-scraper
创建一个docker容器,环境配置文件读取的是./crawlerConfig.json中的内容,用jq将原始json内容转换成字符串, 运行algolia/docsearch-scraper最新版本的镜像。现在可以看懂了。
最后
感觉docker就和当时的TypeScript一样,你可以不学,可是你接触的别的应用功能,使用了这项技术。倒逼人得去学习。开卷有益,只要肯花费时间和精力,总会有收获的。通过本文,我们知道了docker是什么,docker的工作原理与重要概念,docker的优势,如何创建镜像,运行镜像,重新生成镜像,Docker Desktop的安装使用方法,以及docker常用的一些命令。对docker已经有了一个大概的了解,若想掌握更高级的技巧,比如说希望启动更新后,容器能保留原来的条目;希望无需 重新生成镜像与运行就能看到改动的代码更新,还是要去系统的学习一下官方文档以及多搜索一些这方面的资料。