【java总结】Docker总结

docker基础

什么是Docker?

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。使用Go语言进行开发实现,由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。Docke最初实现是基于LXC(Linux Container)。

Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

解决了什么问题

Docker解决了运行环境和配置问题,方便发布,也就方便做持续集成

Docker与虚拟机有何不同?

docker可以将应用程序封装在不同的容器中执行,并为每个容器提供单独的运行环境,每个容器内部都可以视为一个完整的主机环境,其功能类似于虚拟机,但和虚拟机相比容器更加轻量。

虚拟机和主机操作系统的关系是相互独立的,它们共享主机硬件但虚拟机之间以及和主机操作系统之间内核是相互隔离的,虚拟机拥有完整的系统内核。容器是运行在主机操作系统之上的,容器以及主机其他程序共享系统内核,但每个容器拥有自己独立的运行库以及其他运行环境

image-20200804230431186

Docker核心概念

镜像是 Docker 运行容器的前提,仓库是存放镜像的场所,可见镜像更是 Docker 的核心。

镜像

Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。

镜像不包含任何动态数据,其内容在构建之后也不会被改变。镜像(Image)就是一堆只读层(read-only layer)的统一视角,也许这个定义有些难以理解,下面的这张图能够帮助读者理解镜像的定义:

image-20200804231333267

从左边我们看到了多个只读层,它们重叠在一起。除了最下面一层,其他层都会有一个指针指向下一层。这些层是 Docker 内部的实现细节,并且能够在主机的文件系统上访问到。

统一文件系统(Union File System)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角。

这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在图片的右边看到这个视角的形式。

容器

