Docker是用GO语言开发的应用容器引擎,基于容器化,沙箱机制的应用部署技术。可适用于自动化测试、打包,持续集成和发布应用程序等场景,包括阿里云,亚马逊在内的云计算服务商都采用了docker来打造serverless服务平台。它不仅仅可以部署项目,还可以用于数据库搭建,nginx服务搭建,nodejs、php等编程语言环境搭建。
PS: docker现已改名为moby
Docker中的三个重要概念:
镜像(image):分片的(只读)文件系统,由Dockerfile创建
独立、易扩展、更效率
容器(container):由Docker进程创建和管理的:文件系统 + 系统资源 + 网络配置 + 日志管理
docker是docker镜像的运行环境,所以容器的概念就比较好理解了
仓库(registry):用来远端存储docker镜像
版本控制、变更管理、为持续集成与快速部署提供便利
总结一下:
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为 MB |
一般为 GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
开发/环境定制 | 方便(命令行、面向对象式) | 进入虚拟机 |
相同点:
不同点:
Docker是容器化部署技术,它主要作用在于通过运行容器来实现应用部署,而容器基于镜像运行。
简单地说,就是将你的项目和依赖包(基础镜像)打成一个带有启动指令的项目镜像,然后在服务器创建一个容器,让镜像在容器内运行,从而实现项目的部署。
服务器就是容器的宿主机,docker容器与宿主机之间是相互隔离的。
Docker:
使用基于java的tomcat镜像 -> docker run -> 指定端口/挂载webapp目录 -> 服务启动
Docker会自己拉取镜像,若本地已经存在该镜像,则不用到网上去拉取
创建新的容器
分配文件系统并且挂着一个可读写的层,任何修改容器的操作都会被记录在这个读写层上,你可以保存这些修改成新的镜像,也可以选择不保存,那么下次运行改镜像的时候所有修改操作都会被消除
分配网络\桥接接口,创建一个允许容器与本地主机通信的网络接口
设置ip地址,从池中寻找一个可用的ip地址附加到容器上,换句话说,localhost并不能访问到容器
运行你指定的程序
捕获并且提供应用输出,包括输入、输出、报错信息
Docker的价值:
从应用架构角度:统一复杂的构建环境;
从应用部署角度:解决依赖不同、构建麻烦的问题,结合自动化工具(如jenkins)提高效率。
从集群管理角度:规范的服务调度,服务发现,负载均衡
操作系统 |
---|
Docker Desktop for Mac (macOS) |
Docker Desktop for Windows (Microsoft Windows 10) |
Docker镜像加速,主要是对docker pull
拉取镜像操作进行网络加速优化:
需要注册阿里云的账号,登录容器Hub服务,在左侧的加速器帮助页面就会显示为你独立分配的加速地址。
针对安装了Docker for Windows的用户,打开docker设置,Docker Engine 输入下面的配置,添加
"registry-mirrors": [
"https://xxx.mirror.aliyuncs.com"
]
完整配置:
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false,
"features": {
"buildkit": true
},
"registry-mirrors": [
"https://xxx.mirror.aliyuncs.com"
]
}
docker [image] pull NAME[:TAG]
NAME 是镜像仓库名称(用来区分镜像),TAG 是镜像的标签(往往用来表示版本信息)。如果不显式指定 TAG,则默认会选择 latest 标签
# 获取一个 Ubuntu 18.04 系统的基础镜像
docker pull ubuntu:18.04
注意:在不同的镜像仓库服务器的情况下,可能会出现镜像重名的情况。严格地讲,镜像的仓库名称中还应该添加仓库地址(注册服务器)作为前缀,只是默认使用的是官方 Docker Hub 服务,该前缀可以忽略
docker pull ubuntu:18.04
相当于 docker pull register.hub.docker.com/ubuntu:18.04
pull 子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-a,–all-tags=true|false | 是否获取仓库中的所有镜像,默认为否 | – |
–disable-content-trust | 取消镜像的内容校验,默认为真 | docker pull --disable-content-trust ubuntu |
下载镜像到本地后,即可随时使用该镜像,例如利用该镜像创建一个容器,在其中运行 bash 应用,执行打印 “hello world” 命令:
docker images
or docker image ls
从列出信息中,可以看到几个字段信息:
images 子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-a,–all=true|false | 列出所有(包括临时文件)镜像文件,默认为否 | – |
–digests=true|false | 列出镜像的数字摘要值,默认为否 | – |
-f,–filter=[] | 过滤列出的镜像 | – |
–no-trunc=true|false | 对输出结果中太长的部分是否进行截断,默认为是 | – |
-q,–quiet=true|false | 仅输出ID信息,默认为否 | docker images -q |
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
# 添加一个新的 myubuntu 镜像
docker tag ubuntu:18.04 myubuntu:18.04
myubuntu 跟 ubuntu 的镜像 ID 是完全一致的,实际上是指向了同一个镜像文件,只是别名不同而已。docker tag 命令添加的标签实际上起到了类似链接的作用
docker [image] inspect NAME[:TAG]
# 查看该镜像的详细信息,包括制作者、适应架构、各层的数字摘要等
docker inspect ubuntu:18.04
# 获取镜像的 Architecture 信息
docker inspect -f {{".Architecture"}} ubuntu:18.04
amd64
docker history NAME[:TAG]
docker search [option] keyword
search 子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-f,–filter filter | 过滤输出内容 | – |
–format string | 格式化输出内容 | – |
–limit int | 限制输出结果个数,默认为 25 个 | – |
–no-trunc | 不截断输出结果 | – |
# 搜索官方提供的带 nginx 关键字的镜像
docker search --filter=is-official=true nginx
# 搜索所有收藏数超过 4 的关键词包括 tensorflow 的镜像
docker search --filter=starts=4 tensorflow
删除:docker rmi IMAGE
or docker image rm IMAGE
清理:docker image prune
IMAGE 可以为标签或ID。当有该镜像创建的容器存在时,镜像文件默认是无法被删除的
rmi 子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-f,-force | 强制删除镜像,即使有容器依赖它 | – |
-no-prune | 不要清理未带标签的父镜像 | – |
只删除某个标签的镜像
# 删除 myubuntu:18.04
docker rmi myubuntu:18.04
镜像ID一样的都会被删除,比如利用某个镜像通过 docker tag
创建的镜像副本,它们的镜像ID是一样的
# 删除镜像ID是 5a214d77f5d7 的镜像
docker rmi 5a214d77f5d7
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-f,-force | 强制删除镜像 | – |
-a,-all | 删除所有无用镜像,不光是临时镜像 | – |
-filter filter | 只清理给定过滤器的镜像 | – |
# 清理临时的遗留镜像文件层
docker image prune -f
创建镜像的方法主要有三种:基于已有镜像的容器创建、基于本地模板导入、基于 Dockerfile 创建。
该方法主要是使用 docker [container] commit 命令。
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-c,–change=[] | 提交的时候执行 Dockerfile 指令 | – |
-a,–author=“” | 作者信息 | – |
-m,–message=“” | 提交消息 | – |
-p,–pause=true | 提交时暂停容器运行 | – |
# 启动容器,创建一个 test 文件,然后退出
docker run -it ubuntu:18.04 /bin/bash
root@f817585f00a5:/# touch test
root@f817585f00a5:/# exit
exit
记住容器ID为 f817585f00a5,此时该容器与原 ubuntu:18.04 镜像相比,已经发生改变,可以使用 docker [container] commit 命令来提交为一个新的镜像。提交时可以使用 ID 或名称来指定容器:
# 使用 commit 命令来提交一个新的镜像
docker commit -m "Added a new file" -a "Docker Newbee" f817585f00a5 test:0.1
sha256:9e62a170b9ef8ed0ddc1510a6ed0df7070a5e27069ff00cc76ad2282431a9bc8
# 查看本地镜像列表,会发现新创建的镜像已经存在
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 0.1 9e62a170b9ef 2 minutes ago 63.1MB
myubuntu 18.04 5a214d77f5d7 14 months ago 63.1MB
ubuntu 18.04 5a214d77f5d7 14 months ago 63.1MB
docker [image] import [OPTIONS] file|URL| - [REPOSITORY[:TAG]]
最常见的方式。Dockerfile 是一个文本文件,利用给定的指令描述基于某个父镜像创建新镜像的过程。
一般,Dockerfile 共包括四部分:
1、一个简单的Koa应用:
const Koa = require('koa')
const app = new Koa()
// response
app.use(ctx => {
ctx.body = 'Hello Koa!!'
})
app.listen(3000)
2、Dockerfile示例:
FROM node:10
LABEL [email protected]
# 创建 app 目录
WORKDIR /app
# 把 package.json,package-lock.json(npm@5+) 或 yarn.lock 复制到工作目录(相对路径)
COPY ["package.json","*.lock","./"]
# 打包 app 源码
# 特别注意:要指定工作目录中的文件名
COPY src ./src
# 使用.dockerignore文件,上面两个COPY合并成一个
# COPY . .
# 使用Yarn安装 app 依赖
# 如果你需要构建生产环境下的代码,请使用:
# --prod参数
RUN yarn --prod --registry=https://registry.npm.taobao.org
# 对外暴露端口 -p 4000:3000
EXPOSE 3000
CMD [ "node", "src/index.js" ]
3、使用docker build
打包:
docker build -t ${your_name}/${image_name}:${tag} .
这里的your_name
代表的是远程仓库中的用户名,或者仓库地址; image_name
为镜像名称,tag
是给镜像打的标签,用于版本控制。例如:
docker build -t lxc/koa:1.0 .
4、启动 run
# 把 3000 端口映射到 4000 上
docker run -d --name nodedemo -p 4000:3000 lxc/koa:1.0
docker save -o FILENAME IMAGE[:TAG]
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-o,-output | 导出镜像到指定参数中 | – |
# 导出本地的 ubuntu:18.04 镜像为文件 ubuntu_18.04.tar
docker save -o ubuntu_18.04.tar ubuntu:18.04
之后就可以通过复制 ubuntu_18.04.tar 文件将该镜像分享给他人
docker load -i IMAGE[:TAG]
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-i,-input | 从指定文件中读取镜像内容 | – |
# 导出本地的 ubuntu:18.04 镜像为文件 ubuntu_18.04.tar
docker load -i ubuntu_18.04.tar
导入成功后,可以使用 docker images 命令进行查看,与原镜像一致
docker push IMAGE[:TAG]
or docker push [REGISTRY_HOST[:REGISTRY_PORT]/]IMAGE[:TAG]
默认上传到 Docker Hub 官方仓库(需要登录)。
如:用户 user 上传本地的 test:latest 镜像
# 先添加新的标签 user/test:latest
docker tag test:latest user/test:latest
# 上传到远程
docker push user/test:latest
第一次上传时,会提示输入登录信息进行注册,之后登录信息会记录到本地 ~/.docker 目录下
docker create CONTAINER[:TAG]
# 新建容器
docker create -it ubuntu:latest
使用 docker create 命令新建的容器处于停止状态,可以使用 docker start 命令来启动它
docker start CONTAINER ID
or docker start NAMES
# 启动新建的容器
docker start 97d41053c282
97d41053c282
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
97d41053c282 ubuntu "bash" 4 minutes ago Up 8 seconds tender_bohr
docker run IMAGE[:TAG]
docker ps
or docker container ls
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-a | 查看所有容器 | – |
docker logs CONTAINER ID
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-details | 打印详尽信息 | – |
-f,-follow | 持续保持输出 | – |
-since string | 输出从某个时间开始的日志 | – |
-tail string | 输出最近的若干日志 | – |
-t | 显示时间戳信息 | – |
-until string | 输出从某个时间之前的日志 | – |
暂停:docker pause CONTAINER ID
or docker pause NAMES
恢复运行:docker unpause CONTAINER ID
or docker unpause NAMES
docker stop CONTAINER ID
or docker stop NAMES
该命令会首先向容器发送 SIGTERM 信号,等待一段时间后(默认 10s),再发送 SIGKILL 信号来终止容器
自动清除所有处于停止状态的容器:docker container prune
直接发送 SIGKILL 信号来终止容器:docker kill CONTAINER ID
将运行中的容器先终止,然后再启动:docker restart CONTAINER ID
docker exec CONTAINER ID
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-d,–detach | 在容器中后台执行命令 | – |
–detach-keys=“” | 指定将容器切回后台的按键 | – |
–e,–env=[] | 指定环境变量列表 | – |
-i,–interactive=true|false | 打开标准输入接受用户输入命令,默认值为 false | – |
–privileged=true|false | 是否给执行命令以高权限,默认值为 false | – |
-t,–tty=true|false | 分配伪终端,默认值为 false | – |
-u,–user=“” | 执行命令的用户名或ID | – |
# 进入刚创建的容器中,并启动一个 bash
docker exec -it 97d41053c282 /bin/bash
root@97d41053c282:/#
docker rm CONTAINER ID
默认情况下,docker rm 命令只能删除已经处于终止或退出状态的容器
子命令支持的选项主要包括:
选项 | 描述 | 示例 |
---|---|---|
-f,–force=false | 是否强制终止并删除一个运行中的容器 | – |
-l,–link=false | 删除容器的连接,但保留容器 | – |
-v,–volumes=false | 删除容器挂载的数据卷 | – |
# 导出容器 97d41053c282 到文件 test_for_run.tar 文件
docker export -o test_for_run.tar 97d41053c282
# 导入 test_for_run.tar 文件到系统中
docker import test_for_run.tar - test/ubuntu:v1.0
docker container inspect CONTAINER ID
or docker container inspect NAMES
docker top CONTAINER ID
or docker top NAMES
docker stats CONTAINER ID
or docker stats NAMES
容器中管理数据主要有两种方式:
一个可供容器使用的特殊目录,它将主机操作系统目录直接映射进容器
# 在本地创建一个数据卷
docker volume create -d local test
test
除了 create 子命令外。docker volume 还支持
在用 docker run 命令的时候,可以使用 -mount 选项来使用数据卷
# 使用 training/webapp 镜像创建一个 Web 容器,并创建一个数据卷挂载到容器的 /opt/webapp 目录
docker run -d -P --name web --mount type=bind,source=/webapp,destination=/opt/webapp training/webapp python app.py
目的是提供数据卷给其他容器挂载
# 创建一个数据卷容器 dbdata,并在其中创建一个数据卷挂载到 /dbdata
docker run -it -v /dbdata --name dbdata ubuntu
在其他容器中使用 --volumes-from 来挂载 dbdata 容器中的数据卷,例如创建 db1 和 db2 两个容器,并从dbdata 容器挂载数据卷
docker run -it --volumes-from dbdata --name db1 ubuntu
docker run -it --volumes-from dbdata --name db2 ubuntu
此时,容器 db1 和 db2 都挂载同一个数据卷到相同的 /dbdata 目录,三个容器任何一方在该目录下的写入,其他容器都可以看到
有时候,需要多个容器之间能够互相访问到对方的服务。Docker 除了通过网络访问外,还提供了两个很方便的功能来满足服务访问的基本需求:一个是允许映射容器内应用的服务端口到本地宿主主机;另一个是互联机制实现多个容器间通过容器名来快速访问
通过 -p 或 -P 参数来指定端口映射。当使用 -P(大写)标记时,Docker 会随机映射一个 49000 ~ 49900 的端口到内部容器开放的网络端口
# 随机分配端口
docker run -d -P training/webapp python app.py
# 查看
docker ps -l
# 映射到指定地址的指定端口,比如 localhost 地址 127.0.0.1
docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
容器的互联是一种让多个容器中的应用进行快速交互的方式。它会在源和接收容器之间创建连接关系,接收容器可以通过容器名快速访问到源容器,而不用制定具体的IP地址。
连接系统依据容器的名称来执行。因此,首先需要自定义一个好记的容器命名。虽然当创建容器的时候,系统会默认会分配一个名字,但自定义命名有两个好处:
# 使用 --name 标记可以为容器自定义命名
docker run -d -P --name web training/webapp python app.py
# 查看容器名
docker inspect -f {{".Name"}} 97d41053c282
# 创建一个新的数据库容器
docker run -d --name db training/postgres
# 然后创建一个新的 web 容器,并将它连接到 db 容器
docker run -d -P --name web --link db:db training/webapp python app.py
–link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias 是别名
配置文件,用来快速创建自定义的镜像
定义创建镜像过程中使用的变量
格式为: ARG
当镜像编译成功后,ARG 指定的变量将不再存在(ENV 指定的变量将在镜像中保留)
指定所创建镜像的基础镜像
格式为:FROM
任何 Dockerfile 中第一条指令必须为 FROM 指令。并且,如果在同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)
ARG VERSION=9.3
FROM debian:${VERSION}
为生成的镜像添加元数据标签信息
格式为:LABEL
LABEL version="1.0.0" date="2020-01-01"
声明镜像内服务监听的端口
格式为:EXPOSE
EXPOSE 22 80 8443
指定环境变量
格式为:ENV
ENV key1=value1 key2=value2
指定镜像的默认入口命令
支持两种格式:
此时,CMD 指令指定值将作为根命令的参数
每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效
创建一个数据卷挂载点
格式为:VOLUME [“/data”]
指定运行容器时的用户名或UID
格式为:USER daemon
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录
格式为:WORKDIR /path/to/workdir
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
则最终路径为 /a/b/c
因此,为了避免出错,推荐 WORKDIR 指令中只使用绝对路径
指定当基于所生成镜像创建子镜像时,自动执行的操作指令
格式为:ONBUILD [INSTRUCTION]
指定所创建镜像启动的容器接受退出的信号值
STOPSIGNAL signal
配置所启动容器如何进行健康检查
格式有两种:
指定其他命令使用 shell 时的默认 shell 类型
默认值为 [“/bin/sh”, “-c”]
运行指定命令
格式为:RUN
注意:后者指令会被解析为 JSON 数组,必须使用双引号。前者默认将在 shell 终端中运行命令,即 /bin/sh -c;后者使用 exec 执行,不会启动 shell 环境
当命令较长时可以使用\来换行。例如:
RUN apt-get update \
&& ...
指定启动容器时默认执行的命令
支持三种格式:
每个 Dockerfile 只能有一条 CMD 命令。如果有多条命令,只有最后一条会被执行
添加内容到镜像
格式为:ADD
该命令将复制指定的
其中
复制内容到镜像
格式为:COPY
COPY 与 ADD 指令功能类似,当使用本地目录为源目录时,推荐使用 COPY
编写完 Dockerfile 之后,可以通过 docker [image] build 命令来创建镜像
格式为:docker build [OPTIONS] PATH | URL | -
该命令将读取指定路径下(包括子目录)的 Dockerfile,并将该路径下所有数据作为上下文发送给 Docker 服务端。碰到 ADD、COPY、RUN 指令会生成一层新的镜像。如果创建成功,会返回最终镜像的 ID
# 上下文路径为 /tmp/docker_builder/, 生成镜像标签为 builder/first_image:1.0.0
docker build -t builder/first_image:1.0.0 /tmp/docker_builder/
大部分情况,生成新的镜像都需要通过 FROM 指令来指定父镜像。父镜像是生成镜像的基础,会直接影响到所生成镜像的大小和功能。
两种镜像选择作为父镜像:
定义一个简单的基础镜像,将用户提前编译好的二进制可执行文件 binary 复制到镜像中,运行容器时执行 binary 命令:
FROM scratch
ADD binary /
CDM ["/binary"]
通过 Docker-Compose 用户可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建
Compose 中有两个重要的概念:
Docker Desktop 自带 docker-compose
例子
# 创建一个 test-mysql 容器
version: '3'
services:
mysql:
image: mysql
container_name: test-mysql
ports:
- "8000:3306"
environment:
- MYSQL_ROOT_PASSWORD=123456
在此文件的当前目录下,使用docker-compose up -d
来执行(默认执行 docker-compose.yml)
指定执行: docker-compose -f xxx.yml up -d
两个复杂的应用:
# restart: always 作用:当服务器(docker-image)重启之后,这个容器就会自动启动
version: '3.1'
services:
mongo:
image: mongo
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: 123456
mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: 123456