Docker基础学习笔记
各平台安装
Mac brew 安装 docker
1 | # 先卸载 |
Liunx CentOS7 环境安装Docker
Linux设置
1 | # 配置主机名 |
Docker及其依赖安装
1 | # 安装基础软件包 |
启动docker
1 | 启动 docker 服务 |
开启包转发功能和修改内核参数
1 | 内核参数修改:br_netfilter 模块用于将桥接流量转发至 iptables 链,br_netfilter 内核参数需要开 |
配置docker镜像加速器ß
登陆阿里云镜像仓库
https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
如果没有开通,可开通阿里云的镜像服务
找到加速器地址,执行如下命令
1 | sudo mkdir -p /etc/docker |
镜像和容器命令
镜像操作
1 | # dockerhub查找镜像 |
容器操作
1 | # 以交互方式启动并进入容器,输入 exit,退出容器,退出之后容器也会停止,不会再前台运行 |
Dockerfile
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。基于 Dockerfile 构建镜像可以使用 docker build 命令。docker build 命令中使用-f 可以指定具体的dockerfile 文件
Dockerfile例子
1 | FROM centos |
指令介绍
FROM
基础镜像,必须是可以下载下来的,定制的镜像都是基于 FROM 的镜像,这里的 centos 就是定制需要的基础镜像。后续的操作都是基于 centos 镜像。
MAINTAINER
指定镜像的作者信息
RUN
指定在当前镜像构建过程中运行
的命令, 包含两种模式:
- shell模式 RUN (shell 模式,这个是最常用的,需要记住) eg: RUN echo hello
- exec模式 RUN [“executable”,“param1”,“param2”] (exec 模式)
1
RUN [“/bin/bash”,”-c”,”echo hello”] 等价于/bin/bash -c echo hello
EXPOSE
仅仅只是声明端口。作用:
- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射
- 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
- 可以是一个或者多个端口,也可以指定多个 EXPOSE
1
格式:EXPOSE <端口 1> [<端口 2>...]
CMD
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
- CMD 在 docker run 时运行
- RUN 是在 docker build 构建镜像时运行的
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被docker run
启动命令行参数中指定要运行的程序所覆盖。1
2
3CMD[“executable”,“param1”,“param2”] (exec 模式)
CMD command (shell 模式)
CMD [“param1”,”param2”] 作为 ENTRYPOINT 指令的默认参数
ENTERYPOINT
类似于 CMD 指令,但其不会被 docker run 启动命令行参数指定的指令所覆盖
,而且这些命令行参数会被 当作参数送给 ENTRYPOINT 指令指定的程序。但是, 如果运行 docker run 时使用了 –entrypoint 选项,将覆盖 entrypoint 指令指定的程序。
优点:在执行 docker run
的时候可以指定 ENTRYPOINT 运行所需的参数。注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令
,仅最后一个生效
。格式:
1 | ENTERYPOINT [“executable”,“param1”,“param2”] (exec 模式) |
可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,示例说明:
1 | 假设已通过 Dockerfile 构建了 nginx-cmd:v1 镜像: |
不传参运行:
1 | docker run nginx-cmd:v1 |
传参运行:
1 | docker run nginx-cmd:v1 -c /etc/nginx/new.conf |
COPY
1 | COPY<src>..<dest> |
复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
格式:
1 | COPY [--chown=<user>:<group>] <源路径 1>... <目标路径> |
<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:
1 | COPY hom* /mydir/ |
<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建
。
ADD
1 | ADD <src>...<dest> |
ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY
)。功能也类似,不同之处如下:
- ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下, 会
自动复制并解压
到 <目标路径>。 - ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像 构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
ADD vs COPY
ADD 包含类似 tar 的解压功能 如果单纯复制文件,dockerfile 推荐使用 COPY
示例演示:拷贝本地的index.html替换容器内部的index.html
1 | # 替换/usr/share/nginx 下的 index.html |
VOLUME
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
作用:
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
- 避免容器不断变大。
格式:
1 | VOLUME ["<路径 1>", "<路径 2>"...] |
在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。
1 | VOLUME [“/data”] |
WORKDIR
指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作 目录,必须是提前创建好的)。
docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才 会一直存在。
格式:
1 | WORKDIR <工作目录路径> |
ENV
设置环境变量
1 | ENV <key> <value> |
以下示例设置 NODE_VERSION =6.6.6, 在后续的指令中可以通过 $NODE_VERSION 引用:
1 | ENV NODE_VERSION 6.6.6 |
USER
用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已 经存在)。
1 | USER <用户名>[:<用户组>] |
ONBUILD
用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜 像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
格式:
1 | ONBUILD <其它指令> |
用途:为镜像添加触发器 当一个镜像被其他镜像作为基础镜像时需要写上 OBNBUILD 会在构建时插入触发器指令
演示示例:
1 | # 第一个镜像,执行dockerfile时不会执行index.html的拷贝 ,生成的镜像名假设为onbuild-nginx:v1 |
LABEL
LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:
1 | LABEL <key>=<value> <key>=<value> <key>=<value> ... |
比如我们可以添加镜像的作者:
1 | LABEL org.opencontainers.image.authors="lessismore" |
HEALTHCHECK
用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
格式:
1 | HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令 |
HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
ARG
构建参数,与 ENV
作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就 是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build 中可以用 –build-arg <参数名>=<值> 来覆盖。 格式:
1 | ARG <参数名>[=<默认值>] |
数据卷
Docker 容器的数据管理
什么是Docker容器的数据卷?
数据卷是经过特殊设计的目录,可以绕过联合文件系统(UFS),为一个或者多个容器提供访问,数据卷设计的目的,在于数据的永久存储,它完全独立于容器的生存周期,因此,docker 不会在容器删除时删 除其挂载的数据卷,也不会存在类似的垃圾收集机制,对容器引用的数据卷进行处理,同一个数据卷可以支持多个容器的访问。
数据卷的特点
- 数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,这些数据会被拷贝到新初始 化的数据卷中
- 数据卷可以在容器之间共享和重用
- 可以对数据卷里的内容直接进行修改
- 数据卷的变化不会影像镜像的更新
- 卷会一直存在,即使挂载数据卷的容器已经被删除
数据卷的使用
- 为容器添加数据卷
创建一个数据卷,数据存储在宿主机默认docker目录
1 | docker run --name data -v /opt/data -t -i centos /bin/bash |
创建一个数据卷,数据存储在宿主机指定的映射目录 ~/datavolume
1 | docker run -v /datavolume:/data -it centos /bin/bash |
注:~/datavolume 为宿主机目录,/data 为 docker 启动的 volume 容器的里的目录 这样在宿主机的/datavolume 目录下创建的数据就会同步到容器的/data 目录下
为数据卷添加访问权限
1 | docker run --name volume1 -v ~/datavolume1:/data:ro -itd centos /bin/bash |
添加只读权限之后在 docker 容器的/data 目录下就不能在创建文件了,为只读权限;在宿主机下的 /datavolume1 下可以创建东西
- 使用 dockerfile 构建包含数据卷的镜像
Docker数据卷容器
什么是数据卷容器?
命名的容器挂载数据卷,其他容器通过挂载这个容器实现数据共享,挂载数据卷的容器,就叫做数据卷 容器。
挂载数据卷容器的方法
1 | docker run --volumes-from [container name] |
示例:
1 | # (volume 这个镜像是上面创建的带两个数据卷/datavolume3 和/ddatavolume6 的镜像) |
docker 数据卷的备份和还原
数据备份方法
1 | docker run --volumes-from [container name] -v $(pwd):/backup centos tar czvf /backup/backup.tar [container data volume] |
数据还原方法
1 | docker run --volumes-from [container name] -v $(pwd):/backup centos tar xzvf /backup/backup.tar.gz [container data volume] |
容器网路互联
docker0 : 安装 docker 的时候,会生成一个 docker0 的虚拟网桥
Linux虚拟网桥: 可以设置 ip 地址 相当于拥有一个隐藏的虚拟网卡;每运行一个 docker 容器都会生成一个 veth 设备对,这个 veth一个接口在容器里,一个接口在物理机上。
安装网桥管理工具
1 | yum install bridge-utils -y |
brctl show 可以查看到有一个 docker0 的网桥设备,下面有很多接口,每个接口都表示一个启动的 docker 容器,因为我在 docker 上启动了很多容器,所以 interfaces 较多,如下所示:
1 | [root@centos _data]# brctl show |
实验用例准备(由于centos 镜像被docker官方标记为deprecated,很多依赖拉取失败,可以制作一个基础镜像备用,节省环境准备时间):
1 | ## 准备测试镜像 |
允许所有容器间互联
- 同一台机器的容器启动后默认是互联的,但是容器重启后IP被重新分配。
- docker
--link
设置网络别名,给容器起一个代号,这样可以直接以代号访问,避免了容器重启 ip 变化带来的问题1
2
3
4
5
6
7# 语法
docker run --link=[CONTAINER_NAME]:[ALIAS] [IMAGE][COMMAND]
# 先启动一个容器 network01
docker run --name network01 -itd inter-image /bin/bash
# 然后启动第二个容器 network02
docker run --name network02 -itd --link=network01:webnet inter-image /bin/bash
# 在network02上访问network01容器只需要,ping webnet即可。我们重启network01容器,ip变化了也没问题。
容器的网络模式
docker run 创建 Docker 容器时,可以用–net 选项指定容器的网络模式,Docker 有以下 4 种网络模式:
- bridge 模式:使–net =bridge 指定,默认设置;
- host 模式:使–net =host 指定;
- none 模式:使–net =none 指定;
- container 模式:使用–net =container:NAME orID 指定。
brigde 模式
当Docker
进程启动时,会在主机上创建一个名为docker0
的虚拟网桥,此主机上启动的Docker
容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。从docker0
子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair
设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为eth0
(容器的网卡),另一端放在主机中,以vethxxx
这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。可以通过brctl show
命令查看。
bridge
模式是 docker 的默认网络模式,不写–net
参数,就是bridge
模式。使用docker run -p
时,docker 实际是在iptables
做了DNAT
规则,实现端口转发功能。可以使用iptables -t nat -vnL
查看。
1 | docker run --name bridge -it --privileged=true centos bash |
host 模式
如果启动容器的时候使用host
模式,那么这个容器将不会获得一个独立的Network Namespace
,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
1 | docker run --name host -it --net=host --privileged=true centos bash |
none 模式
使用none
模式,Docker 容器拥有自己的 Network Namespace,但是,并不为Docker 容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。
1 | # 示例 |
container 模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
1 | docker run --name container2 --net=container:none -it --privileged=true centos |
资源限制
Docker资源配额
Docker 通过 cgroup 来控制容器使用的资源限制,可以对 docker 限制的资源包括 CPU、内存、磁盘
控制CPU
CPU份额
1 | 查看配置份额的帮助命令: |
CPU shares (relative weight) 在创建容器时指定容器所使用的 CPU 份额值。cpu-shares 的值不能保证可以获得 1 个 vcpu 或者多少 GHz 的 CPU 资源,仅仅只是一个弹性的加权值。默认每个 docker 容器的 cpu 份额值都是 1024
。在同一个 CPU 核心上,同时运行多个容器时,容器的 cpu 加权的效果才能体现出来。
两个容器 A、B 的 cpu 份额分别为 1000 和 500,结果会怎么样?
情况 1:A 和 B 正常运行,占用同一个 CPU,在 cpu 进行时间片分配的时候,容器 A 比容器 B 多一倍 的机会获得 CPU 的时间片
。
情况 2:分配的结果取决于当时其他容器的运行状态
。比如容器 A 的进程一直是空闲的,那么容器 B 是可以获取比容器 A 更多的 CPU 时间片的; 比如主机上只运行了一个容器,即使它的 cpu 份额只有 50,它也可以独占整个主机的 cpu 资源。cgroups 只在多个容器同时争抢同一个 cpu 资源时,cpu 配额才会生效
。因此,无法单纯根据某个容 器的 cpu 份额来确定有多少 cpu 资源分配给它,资源分配结果取决于同时运行的其他容器的cpu分配和容器中进程运行情况
。
示例:给容器实例分配 512 权重的 cpu 使用份额
1 | # 参数: --cpu-shares 512 |
注:稍后,我们启动多个容器,测试一下是不是只能使用 512 份额的 cpu 资源。单独一个容器,看 不出来使用的 cpu 的比例。 因没有 其他docker 实例同此 docker 实例竞争
通过-c 设置的 cpu share 并不是 CPU 资源的绝对数量,而是一个相对的权重值。某个容器最终能 分配到的 CPU 资源取决于它的 cpu share 占所有容器 cpu share 总和的比例。通过 cpu share 可以设置容器使用 CPU 的优先级
。
比如在 host 中启动了两个容器:
1 | docker run --name "container_A" -c 1024 ubuntu |
container_A 的 cpu share 1024,是 container_B 的两倍。当两个容器都需要 CPU 资源时, container_A 可以得到的 CPU 是 container_B 的两倍。
需要注意的是,这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下。如果 container_A 处于空 闲状态,为了充分利用 CPU 资源,container_B 也可以分配到全部可用的 CPU。
CPU core核心控制
参数:–cpuset 可以绑定 CPU
对多核 CPU 的服务器,docker 还可以控制容器运行限定使用哪些 cpu 内核和内存节点,即使用-- cpuset-cpus
和--cpuset-mems
参数。对具有 NUMA 拓扑(具有多 CPU、多内存节点)的服务器尤其有 用
,可以对需要高性能计算的容器进行性能最优的配置。如果服务器只有一个内存节点,则– cpuset-mems 的配置基本上不会有明显效果。
CPU控制参数混合使用
在上面这些参数中,cpu-shares 控制只发生在容器竞争同一个 cpu 的时间片时有效。 如果通过 cpuset-cpus 指定容器 A 使用 cpu 0,容器 B 只是用 cpu1,在主机上只有这两个容器使用 对应内核的情况,它们各自占用全部的内核资源,cpu-shares 没有明显效果。
如何才能有效果?
容器 A 和容器 B 配置上 cpuset-cpus 值并都绑定到同一个 cpu 上,然后同时抢占 cpu 资源,就可以 看出效果了。
测试 cpu-shares 和 cpuset-cpus 混合使用运行效果,就需要一个压缩力测试工具 stress 来让 容器实例把 cpu 跑满。
如何把 cpu 跑满?
如何把 4 核心的 cpu 中第一和第三核心跑满?可以运行 stress,然后使用 taskset 绑定一下 cpu。
stress工具
linux 系统压力测试软件 stress
1 | yum install -y epel-release |
控制内存
Docker 提供参数-m, –memory=””限制容器的内存使用量。
例 1:允许容器使用的内存上限为 128M:
1 | docker run -it -m 128m centos |
例 2:创建一个 docker,只使用 2 个 cpu 核心,只能使用 128M 内存
1 | docker run -it --cpuset-cpus 0,1 -m 128m centos |
控制IO
1 | docker run --help | grep write-b |
–device-read-bps value 限制此设备上的写速度(bytes per second),单位可以是 kb、mb 或者 gb。
防止某个 Docker 容器吃光你的磁盘 I / O 资源
例 1:限制容器实例对硬盘的最高写入速度设定为 2MB/s。
1 | mkdir -p /var/www/html/ |
容器结束自动释放资源
1 | docker run --help | grep rm |
作用:当容器命令运行结束后,自动删除容器,自动释放资源
1 | docker run -it --rm --name auto-rm-test centos sleep 6 |
镜像仓库harbor
Docker 容器应用的开发和运行离不开可靠的镜像管理,虽然 Docker 官方也提供了公共的镜像仓库, 但是从安全和效率等方面考虑,部署我们私有环境内的 Registry 也是非常必要的。Harbor 是由 VMware 公司开源的企业级的 Docker Registry 管理项目,它包括权限管理(RBAC)、LDAP、日志审核、管理界面、 自我注册、镜像复制和中文支持等功能。
官网地址:https://github.com/goharbor/harbor