每个容器都在自己的命名空间中运行,但使用与所有其他容器完全相同的内核。发生隔离是因为内核知道分配给进程的命名空间,并且在API调用期间确保进程只能访问其自己的命名空间中的资源。
docker引擎(包含Docker客户端&服务端),docker镜像,docker容器,Registry镜像仓库。其C/S架构。
Docker是一个可以把开发的应用程序自动部署到容器的开源引擎。
容器的中心思想就是秒级启动;一次封装、到处运行;这是主机部署应用无法达到的效果,但同时也更应该注重容器的数据持久化问题。
另外,容器部署可以将各个服务进行隔离,互不影响,这也是容器的另一个核心概念。
docker run 启动镜像
docker kill 立即停止容器
docker stop 有序停止容器
docker start 启动被挂起的容器
docker pull 拉取或者更新指定镜像
docker push 将镜像推送至远程仓库
docker rm 删除容器
docker rmi 删除镜像
docker images 列出所有镜像
docker ps 列出所有容器
docker logs 看后台的日志
docker attach 同样操作的是已运行的容器,可以将本机标准输入(键盘输入)输到容器中,也可以将容器的输出显示在本机的屏幕上,如果你想查看容器运行过程中产生的标准输入输出,用attach。
docker exec 在已运行的容器中,执行命令,操作对象是容器,如果你要进入已运行的容器,并且执行命令,用exec。
FROM:指定基础镜像
LABEL:功能是为镜像指定标签
RUN:运行指定的命令
CMD:容器启动时要运行的命令
ONBUILD:当镜像用作另一个镜像构建的基础时,该指令向镜像添加将在稍后执行的触发指令。如果要构建将用作构建其他镜像的基础的镜像(例如,可以使用特定于用户的配置自定义的应用程序构建环境或守护程序)。
docker cp 命令用于容器与主机之间的数据拷贝
主机到容器:
docker cp /www 96f7f14e99ab:/www/
容器到主机:
docker cp 96f7f14e99ab:/www /tmp/
Docker提供docker stats和docker事件等工具来监控生产中的Docker。我们可以使用这些命令获取重要统计数据的报告。
按Ctrl+p 后按Ctrl+q、如果按Ctrl+c 会使容器内的应用进程终止,进而会使容器终止。
使用Docker compose可以用YAML文件来定义一组需要启动的容器,以及容器运行时的属性。docker-compose用来对这一组容器进行操作。只能管理当前主机上的docker集群。
docker swarm 原生的Docker集群管理工具,依赖docker本身,很多重要功能依赖团队二次开发。且社区不够活跃,一般公司生产环境会选择k8s,个人项目或者容器数量较少可选swarm,只需要docker即可完成,相对较轻。可以管理多机上的容器,而且功能丰富,具有负载均衡、扩容等功能。
Docker是一个应用层的抽象,容器之间通过网络命名空间进行隔离,多个容器共享同一个操作系统内核。VM是对物理硬件层的抽象,每个VM都包含独立的操作系统,重且启动缓慢。VM主要为了提供系统环境,容器主要是为了提供应用环境。
Docker主要专注于在应用程序容器内自动部署应用程序。应用程序容器旨在打包和运行单个服务,而系统容器则设计为运行多个进程,如虚拟机。因此,Docker被视为容器化系统上的容器管理或应用程序部署工具。
Docker不是虚拟化方法。它依赖于实际实现基于容器的虚拟化或操作系统级虚拟化的其他工具。为此,Docker最初使用LXC驱动程序,然后移动到libcontainer现在重命名为runc。
LXC是一种容器引擎,所谓容器引擎就是一种驱动和管理容器生命周期的runtime工具。
所谓runtime管理工具,就是指只对容器运行时的相关状态和操作进行管理的工具。
LXC利用Linux上相关技术实现容器,Docker则在如下的几个方面进行了改进:
Libcontainer是一种容器引擎,是一种runtime管理工具,Docker所有对容器的管理操作都是通过调用Libcontainer的API来实现的。
Libcontainer提供的功能有:运行容器、暂停容器、恢复容器、向容器发送信号、获取容器信息、修改容器配置、Checkpoint容器。
容器的技术核心就是Nampspace和Cgroup,所谓运行一个容器就是创建属于容器的Namespace和Cgroup。
Cgroup: 是control group的缩写,属于Linux内核提供的一个特性,用于限制和隔离一组进程对系统资源的使用,也就是做资源Qos,这些资源主要包括CPU、内存、IO和网络。Cgroup可以对进程进行任意分组,不同组获取资源的限额不同。Cgroup的原生接口通过cgroupfs提供,类似于procfs和sysfs,是一种虚拟文件系统。Cgroup通过内部的子系统实现不同资源的分配管理。
NameSpace: 是将内核的全局资源做封装,使得每个Namespace都有一份独立的资源,不同进行在各自的Namespace内对同一种资源的使用不会相互干扰。
对Namespace的操作主要通过这三个系统调用来完成的:
Linux内核总共实现了六种Namespace:
创建容器:
具体流程入下图所示。
runC也是一款容器引擎,是runtime引擎,它由Libcontainer演变而来,现在runC已经代替Libcontainer称为Docker的引擎了。
runC在底层还是使用了Libcontainer的库。
runC通过读取用户编写的JSON文件,获取容器所需的所有信息,然后把内容填充到Libcontainer提供的Config文件中,让Libcontainer去做底层的工作。
由于 /proc 文件系统是以只读的方式挂载到容器内部,所以在容器内看到的都是宿主机的信息,包括 CPU 和 Memory,docker 是以 cgroups 来进行资源限制的,而 jdk1.9 以下版本目前无法自动识别容器的资源配额,1.9以上版本会自动识别和正常读取 cgroups 中为容器限制的资源大小。
Memory 隔离不彻底
Docker 通过 cgroups 完成对内存的限制,而 /proc 文件目录是以只读的形式挂载到容器中,由于默认情况下,Java 压根就看不到 cgroups 限制的内容的大小,而默认使用 /proc/meminfo 中的信息作为内存信息进行启动,默认情况下,JVM 初始堆大小为内存总量的 1/4,这种情况会导致,如果容器分配的内存小于 JVM 的内存, JVM 进程会被 linux killer 杀死。
那么目前有几种解决方式:
(1)升级 JDK 版本到1.9以上,让 JVM 能自动识别 cgroups 对容器的资源限制,从而自动调整 JVM 的参数并启动 JVM 进程。
(2)对于较低版本的JDK,一定要设置 JVM 初始堆大小,并且JVM 的最大堆内存不能超过容器的最大内存值,正常理论值应该是:容器 limit-memory = JVM 最大堆内存 + 750MB。
(3)使用 lxcfs ,这是一种用户态文件系统,用来支持LXC 容器,lxcfs 通过用户态文件系统,在容器中提供下列 procfs 的文件,启动时,把宿主机对应的目录 /var/lib/lxcfu/proc/meminfo 文件挂载到 Docker 容器的 /proc/meminfo 位置后,容器中进程(JVM)读取相应文件内容时,lxcfs 的 fuse 将会从容器对应的 cgroups 中读取正确的内存限制,从而获得正确的资源约束设定。
CPU 隔离不彻底
JVM GC (垃圾回收)对于 java 程序执行性能有一定的影响,默认的 JVM 使用如下公式: ParallelGCThreads = ( ncpu <= 8 ) ? ncpu:3 + (ncpu * 5)/ 8 来计算并行 GC 的线程数,但是在容器里面,ncpu 获取的就是所在宿主机的 cpu 个数,这会导致 JVM 启动过多的 GC 线程,直接的结果就是 GC 的性能下降,java 服务的感受就是:延时增加, TPS 吞度量下降,针对这种问题,也有以下几种解决方案:
(1)显示传递 JVM 启动参数:“-XX: ParallelGCThreads" 告诉 JVM 应该启动多少个并行 GC 线程,缺点是需要业务感知,而且需要为不同配置的容器传递不同的 JVM 参数。
(2)在容器内使用 Hack 过的 glibc ,使 JVM 通过 sysconf 系统调用能正确获取容器内 CPU 资源核数,优点是业务无感知,并且能自动适配不同配置的容器,缺点是有一定的维护成本。
作者:莹宝与梨梦
链接
整体原则上、尽量保持镜像功能的明确和内容的精简、要点包括:
sudo docker rmi$(sudo docker images -q -f danging=true)
批量的停止容器,删除启动过了的容器也都可以这样来命令。
一般而言,虽然ADD并且COPY在功能上类似,但是首选COPY。
那是因为它比ADD更易懂。COPY仅支持将本地文件复制到容器中,而ADD具有一些功能(如仅限本地的tar提取和远程URL支持),这些功能并不是很明显。因此,ADD的最佳用途是将本地tar文件自动提取到镜像中,如ADD rootfs.tar.xz /。
唯一差别在于ADD源文件可以支持url且可以对压缩文件进行解压操作。而COPY针对的是当前构建环境。
镜像是一个只读模板,包括运行容器所需的数据,其内容在构建之后就不会被改变,可以用来创建新的容器。 镜像由多个只读层组成,容器在只读层的基础上多了一个读写层。
CMD 用于指定容器启动时候默认执行的命令。可以被docker run指定的启动命令覆盖。ENTRYPONIT 指令可让容器以应用程序或者服务的形式运行。一般不会被docker run指定的启动命令覆盖。dockerfile中的多个CMD & ENTRYPONIT只有最后一个会生效。
Docker的默认存放位置是/var/lib/docker,如果希望将Docker的本地文件存储到其他分区,可以使用Linux软连接的方式来做。
Ubuntu系统下Docker的配置文件是/etc/default/docker,CentOS系统配置文件存放在/etc/sysconfig/docker