Docker简介
一、什么是 Docker?
从概念上来看 Docker 和我们传统的虚拟机比较类似,只是更加轻量级,更加方便使用。docker与虚拟机的对比如下:
特性 | 容器 | 虚拟机 |
---|---|---|
启动速度 | 秒级 | 分钟级 |
硬盘使用 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于原生 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
Docker是一个Client-Server结构的系统,一个docker 容器,是一个运行时环境,可以简单理解为进程运行的集装箱。
二、为啥要用容器?
- 环境隔离
- 方便部署
三、Docker Engine
Docker Engine是一个C/S架构的应用程序,主要包含下面几个组件:
- 常驻后台进程Dockerd
- 一个用来和 Dockerd 交互的 REST API Server
- 命令行CLI接口,通过和 REST API 进行交互(我们经常使用的 docker 命令)
四、Docker 架构
Docker 使用 C/S (客户端/服务器)体系的架构,Docker 客户端与 Docker 守护进程通信,Docker 守护进程负责构建,运行和分发 Docker 容器。Docker 客户端和守护进程可以在同一个系统上运行,也可以将 Docker 客户端连接到远程 Docker 守护进程。Docker 客户端和守护进程使用 REST API 通过UNIX套接字或网络接口进行通信。
- Docker Damon:dockerd,用来监听 Docker API 的请求和管理 Docker 对象,比如镜像、容器、网络和 Volume。
- Docker Client:docker,docker client 是我们和 Docker 进行交互的最主要的方式方法,比如我们可以通过 docker run 命令来运行一个容器,然后我们的这个 client 会把命令发送给上面的 Dockerd,让他来做真正事情。
- Docker Registry:用来存储 Docker 镜像的仓库,Docker Hub 是 Docker 官方提供的一个公共仓库,而且 Docker 默认也是从 Docker Hub 上查找镜像的,当然你也可以很方便的运行一个私有仓库,当我们使用 docker pull 或者 docker run 命令时,就会从我们配置的 Docker 镜像仓库中去拉取镜像,使用 docker push 命令时,会将我们构建的镜像推送到对应的镜像仓库中。
- Images:镜像,镜像是一个只读模板,带有创建 Docker 容器的说明,一般来说的,镜像会基于另外的一些基础镜像并加上一些额外的自定义功能。比如,你可以构建一个基于 Centos 的镜像,然后在这个基础镜像上面安装一个 Nginx 服务器,这样就可以构成一个属于我们自己的镜像了。
- Containers:容器,容器是一个镜像的可运行的实例,可以使用 Docker REST API 或者 CLI 来操作容器,容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。
五、安装
直接前往官方文档选择合适的平台安装即可,Docker Desktop/Docker Engine
镜像和容器的基本操作
一、获取镜像
docker pull [选项] [Docker Registry 地址[:端口]/]仓库名[:标签]
docker pull centos:7
二、运行
docker run -it --rm \
centos:7 \
/bin/bash
- -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
- –rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用–rm可以避免浪费空间。
- centos:7:这是指用 centos:7 镜像为基础来启动容器。
- bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。
当利用docker run来创建容器时,Docker 在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
三、列出镜像
docker image ls
四、启动已终止的容器
docker start 容器Id/容器名
五、后台运行
docker run -d centos:7 /bin/sh -c "while true; do echo hello world; sleep 1; done"
docker logs
docker ps
docker ps -a
六、终止容器
docker stop 容器Id/容器名
七、进入容器
docker exec -it 容器Id/容器名 bash
八、删除容器
docker rm -f 容器Id/容器名
九、删除本地镜像
docker rmi 镜像名/镜像Id
Dockerfile定制镜像
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
- FROM指定基础镜像
- RUN执行命令,尽量减少RUN命令的条数
- 在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。
- 构建镜像:docker build -t tag名称 .
- 镜像构建上下文(Context),docker build 命令最后有一个“.”就是在指定上下文路径。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。
- COPY和ADD,ADD时如果源文件是一个压缩文件会自动解压,ADD时如果源是一个url会自动下载
数据共享和持久化
一、数据卷(Data Volumes)
docker volume create my-vol
docker volume ls
docker volume inspect my-vol
docker run -d -P \
--name web \
# -v my-vol:/wepapp \
--mount source=my-vol,target=/webapp \
training/webapp \
python app.py
docker inspect web
docker volume rm my-vol
docker volume prune
二、挂载主机目录(Bind mounts)
docker run -d -P \
--name web \
# -v /src/webapp:/opt/webapp:ro \
--mount type=bind,source=/src/webapp,target=/opt/webapp,readonly \
training/webapp \
python app.py
Docker的网络模式
一、Bridge模式
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。从docker0子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。
bridge模式是 docker 的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker 实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。bridge模式如下图所示:
docker network create -d bridge my-net
docker run -it --rm --name busybox1 --network my-net busybox sh
二、其他
- Host模式
- Container模式
- None模式
Tips & Thinking
- 为什么容器可以访问外网,而外网不可以访问容器?(为什么我们的办公电脑可以访问外网,而外网不可以访问它?)
- NAT/NAPT 网络地址转换
- telnet www.baidu.com 80/tcpdump -i any -n host 112.80.248.75
- 如果要让容器被主机外部访问如何处理?
route add 172.17.0.0 mask 255.255.255.0 192.168.238.131
route print
tracert 172.17.0.2
- docker run后的命令、CMD、entrypoint
- CMD指令可以指定容器启动时要执行的命令,但它可以被docker run命令的参数覆盖掉。
- ENTRYPOINT指令也可指定容器启动时要执行的命令。如果dockerfile中也有CMD指令,CMD中的参数会被附加到ENTRYPOINT 指令的后面。 如果这时docker run命令带了参数,这个参数会覆盖掉CMD指令的参数,并也会附加到ENTRYPOINT 指令的后面。
- 容器启动后就退出,如何排查?
- docker logs查看容器运行的日志
- docker exec进入容器进行各种分析
Docker技术的基础
namespace,容器隔离的基础,保证A容器看不到B容器. 6个名空间:User,Mnt,Network,UTS,IPC,Pid
cgroups,容器资源统计和隔离。主要用到的cgroups子系统:cpu,blkio,device,freezer,memory
unionfs,典型:aufs/overlayfs,分层镜像实现的基础
Linux 命名空间、控制组和 UnionFS 三大技术支撑了目前 Docker 的实现,也是 Docker 能够出现的最重要原因。
一、namespace
docker容器本质上是宿主机的进程,Docker通过namespace实现了资源隔离。
linux内核提拱了6种namespace隔离的系统调用,如下图所示,但是真正的容器还需要处理许多其他工作。
namespace | 系统调用参数 | 隔离内容 |
---|---|---|
UTS | CLONE_NEWUTS | 主机名或域名 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 |
PID | CLONE_NEWPID | 进程编号 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口等 |
Mount | CLONE_NEWNS | 挂载点 |
User | CLONE_NEWUSER | 用户与组 |
实际上,linux内核实现namespace的主要目的,就是为了实现轻量级虚拟化技术服务。在同一个namespace下的进程合一感知彼此的变化,而对外界的进程一无所知。这样就可以让容器中的进程产生错觉,仿佛自己置身一个独立的系统环境中,以达到隔离的目的。
二、control groups
cd /sys/fs/cgroup/cpu
mkdir znyin
echo 20000 > znyin/cpu.cfs_quota_us
echo $$ > znyin/tasks
while true
do
echo ''
done
# 重新打开一个shell,使用top观察CPU的使用率。可以发现,我们的死循环,最多只使用了20%的CPU
5分钟了解Docker原理之二,最简单的Cgroups介绍!
三、OverlayFS
参考资料
- 《Linux Namespace 简介》
- 《自已动手写Docker》
- Docker基础
- iptables 命令解析
- 使用iptables进行NAT转发