容器(Container)的定义和镜像(Image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

image-20200804231406804

由于容器的定义并没有提及是否要运行容器,所以实际上,容器 = 镜像 + 读写层。

仓库

Docker 仓库是集中存放镜像文件的场所。镜像构建完成后,可以很容易的在当前宿主上运行。

但是, 如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry(仓库注册服务器)就是这样的服务。

有时候会把仓库(Repository)和仓库注册服务器(Registry)混为一谈,并不严格区分。

Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。

实际上,一个 Docker Registry 中可以包含多个仓库(Repository),每个仓库可以包含多个标签(Tag),每个标签对应着一个镜像。

所以说,镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。

我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 Latest 作为默认标签。

仓库又可以分为两种形式:

  • Public(公有仓库)
  • Private(私有仓库)

Docker Registry 公有仓库是开放给用户使用、允许用户管理镜像的 Registry 服务。

一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。

当用户创建了自己的镜像之后就可以使用 Push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 Pull 下来就可以了。

我们主要把 Docker 的一些常见概念如 Image,Container,Repository 做了详细的阐述,也从传统虚拟化方式的角度阐述了 Docker 的优势。

我们从下图可以直观地看到 Docker 的架构:

image-20200804231628184

Docker 使用 C/S 结构,即客户端/服务器体系结构。Docker 客户端与 Docker 服务器进行交互,Docker服务端负责构建、运行和分发 Docker 镜像。

Docker 客户端和服务端可以运行在一台机器上,也可以通过 RESTful 、 Stock 或网络接口与远程 Docker 服务端进行通信。

image-20200804231651989

这张图展示了 Docker 客户端、服务端和 Docker 仓库(即 Docker Hub 和 Docker Cloud ),默认情况下 Docker 会在 Docker 中央仓库寻找镜像文件。

这种利用仓库管理镜像的设计理念类似于 Git ,当然这个仓库是可以通过修改配置来指定的,甚至我们可以创建我们自己的私有仓库。

Dokcer 组成原理

image-20200804224851934

所有 Docker 容器都共享主机系统的 bootfs 即 Linux 内核

每个容器有自己的 rootfs,它来自不同的 Linux 发行版的基础镜像,包括 Ubuntu,Debian 和 SUSE 等

所有基于一种基础镜像的容器都共享这种 rootfs

image-20200806155128497

Docker 的优点

  • 1、简化程序:
    Docker 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,便可以实现虚拟化。Docker改变了虚拟化的方式,使开发者可以直接将自己的成果放入Docker中进行管理。方便快捷已经是 Docker的最大优势,过去需要用数天乃至数周的 任务,在Docker容器的处理下,只需要数秒就能完成。
  • 2、避免选择恐惧症:
    如果你有选择恐惧症,还是资深患者。Docker 帮你 打包你的纠结!比如 Docker 镜像;Docker 镜像中包含了运行环境和配置,所以 Docker 可以简化部署多种应用实例工作。比如 Web 应用、后台应用、数据库应用、大数据应用比如 Hadoop 集群、消息队列等等都可以打包成一个镜像部署。
  • 3、节省开支:
    一方面,云计算时代到来,使开发者不必为了追求效果而配置高额的硬件,Docker 改变了高性能必然高价格的思维定势。Docker 与云的结合,让云空间得到更充分的利用。不仅解决了硬件管理的问题,也改变了虚拟化的方式。

docker常用命令

docker安装

yum install docker-io

/etc/init.d/docker start启动docker。

制作自己的Docker镜像

这里先配置下Docker的镜像加速

https://aw7s5l76.mirror.aliyuncs.com

可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器,大家最好使用自己的加速地址

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://aw7s5l76.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

自己制作的docker我都推送到了阿里云docker管理平台了,而不是推送到docker官方的管理平台。下面首先介绍一下使用阿里云docker管理平台创建自己的仓库。

image-20200804232408934

创建完成后登录阿里云docker

$ sudo docker login --username=  registry.cn-beijing.aliyuncs.com

登录registry的用户名是您的阿里云账号全名,密码是您开通服务时设置的密码。

你可以在镜像管理首页点击右上角按钮修改docker login密码。

从registry中拉取镜像:

$ sudo docker pull registry.cn-beijing.aliyuncs.com/spencer_docker/django2:[镜像版本号]

将镜像推送到registry:

$ sudo docker login --username= registry.cn-beijing.aliyuncs.com

$ sudo docker tag [ImageId] registry.cn-beijing.aliyuncs.com/spencer_docker/django2:[镜像版本号]

$ sudo docker push registry.cn-beijing.aliyuncs.com/spencer_docker/django2:[镜像版本号]

其中[ImageId],[镜像版本号]请你根据自己的镜像信息进行填写。

① -m是对提交的描述,author是作者(选填),后面的c28e5976a6ab是修改容器的id,后面的是新镜像名字和标签(tag)。

② 成功之后,会生成新的镜像id

③ 输入docker images 查看镜像,会发现新的名为spencer/django,标签为v1的镜像已经存在。

4)推送到阿里云docker管理平台,命令参考上面将镜像推送到阿里云registry

容器生命周期管理

docker run :创建一个新的容器并运行一个命令

docker start :启动一个或多个已经被停止的容器

docker stop :停止一个运行中的容器

docker restart :重启容器

docker kill :杀掉一个运行中的容器

docker rm :删除一个或多少容器

docker pause :暂停容器中所有的进程

docker unpause :恢复容器中所有的进程

docker create :创建一个新的容器但不启动它

docker exec :在运行的容器中执行命令

容器操作

docker ps : 列出容器

docker inspect : 获取容器/镜像的元数据

docker top :查看容器中运行的进程信息,支持 ps 命令参数

docker attach :连接到正在运行中的容器

docker events : 从服务器获取实时事件

docker logs : 获取容器的日志

docker wait : 阻塞运行直到容器停止,然后打印出它的退出代码

docker export :将文件系统作为一个tar归档文件导出到STDOUT

docker port :列出指定的容器的端口映射,或者查找将PRIVATE_PORT NAT到面向公众的端口

docker ps -a | awk ‘{print $1}’ 查询

docker ps -a | awk ‘{print $1}’|xargs docker stop 批量停止

docker ps -a | awk ‘{print $1}’|xargs docker rm 删除

容器rootfs命令

docker commit :从容器创建一个新的镜像(提交镜像)

docker cp :用于容器与主机之间的数据拷贝

docker diff : 检查容器里文件结构的更改

镜像仓库

docker login : 登陆到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub

docker logout : 登出一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub

docker pull : 从镜像仓库中拉取或者更新指定镜像(下载镜像)

docker push : 将本地的镜像上传到镜像仓库,要先登陆到镜像仓库

docker search : 从Docker Hub查找镜像

本地镜像管理

docker images : 列出本地镜像

docker rmi : 删除本地一个或多少镜像

docker tag : 标记本地镜像,将其归入某一仓库

docker build 命令用于使用 Dockerfile 创建镜像

docker history : 查看指定镜像的创建历史

docker save : 将指定镜像保存成 tar 归档文件

docker import : 从归档文件中创建镜像

info|version

docker info : 显示 Docker 系统信息,包括镜像和容器数。。

docker version :显示 Docker 版本信息

DockerFile

dockerfile是一个包含构建镜像所需命令的文本文件,docker可以根据dockerfile文件构建镜像
dockerfile以from开始,表示使用另一个镜像作为生成当前镜像的基础,新创建的镜像会包含基础镜像的内容。dockerfile中包含了构建镜像所需的全部命令。

# 使用python的运行环境作为parent image
FROM python:2.7-slim

# 设置工作路径
WORKDIR /app

# 将当前目录的内容复制到相应路径下
COPY . /app

# 下载requirements.txt中要求的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 暴露容器的80端口
EXPOSE 80

# 定义环境变量
ENV NAME World

#当docker开始运行时执行文件
CMD ["python", "app.py"]

Docker场景题

Docker是如何实现隔离的?

Docker的隔离性主要运用Namespace 技术。传统上Linux中的PID是唯一且独立的,在正常情况下,用户不会看见重复的PID。然而在Docker采用了Namespace,从而令相同的PID可于不同的Namespace中独立存在。如,A Container 之中PID=1是A程序,而B Container之中的PID=1同样可以是A程序。虽然Docker可透过Namespace的方式分隔出看似是独立的空间,然而Linux内核(Kernel)却不能Namespace,所以即使有多个Container,所有的system call其实都是通过主机的内核处理,这便为Docker留下了不可否认的安全问题。

namespace(命名空间)可以隔离哪些

  • 文件系统需要是被隔离的
  • 网络也是需要被隔离的
  • 进程间的通信也要被隔离
  • 针对权限,用户和用户组也需要隔离
  • 进程内的PID也需要与宿主机中的PID进行隔离
  • 容器也要有自己的主机名
  • 有了以上的隔离,我们认为一个容器可以与宿主机和其他容器是隔离开的。
  • 恰巧Linux 的namespace可以做到这些。

使用Namespace进行容器的隔离有什么缺点呢?
  最大的缺点就是隔离不彻底
  1)容器知识运行在宿主机上的一种特殊的进程,那么多个容器之间使用的就还是同一个宿主机的操作系统内核
  2)在Linux内核中,有很多资源和对象是不能被Namespace化的,最典型的例子是:时间即如果某个容器修改了时间,那整个宿主机的时间都会随之修改
  3)容器给应用暴露出来的攻击面比较大,在生产环境中,没有人敢把运行在物理机上的Linux容器暴露在公网上

Linux的 cgroups
强大内核工具cgroups
cgroups是Linux的另外一个强大的内核工具,有了cgroups,不仅可以限制被namespace隔离起来的资源,还可以为资源设置权重、计算使用量、操控任务(进程或县城)启停等。说白了就是:cgroups可以限制、记录任务组所使用的物理资源(包括CPU,Memory,IO等),是构建Docker等一系列虚拟化管理工具的基石。

cgroups 的作用
  cgroups 为不同用户层面的资源管理提供了一个统一接口,从单个的资源控制到操作系统层面的虚拟化,cgroups提供了4大功能。

  • 资源限制

cgroups可以对任务使用的资源总额进行限制。
如 设定应用运行时使用的内存上限,一旦超过配额就发出OOM提示

  • 优先级分配

通过分配的CPU时间片数量以及磁盘IO带宽大小,实际上就相当于控制了任务运行的优先级

  • 资源统计

cgroups可以统计系统的资源使用量
如CPU使用时长,内存用量等,这个功能非常适用于计费

  • 任务控制

cgroups 可以对任务进行挂起、恢复等操作