Docker学习笔记及面试题

深入浅出Docker学习笔记

Docker引擎

  • Docker引擎:用来运行和管理容器的核心文件
    • 模块化(基于开放容器计划OCI相关标准要求)
    • 主要组件:Docker ClientDocker daemonDocker守护进程)、containerd以及runc
  • runc
    • OCI容器运行时规范的参考实现
    • 实质上是一个轻量级的、针对Libcontainer进行了包装的命令行交互工具
      • LibcontainerDocker公司开发用于替代LXC的工具
      • LXC提供了对诸如命令空间(Namespace)控制组(CGroup)等基础工具的操作能力,它们是基于Linux内核容器虚拟化技术
      • LXC基于Linux,对跨平台有阻碍
    • 作用:创建容器
      • 是一个CLI包装器,实质上就是一个独立的容器运行时工具
      • runc所在的层有时也称为“OCI层
    • runc与操作系统内核接口进行通信,基于所有必要的工具(NamespaceCGroup等)来创建容器,容器进程以runc的子进程启动,启动完毕后,runc将会退出
  • containerd
    • 作用:负责容器的生命周期管理——start|stop|pause|rm...
    • 后来也负责镜像管理,额外功能都是模块化可选的
    • daemon(守护进程)方式运行
    • 不负责创建容器,而是指挥runc去做containerdDocker镜像转换成OCI bundle,并让runc基于此创建一个新的容器
  • daemon
    • 提供功能丰富、基于版本的REST API
    • 使用CRUD风格API,通过gRPCcontainerd进行通信
    • 将所有的用于启动、管理容器的逻辑和代码从daemon中移除,意味着容器运行时Docker daemon是解耦的,有时称为“无守护进程的容器”
      • 好处:对Docker daemon的维护和升级工作不会影响到运行中的容器
    • 将越来越多的功能从daemon中拆解出来并模块化之后,daemon还剩什么?
      • 暂时还包括的主要功能:镜像管理、镜像构建、REST API、身份验证、安全、核心网络以及编排
  • shim
    • 是实现daemon的容器不可或缺的工具
    • containerd指挥runc来创建容器。实际上,每次创建容器时,shim都会fork一个新的runc实例,一旦容器创建完毕,对应的runc进程就会退出
    • 一旦容器进程的父进程runc退出,相关联的containerd-shim进程就会成为容器的父进程。
    • 作为容器的父进程,shim的部分职责如下:
      • 保持所有的STD_INSTD_OUT流是开启状态,从而当daemon重启的时候,容器不会因为管道(pipe)的关闭而终止
      • 将容器的退出状态反馈给daemon

Docker镜像

  • 镜像由多个层组成,每个层叠加后,从外部看就像一个独立的对象。
  • 镜像内部是一个精简的操作系统(OS),同时还包含应用运行所必须的文件和依赖包
    • 通常镜像中只包含一个精简的shell,甚至没有。
    • 镜像中不包含内核——容器都是共享所在Docker主机的内核
  • 镜像可以理解为一种构建时(build-tiem)结构,而容器可以理解为一种运行时(run-time)结构
  • 一个镜像可以有多个标签
  • 悬虚镜像
    • 没有标签的镜像
    • 出现原因:构建了一个新镜像,然后为新镜像打了一个已经存在的标签,接着Docker会移除旧镜像上的标签,旧镜像就变成了一个悬虚镜像
  • Docker通过存储引擎(新版本采用快照机制)的方式实现镜像层堆栈,并保证多镜像层对外展示位同一的文件系统。
    • Linux上可用的存储引擎有AUFSOverlay2Device MapperBtrfs以及ZFS
    • Windows上仅支持windowsfilter
  • 多个镜像之间可以并且确实会共享镜像层。节省空间并提升性能
    • 镜像本身就是一个配置对象,其中包含了镜像层的列表和一些元数据信息
    • 镜像层才是实际数据存储的地方
  • 内容散列
    • 镜像的唯一标识是一个加密ID,即配置对象本身的散列值
    • 每个镜像层也由一个加密ID区分,其值为镜像层本身内容的散列值
    • 修改镜像的内容或其中任一的镜像层,都会导致散列值的变化。镜像和镜像层都是不可变的,任何改动都能很轻松地辨别
    • 推送和拉取镜像的时候,都会对镜像进行压缩来节省网络带宽以及仓库的二进制存储空间,但是压缩会改变镜像内容,这导致镜像内容的散列值在推送或者拉取之后,会与镜像内容不相符。为了避免该问题,每个镜像层同时会包含一个分发散列值,这是一个压缩版镜像的散列值。
  • 如果某镜像中有一个Bug,拉取下来并修改之后,重新推送回仓库。此时使用了问题镜像相同的标签,原镜像被覆盖,但是生产环境使用的大量运行中的容器无法区分正在使用的镜像版本是修复前还是修复之后的
    • Docker1.10中引入了新的内容寻址存储模型。
    • 作为模型的一部分,每一个镜像现在都有一个基于其内容的密码散列值,镜像内容变更一定会导致散列值变更
  • 多架构镜像,配置Menifest列表
  • 删除镜像时,必须确保与该镜像相关的全部容器停止并被删除
  • 如果镜像的某个层被多个镜像共享,那么只有依赖该镜像层的所有镜像被删除后,该镜像层才会删除
  • 常用命令
    • docker container run
    • docker service create
    • docker image pull <repository>:<tag>:拉取镜像,如果没有指定tag标签,默认latest-a 拉取全部镜像
    • docker image ls
      • --filter dangling=true/false:仅返回悬虚镜像(true
      • --filter before id/name:返回在id或者镜像名之前创建的全部镜像
      • --filter since id/name:返回在id或者镜像名之后创建的全部镜像
      • --filter label::根据标注(label)的名称或值,对镜像进行过滤
      • --format:通过go模版对输出内容进行格式化
      • --digests:查看镜像摘要(内容散列值)
    • docker search
      • NAME:搜索仓库名为NAME中包含的所有镜像
      • label:返回所有仓库中包含label的镜像
      • --filter is-official=true:过滤非官方镜像
      • --filter is-automated=true:只显示自动创建的镜像
      • --limit:增加返回内容的行数,最大100行,默认25行
    • docker image inspect name:显示镜像分层信息
    • docker image rm id

Docker容器

  • 虚拟机与容器的最大区别:容器更快并且更轻量级——容器会共享其所在主机的操作系统/内核
    • 容器只有一份OS损耗,也只要一份授权,占用更少的资源,启动更快
  • Hypervisor硬件虚拟化,容器时操作系统虚拟化
  • 常用命令
    • docker container run <image> <app>:启动容器
      • docker container run -it ubuntu /bin/bash-it参数表示将当前终端(terminal)以交互方式连接到容器的shell上。启动ubuntu镜像bash程序
      • -d:后台模式
      • sleep time:运行指定time时间关闭容器
      • --restart
        • always
        • unless-stopped
        • on-failed
    • docker container exec -it id bash:将终端重新连接到docker
    • docker container stop
    • docker container start
    • docker container rm
    • Crtl+PQ:断开shell与容器终端的链接,并在退出后保持容器在后台处于运行(UP)状态
    • docker container exec:允许用户在运行状态的容器中,启动一个新进程
    • docker container inspect:显示容器的配置细节和运行时信息

应用的容器化

  • 项目源码
  • 编写Dockerfile
    • 对当前应用的描述
    • 指导Docker完成应用容器化
    • FROM:指定基础镜像层
    • LABEL:设置一个键值对
    • RUN:会在FROM指定的基础镜像层上,新建一个镜像层,并执行指令,例如安装程序
    • COPY:将应用相关文件从构建上下文中复制到当前镜像中,并新建一个镜像层来存储
    • WORKDIR:为Dockerfile中尚未执行的指令设置工作目录,该目录与镜像相关,并且会作为元数据记录到镜像的配置中,但不会创建新的镜像层
    • EXPOSE 8080:暴露指定端口,作为元数据保存,不会产生新的镜像层
    • ENTERPOINT:指定镜像以容器方式启动后默认运行的程序
    • Docker指令不区分大小写,通常采取大写,便于区分;部分指令会创建新的镜像层(通常是添加新的文件或程序),其他指令只会增加或修改镜像的元数据信息
  • docker image build -t web:latest .:在当前目录(.)构建一个名为web:latest的镜像
  • 推送镜像到仓库 docker image push
    • 先使用Docker ID登录Docker Hub
      • Docker login
    • 推送之前,为镜像打上标签
      • Registry(镜像仓库服务,默认docker.io
      • Repository(镜像仓库)
      • Tag(镜像标签,默认latest
      • docker image tag web:latest you_can_access/web:latest:为镜像添加一个额外的标签,并且不需要覆盖已经存在的标签
    • docker image push you_can_access/web:latest
  • 运行应用程序
    • docker container run -d --name ...
  • APP测试
  • 查看构建历史:docker image history web:latest
  • 生产环境的多阶段构建
    • 在一个Dockerfile中添加多个FROM指令,构成几个单独的构建阶段(一般前面几个阶段构建运行环境,一般镜像都很大,最后一个阶段构建应用生产环境,用一个比较小的包作为基础)
    • 最后一个阶段使用COPY --from指令从前面几个阶段构建的镜像中仅复制生产环境相关的应用代码,而不会复制生产环境不需要的构造
    • 完成之后,前几个阶段生成的镜像会变成悬虚镜像
  • 建造者模式构建
    • 至少需要两个Dockerfile
    • 一个用于开发环境Dockerfile.dev,一个用于生产环境Dockerfile.prod(基于较小的基础镜像开始构建)
    • 生产环境的Dockerfile将开发环境中的与应用程序相关的部分复制过来,整个过程需要额外的脚本才能串联起来
  • 最佳实践
    • 利用构建缓存
    • 合并镜像:docker image build --squash
    • 使用no-install-recommends:确保LinuxAPT包管理器仅安装核心依赖
    • 不要使用MSI包Windows

使用Docker Compose部署应用

  • 负责将多个比较小的服务互相协同起来组成一个完整可用的应用
  • 使用一个声明式的配置文件描述整个应用,从而使用一条命令完成部署,部署成功后,还可以通过一系列简单的命令实现对其完整生命周期的管理,甚至,配置文件还可以置于版本控制系统中进行存储和管理
  • Compose文件:可以使用JSON文件,也可以使用JSON的子集YAML文件
    • version必须指定,且总是位于文件的第一行。它定义了Compose文件的格式(主要是API)的版本,version并非是Docker Compose的版本号
    • services用于定义不同的应用服务
      • 下面的key定义服务名
        • build .:指定Docker基于当前目录(.)下的Dockerfile构建镜像,此镜像用于启动该服务的容器
        • command:指定Docker在容器中运行的程序
        • ports:容器内的(-target)端口映射到主机(published)的端口
        • networks:将服务连接到指定的网络上。这个网络应该是已经存在的,或者在networks一级key中定义的网络
        • volumes:指定Dockersource挂载到target上,source指定的卷应该是已存在的,或者在volumes一级key中定义
    • networks用于指引Docker创建新的网络,默认情况下,会创建bridge网络。
      • 这是一个单主机网络,只能实现同一主机上容器的连接
      • 可以使用driver属性指定不同的网络类型
  • Docker Compose部署命令
    • docker-compose up
      • &:将终端窗口返回,日志输出到终端
      • -d:后台运行
      • docker-compose -f you_configure.yaml up :指定Compose文件,默认使用的是docker-compose.yml或者docker-compose.yaml
      • Docker Compose会将项目名称和Compose文件中定义的资源名称连起来,作为新构建的镜像名称;每个容器的名称都以项目名称(所在目录名称)为前缀,此外,还都以一个数字为后缀用于标识容器的实例序号
      • 第一次部署,Docker Compose会检查是否有同名的卷存在,如果不存在,就会创建它
      • Docker主机对卷中文件的修改,会立即反应到应用中
    • docker-compose down:停止和关闭应用,应用被删除,仅留下镜像、卷和源码
    • docker-compose top:列出各个服务(容器)内运行的进程
    • docker-compose stop:停止应用,但不会删除资源
    • docker-compose rm:对于已停止应用,用rm删除
    • docker-compose ps:列出Compose应用中的各个容器
    • docker-compose restart:重启已停止的Compose应用

Docker Swarm

  • Docker Swarm主要包含两方面:
    • 企业级的Docker安全集群
    • 微服务应用编排引擎
  • 集群方面
    • Swarm将一个或多个Docker节点组织起来,可以用集群的方式管理它们
    • 默认内置有加密的分布式集群存储、加密网络、公用TLS、安全集群接入令牌以及一套简化数字证书管理的PKIpublic key infrastructure
    • 可以自如地添加删除节点
  • 编排方面
    • Swarm提供一套丰富的API使得部署和管理复杂的微服务应用变得易如反掌
    • 通过将应用定义在声明式配置文件中,就可以使用原生的Docker命令完成部署
    • 可以执行滚动升级、回滚以及扩缩容操作
  • Swarm集群
    • 唯一前提:所有节点通过可靠的网络相连
    • 节点属性
      • 管理节点(Manager
      • 工作节点(Worker
    • Swarm的配置和状态信息
      • 保存在一套位于所有管理节点上的分布式etcd数据库
      • Swarm内置etcd数据库,无需管理
      • etcd数据库运行于内存中,并保持数据的最新状态
    • Swarm安全性
      • Swarm集成TLS保证集群安全性
      • 使用TLS进行通信加密、节点认证和角色授权
      • 自动密钥轮换(Automatic Key Rotation)在后台默默运行
  • Swarm应用编排
    • Swarm最小调度单元是服务
      • 服务在API中是一个新的对象元素,它基于容器封装了一些高级特性,是一个更高层次的概念
      • 当容器被封装在一个服务中时,我们称之为一个任务或一个副本
      • 服务中增加了诸如扩缩容、滚动升级以及简单回滚等特性
  • 搭建安全Swarm集群
    • 要求
      • 每个节点都需要安装Docker,并且能够与Swarm的其他节点通信
      • 网络方面,需要在路由器和防火墙中开放如下端口
        • 2377/tcp:用于客户端与Swarm进行安全通信(2377端口可配置,但是这是一个约定成俗的端口配置,最好不要改)
        • 7946/tcp7946/udp:用于控制gossip分发
        • 4789/udp:用于基于VXLAN的覆盖网络
    • 大体流程
      • 初始化第一个管理节点
      • 加入额外的管理节点
      • 加入工作节点
      • 完成
    • 初始化一个全新的Swarm
      • 不包含在任何Swarm中的Docker节点,称为运行于单引擎(Single-Engine)模式
        • 一旦被加入Swarm集群,则切换为Swarm模式
        • 单引擎模式下,运行docker swarm init会将其切换到Swarm模式,并创建一个新的Swarm,将自身设置为Swarm的第一个管理节点
      • docker swarm init --advertise-addr 10.0.0.1:2377 --listen-addr 10.0.0.1:2377
        • docker swarm init:通知Docker来初始化一个新的Swarm,并将自身设置为第一个管理节点,开启Swarm模式
        • --advertise-addr:指定其他节点用来连接到当前管理节点的IP和端口
        • --listen-addr:指定用于承载Swarm流量IP和端口。建议总是和--advertise-addr一起使用
      • docker node ls:列出Swarm中的所有节点
      • 添加工作节点与管理节点
        • 在第一个管理节点上执行
          • docker swarm join-token worker:获取工作节点接入命令中的Token
          • docker swarm join-token manager:获取管理节点接入命令中的Token
          • 一个节点是作为工作节点加入还是管理节点加入完全依赖于使用的Token
        • 登录到要加入的节点
          • docker swarm join --token 工作节点/管理节点Token 10.0.0.1:2377 --advertise-addr 10.0.0.4:2377 --listen-addr 10.0.0.4:2377
      • docker node ls:在管理节点上执行,列出Swarm节点
        • MANAGER STATUS无任何显示的是工作节点,显示Leader的是主节点
    • Swarm管理器高可用性(HA
      • HA支持:一个或多个节点发生故障,剩余管理节点也会继续保证Swarm的运转
      • Swarm实现了一种主从方式的多管理节点HA(有多个管理节点,但也总是仅有一个节点处于活动状态,通常处于活动状态的管理节点被称为“主节点”)
      • 主节点是唯一一个会对Swarm发送控制命令的节点,只有主节点才会变更配置,或发送任务到工作节点
        • 命令从一个远程Docker客户端发送给一个管理节点
        • 如果接收到命令的管理节点非节点,则将命令转发给主节点
        • 主节点对Swarm执行命令
      • 管理节点或是Leader或是Follower
        • Raft术语
        • Swarm使用了Raft共识算法
      • 最佳实践原则
        • 部署奇数个管理节点
          • 有利于减少脑裂(这里解释有点奇怪)
        • 不要部署太多的管理节点
          • 对于共识算法来说,更多的参与节点就意味着需要花费更多的时间来达成共识。也就是我要在更多个管理节点上同步状态信息,保证主节点宕机后,Follower能够接替主节点的工作
          • 管理节点之间一定要确保高速可靠的网络连接
      • 内置的Swarm安全机制
        • 提供了开箱即用的合理的默认配置
        • CA设置、接入Token、公用TLS、加密集群存储、加密网络、加密节点ID
      • 锁定Swarm
        • 重启一个旧的管理节点或进行备份恢复仍有可能对集群造成影响
        • 一个旧的管理节点重新接入Swarm自动解密Raft数据库中长时间序列的访问权,这会带来安全隐患
        • 进行备份恢复可能会抹掉最新的Swarm配置
        • Swarm提供了自动锁机制来锁定Swarm,这会强制要求重启的管理节点在提供一个集群解锁码之后才有权重新接入集群
        • docker swarm init --autolock=true
        • docker swarm update --autolock=true
        • 上面两条命令可以开启自动锁,并得到解锁钥匙,解锁钥匙必须妥善保存在安全的地方
        • service docker restart:重启一个管理节点
        • docker swarm unlock:为重启的管理节点解锁Swarm,提供解锁码
    • Swarm服务
      • docker service create --name web-fe -p 8080:8080 --replicas 5 yuq/docker-ci
      • docker service create:声明一个新服务
      • --name web-fe:命名为web-fe
      • -p 8080:8080:将每个节点的8080端口映射到服务副本内部的8080端口
      • --replicas 5:告知Docker应该总是有5个此服务的副本(容器)
      • yuq/docker-ci:告知Docker镜像yuq/docker-ci用于副本
      • 所有的服务都会被Swarm持续监控——Swarm在后台进行轮询检查
        • 如果某个副本宕机了,Docker会启动一个新的副本来是实际状态能够满足期望
      • docker service ps web-fe:产看web-fe服务副本列表及状态
      • docker service inspect --pretty web-fe:查看更详细的信息,--pretty限制输出仅包含最感兴趣的内容
      • 副本服务vs全局服务
        • 服务的默认复制模式是副本模式
          • 部署期望数量的服务副本,并尽可能均匀地将各个副本分布在整个集群中
        • 全局模式 docker service create --mode global
          • 每个节点上仅运行一个副本
      • 服务的扩缩容
        • docker service scale web-fe=10:服务器副本数量从5个加到10个
        • Swarm实现一个调度算法,默认将副本尽可能均衡分配给Swarm中的所有节点
      • 删除服务
        • docker service rm web-fe
        • 谨慎使用删除,删除所有副本时并不会进行确认
        • docker service ls
      • 滚动升级
        • 先创建一个覆盖网络给服务使用:docker network create -d overlay uber-net
          • 覆盖网络是一个二层网络,容器可以接入该网络,并且所有接入的容器均可互相通信
          • 覆盖网络是创建于底层异构网络之上的一个新的二层容器网络
        • 创建一个新的服务接入uber-net网络
          • docker service create --name uber-svc --network uber-net -p 80:80 --replicas 12 yuq/tu-demo:v1
          • -p 80:80:会建立Swarm集群范围的网络流量映射,到达Swarm任何节点80端口的流量,都会映射到任何服务副本的内部80端口
          • 默认的模式是在Swarm中的所有节点开放端口——即使节点上没有服务的副本——称为入站模式
          • 主机模式仅在运行有容器副本的节点上开放端口
          • 打开主机模式:docker service create --name uber-svc --network uber-net --publish published=80,target=80,mode=host --replicas 12 yuq/tu-demo:v1
        • 升级服务
          • docker service update --image yuq/tu-demo:v2 --update-parallelism 2 --update-delay 20s uber-svc
            • --update-parallelism:每次使用新镜像更新几个副本
            • --update-delay:每次更新与上一次之间的延迟时间
    • 故障排除
      • docker service logs:查看log
        • 默认使用json-file日志驱动
        • 其他还有journaldsyslogsplunkgelf
      • 可在daemon.json中配置日志驱动"log-driver":"syslog"
      • docker service create 命令时传入 --logdriver--log-opts 参数可以强制某服务使用一个不同的日志驱动,这会覆盖daemon.json中的配置

Docker网络

  • CNM
    • 容器网络模型(Container Network Model)(设计标准,规定Docker网络架构的基础组成要素)
    • 定义3个基本要素:沙盒(Sandbox)、终端(Endpoint)、网络(Network
    • 沙盒(独立的网络栈)被放置在容器内部,容器中的接口(终端,虚拟网络接口)连接到网络(802.1d网桥)上,同一网络上的容器相互之间可以通信
    • 同一份容器可以有多个终端,分别可以连接到不同的网络(一个终端只能接入某一个网络),同一个容器中的多个终端需要三层路由器的支持才能通信
    • 如果容器需要接入到多个网络,就需要多个终端
  • Libnetwork
    • CNM的具体实现(Go语言编写)
    • 实现了CNM定义的三个基本组件,还实现了本地服务发现、基于Ingress容器负载均衡,以及网络控制层和管理层功能
  • 驱动
    • 通过实现特定网络拓扑的方式来拓展该模型的能力
    • 如果说Libnetwork实现了控制层和管理层的功能,那么驱动就负责实现数据层
    • 网络的连通性和隔离性是由驱动来处理的
    • Docker封装的内置驱动(原生驱动或者本地驱动)
      • LinuxBridgeOverlayMacvlan
      • WindowsNATOverlayTransportL2Bridge
    • Docker也可以使用第三方网络驱动(远程驱动)
    • Libnetwork支持同时激活多个网络驱动。Docker环境支持一个庞大的异构网络
  • 单机桥接网络
    • 单机:只能在单个Docker主机上运行,并只能与所在的Docker主机上的容器进行连接
    • 桥接:802.1d桥接的一种实现(二层交换机)
    • 每一个Docker主机都有一个默认的单机桥接网络,Linux上网络名称为bridgewindows上为nat
    • 可以使用标准Linux工具来查看bridge网络 ip link show docker0
    • 接在相同网络中的容器之间可以通过容器名(新容器都注册了指定的Docker DNS服务ping通(默认的Bridge网络不支持,自定义桥接网络可以)
    • 可以使用端口映射来绕开“桥接网络中的容器只能与位于相同网络中的容器进行通信”的限制
      • 端口数有限制,所以单机桥接网络只适合本地开发环境以及非常小的应用
  • 多机覆盖网络
    • 允许单个网络包含多个主机,不同主机上的容器间就可以在链路层实现通信
    • 只要在docker network create命令中加入-d overlay参数
  • 接入现有网络
    • 例如应用中已经容器化的部分需要与那些运行在物理网络和VLAN上未容器化部分进行通信
    • Docker内置的Macvlan驱动(windows中为Transparent
    • 通过为容器提供MAC和IP地址
    • 优点:Macvlan性能优异,因为无需端口映射或者额外桥接,可以直接通过主机接口(或者子接口)访问容器接口
    • 缺点:需要将主机网卡(NIC)设置为混杂模式,这在大部分公有云平台上是不允许的
  • 服务发现
    • 允许容器和Swarm服务通过名称相互定位,唯一的要求是需要位于同一个网络中
    • 底层实现利用Docker内置的DNS服务器,为每个容器提供DNS解析功能
    • 可以自定义DNS配置Linux上,自定义操作会在容器内部的/etc/resolve.conf中增加条目)
      • --dns:指定自定义的DNS列表
      • --dns-search:指定自定义查询时所使用的域名
  • Ingress网络
    • Swarm支持两种服务发布模式,均保证服务从集群外可以访问
      • Ingress模式(默认)
        • 保证集群内的任一节点都能访问该服务
        • 采用Service Mesh或者Swarm Mode Service Mesh的四层路由网络来实现
      • Host模式
        • 只能通过运行服务副本的节点来访问
        • --publish published=5000,target=80,mode=host
  • 基本命令
    • docker network ls
    • docker network inspect bridge:查看bridge网络的详细信息
    • docker port name:查看端口映射
    • docker network prune:删除Docker主机上全部未使用的网络
    • docker network rm:删除Docker主机上指定网络
    • docker network create

Docker覆盖网络

  • Swarm模式下构建及测试Docker覆盖网络
    • 构建Swarm,初始化管理节点,以及加入工作节点
    • 创建一个新的覆盖网络uber-netdocker network create -d overlay uber-net
      • 能连接Swarm所有主机
      • 包括了一个TLS加密控制层
      • 如果相对数据层加密,添加-o encrypted
      • 只有当运行中的容器连接到覆盖网络的时候,该网络才变为可用状态。这种延迟生效策略通过减少网络梳理,提升了网络的扩展性(这句看不太明白~)
    • 将服务连接到覆盖网络
      • docker service create --name test --network uber-net --replicas 2 ubuntu sleep infinity
      • 创建一个名为test的服务,连接到了uber-net这个覆盖网络,并且指定ubuntu镜像创建两个副本(容器),采用sleep命令保持容器运行
    • 测试覆盖网络
      • docker network inspect uber-net
      • docker container inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' your_container_id
      • 查看被分配给覆盖网络的Subnet(两个需要ping的容器上分别运行,得到容器IDIP地址
      • 安装ubuntu上的ping工具包:iputils-ping
      • 跟踪路由:traceroute
  • 工作原理
    • VXLAN入门
      • VXLAN隧道技术:创建虚拟二层覆盖网络
      • 优点:是一种封装技术,能使现存的路由器和网络架构看起来就像普通的IP/UDP包一样
      • 为了创建二层网络,VXLAN基于现有的三层IP网络创建了隧道。基础网络:三层之下的基础部分
      • VXLAN隧道两端都是VXLAN隧道终端(VXLAN Tunnel EndpointVTEPVTEP完成了封装和解压的步骤
    • 两个容器示例
      • 每台主机上都有一个Sandbox(网络命名空间):当前主机上独立的网络栈
        • Sandbox内部创建了一个名为Br0虚拟交换机(虚拟网桥),同时还创建了一个VTEPVTEP的一端接入到Br0,另一端接入主机网络栈
      • 在主机网络栈中的VTEP(终端)从主机所连的基础网络中获取到IP地址,并以UDP Socket的方式绑定到4789端口
      • 不同主机上的两个VTEP通过VXLAN隧道创建了一个覆盖网络
      • 每个容器都有自己的虚拟以太网(veth)适配器,并接入本地Br0虚拟交换机
    • 通信示例——node1上的C1到连接node2上的C2
      • C1发起ping请求目标IPC2的地址10.0.0.4(非实际网络ip)。
      • 请求的流量通过连接到Br0虚拟交换机veth接口发出
        • 虚拟交换机并不知道要将包发送到哪里,所以Br0将包发送到其上的全部端口
        • 连接到Br0VETP接口知道如何转发这个数据帧,所以将自己的MAC地址返回(代理ARP响应
        • Br0更新自己的ARP映射表,将10.0.0.4映射到本地VTEPMAC地址
      • Br0将目标为C2的流量转发到VTEP接口
        • VTEP知道C2,因为所有新启动的容器都会将自己的网络详情采用网络内置的Gossip协议发送给相同Swarm集群内的其他节点
      • VTEP完成数据帧的封装,也就是把VXLAN Header信息添加到以太网帧当中
        • VXLAN Header信息包含VXLAN网络IDVNID),其作用是记录VLAN(这个VLAN具体是啥?)到VXLAN的映射关系
        • 将数据帧放到UDP包中,并设置UDP目的IP字段node2节点的VTEP的IP地址,同时设置UDP Socket端口为4789
      • 当包到达node2,内核将包发给合适的VTEP,有VTEP读取VNID,解压包信息
      • 根据VNID将包发送到本地名为Br0并且连接到VLAN的交换机
      • 在交换机上,包被发送给C2
    • Docker支持使用同样的覆盖网络实现三层路由
      • 可以创建包含两个子网的覆盖网络,Docker会负责子网间的路由
      • docker network create --subnet=10.1.1.0/24 --subnet=11.1.1.0/24 -d overlay prod-net
      • 该命令会在sandbox中创建两个虚拟交换机,默认支持路由

卷与持久化数据

  • 非持久化数据
    • 自动创建,从属于容器,生命周期与容器相同。——容器擅长无状态和非持久化事务
    • 删除容器也会删除全部非持久化数据
  • 持久化数据
    • 将数据存储在卷上,删除一个关联卷的容器,卷并不会删除
    • 卷与容器是解耦的,从而可以独立的创建和管理卷,并且卷并未与任意容器生命周期绑定
  • 容器与持久化数据
    • 用户创建卷,然后创建容器,接着将卷挂载到容器上。卷会挂载载容器的某个目录之下,任何写到该目录下的内容都会写到卷中。即使容器被删除,卷与其上面的数据仍然存在
    • docker volumn create myvol:创建名为myvol的卷
      • 默认情况下,新卷采用内置的local驱动,本地卷只能被所在节点的容器使用
      • 第三方驱动可以通过插件的方式,为Docker集成外部存储系统
        • 块存储:相对性能更高,适用于对小块数据的随机访问负载
        • 文件存储:包括NFS和SMB协议的系统,在高性能场景下表现优异
        • 对象存储:适用于较大且长期存储的、很少变更的二进制数据存储。通常对象存储是根据内容寻址,并且性能较低
      • 也可以使用Dockfile部署卷,VOLUMN
    • docker container run -dit --name voltainer --mount source=bizvol,target=/vol alpine
      • 挂载了一个名为bizvol的卷,如果系统中没有这个卷,则Docker会自动创建一个卷
      • docker service create --name hellcat --mount source=bizvol,target=/vol alpine sleep 1d
    • 在集群节点间共享存储
      • Docker能够集成外部存储系统,使得集群间节点共享外部存储数据变得简单
      • 独立存储LUN或者NFS共享可以应用到多个Docker主机,无论容器或者服务副本运行在那个节点上,都可以共享该存储
      • 共享存储需要注意数据损坏(Data Corruption)
        • 也就是多个容器同时写入,或者某个容器写入到缓存中,还没更新到共享存储,另一个容器在这期间更新的文件,那么当将缓存中的内容更新到文件中时,另一个容器对此一无所知
  • 常用命令
    • docker volumn ls
    • docker volumn inspect myvol(卷名)
    • docker volumn prune:会删除未装入到某个容器或者服务的所有卷
    • docker volumn rm:删除指定卷(不能删除正在被容器或服务使用的卷)

使用Docker Stack部署应用

  • Stack能够在单个声明文件中定义复杂的多服务应用
  • 提供简单的方式来部署应用并管理其完整的生命周期
    • 初始化部署、健康检查、扩容、更新、回滚
  • Compose文件中定义应用,然后通过docker stack deploy命令完成部署和管理
  • Stack基于Docker Swarm之上来完成应用的部署
  • Docker适用于开发和测试,Docker Stack适用于大规模场景和生产
  • Stack部署文件(例如docker-stack.yml
    • versionCompose文件格式的版本号,Stack需要3.0或者更高版本
    • services:定义组成当前应用的服务
      • image:指定用于构建服务的镜像,与Docker Compose的一个区别是,Stack不支持构建,在部署之前,所有镜像必须提前构建完成
      • ports:定义端口映射
        • 默认采用Ingress模式,Swarm集群中的每个节点对应的端口都会映射并且是可访问的
        • 也可以手动设置为Host模式
      • secret:定义服务需要用到的密钥,密钥必须被顶级关键字secrets定义,并且必须在系统上已经存在
        • 密钥以普通文件的形式挂载到服务副本中
      • networks:确保服务所有副本连接到指定网络上(如果网络不存在,自动创建)
      • environment:允许在服务副本中注入环境变量
      • deploy:部署
        • placemnet:contraints: 约束
          • 节点ID,如node.id==o2p4kw2uuw2a(id)
          • 节点名称,如node.hostname==wrk-12
          • 节点角色,如node.role!=manager
          • 节点引擎标签,如engine.labels.operatingsystem==ubuntu16.04
          • 节点自定义标签,如node.labels.zone==prod1
        • 副本数量:replicas=2
        • update_config:定义Docker滚动升级的时候具体如何操作
          • parallelisem:2:每次更新两个副本
        • failure_action:升级失败后的策略
          • pause:(默认)阻止其他副本升级
          • continue:继续
          • rollback:自动回滚
        • restart-policy:定义Swarm针对容器异常退出的重启策略
          • condition:onfailure:某副本以非零返回值退出,立即重启该副本
          • max_attempts:最多重试次数
          • window:每次检测是否启动成功最长等待时间
          • delay:每次重启间隔时间
      • stop_grace_period:设置优雅停止(发送停止信号后,有一段时间处理后事)时间
      • volumns:挂在提前创建的卷或者主机目录到某个服务副本当中
        • /var/run/docker.sockDocker提供的IPC套接字Docker daemon通过该套接字对其他进程暴露其API终端,可以通过该文件管理Docker daemon
    • networks:列出必须的网络
      • 部署时,首先检查并创建networks:关键字对应的网络
      • 默认网络采用overlay驱动,控制层加密
      • 如果需要加密数字层(需要额外的开销)
        • docker network create 命令中指定 -o encrypted参数
        • Stack文件中的driver_opts之下指定 encrypted:'yes'
    • secrets:定义用到的密钥
      • 如果密钥被定义为external,则在Stack部署之前,这些密钥必须存在
    • 所有变更都应该通过Stack文件进行声明,然后通过docker stack deploy进行部署
    • 常用命令
      • docker stack deploy
      • docker stack ls
      • docker stack ps
      • docker stack rm

Docker安全

  • Linux安全技术
    • Namespace
      • 该技术将操作系统进行拆分,是一个操作系统看起来像多个互相独立的操作系统一样
      • 用于隔离不同容器
      • Docker容器是由各种命名空间组成的,Docker容器本质就是命名空间的有组织集合
        • 进程IDPID):为每个容器提供互相独立的容器进程树
        • 网络(NET):提供互相隔离的网络栈
        • 文件系统/挂载(MNT):每个容器都有互相隔离的根目录/
        • 进程内通信(IPC):在容器内提供共享内存,共享内存载不同容器间也是互相独立的
        • 用户(USER):将容器内用户映射到Linux主机的不同用户上(Docker新生事物,非必选项)
        • UTS:为每个容器提供自己的主机名称
    • Control Group
      • 控制组用于限额
      • CGroup允许用户设置限制,防止单个容器占用全部主机资源
    • Capability
      • 选择容器运行所需的root用户权限(只取容器需要的权限,限制不需要的权限)
      • Linux root用户常见能力
        • CAP_CHOWN:允许用户修改文件所有权
        • CAP_NET_BIND_SERVICE:允许用户将socket绑定到系统的端口号
        • CAP_SETUID:允许用户提升进程优先级
        • CAP_SYS_BOOT:允许用户重启系统
      • capability机制用来实现用户以root身份运行容器的同时,还能移除非必须的root能力
    • 强制访问控制(MAC
    • 安全计算(Seccomp
      • 使用过滤模式下的Secomp来限制容器对宿主机内核发起的系统调用
  • Docker平台安全技术
    • Swarm模式
      • 加密节点ID
      • 基于TLS的认证机制
        • 每个加入Swarm的节点都需要发布自己的客户端证书,证书用于双向认证
        • 证书定义节点相关信息,包括从属的Swarm集群以及该节点载集群中的身份、证书更新周期
        • 内容保存在docker内部的swarm-node.crt文件中
        • 使用docker system info查看信息
      • 安全准入令牌:向Swarm中加入管理者和工作者所需的唯一凭证(join-token
        • 管理者所需准入令牌
        • 工作者所需准入令牌
        • 令牌格式:PREFIX - VERSION - SWARM ID - TOKEN
          • PREFIX永远是“SWMTKN”,所以允许通过正则表达式匹配该令牌
          • VERSION展示了Swarm的版本信息
          • SWARM IDSwarm认证信息的一个哈希值
          • TOKEN内容决定该令牌是管理者还是工作者的准入令牌
        • 重设准入令牌:docker swarm join-token --rotate manager
      • 支持周期性证书自动更新的CA配置
        • 被配置为Swarm集群的第一个管理节点,也是根CA节点
        • docker swarm update --cert-expiry 720h:更改证书更新周期
        • docker swarm ca:可以用于管理CA配置--help查看帮助
      • 加密集群存储(配置DB
        • Swarm大脑,保存了集群配置和状态数据,默认加密
        • 目前基于etcd的某种实现,并且会在Swarm所有管理者之间自动复制
      • 加密网络
    • Docker安全扫描
      • Docker安全扫描对Docker镜像进行二进制代码级别的扫描,对其中的软件根据已知缺陷数据库(CVE数据库)进行检查
    • Docker内容信任机制
      • Docker Content TrustDCT允许开发者对发布到Docker Hub或者Docker可信服务的镜像进行签名
      • 当镜像被拉取时,会自动确认签名状态
      • DCT一旦开启,将无法获取并使用未经签名的镜像
    • Docker密钥
      • docker secret命令管理密钥
      • 密钥使用总体流程
        • 创建密钥,并发送到Swarm
        • 将密钥存放在集群存储当中,并且加密(管理节点可以访问集群存储)
        • B服务创建,并且使用该密钥
        • 密钥加密传输到B服务任务节点(容器)
        • B服务的容器将密钥解密并挂在到路径/run/secrets下,这是一个临时内存文件系统
        • 一旦容器(服务任务)完成,内存文件系统关闭,密钥也随之删除
        • A服务中的容器不能访问该密钥

企业版工具及特性

  • Docker EE企业版能够实现私有化部署
  • Docker EE引擎
  • Docker通用控制平面(UCP
    • 基于角色的权限控制(RBAC):UCP通过一中称为授权(Grant)的东西实现
      • 主体(Subject):一个或多个用户,或一个团队
      • 角色(Role):一系列权限的组合
      • 集合(Collection):权限作用的资源
      • 授权:配置谁,对那些资源,拥有什么样的权限
    • 集成活动目录
      • UCP能够与活动目录及其他LDAP目录服务进行集成,从而利用组织中现有的单点登录系统中的用户和组
  • Docker可信镜像仓库服务(DTR
    • 可配置为使用自签名证书或来自可信第三方CA证书
    • 可设置DTR来执行二进制级别的镜像扫描,检查镜像中的缺陷
    • 基于构建流水线来配置自动化的镜像提升策略
  • HTTP路由网格HRM

Docker面试题

  • 什么是虚拟化?
    • 虚拟化允许您在相同的硬件上运行两个完全不同的操作系统。每个客户操作系统都经历了引导,加载内核等所有过程。
    • 可以基于虚拟化方法如何模仿客户操作系统的硬件模拟客户操作环境来对虚拟化方法进行分类。主要有三种类型的虚拟化:
      • 仿真
      • 半虚拟化
      • 基于容器的虚拟化
    • 什么是Docker
      • Docker是一个容器化平台,它以容器的形式将您的应用程序及其所有依赖项打包在一起,以确保您的应用程序在开发,测试或生产的任何环境中无缝运行。
      • Docker容器,将一个软件包装在一个完整的文件系统中,该文件系统包含运行所需的一切:代码,运行时,系统工具,系统库等可以安装在服务器上的任何东西。
      • 这可以保证软件始终运行相同,无论其环境如何。
  • Docker与虚拟机有何不同?
    • Docker不是虚拟化方法。它依赖于实际实现基于容器的虚拟化或操作系统级虚拟化的其他工具。为此,Docker最初使用LXC驱动程序,然后移动到libcontainer现在重命名为runc
    • Docker主要专注于在应用程序容器内自动部署应用程序。应用程序容器旨在打包和运行单个服务,而系统容器则设计为运行多个进程,如虚拟机。因此,Docker被视为容器化系统上的容器管理或应用程序部署工具。
    • 与虚拟机不同,容器不需要引导操作系统内核,因此可以在不到一秒的时间内创建容器。
    • 由于基于容器的虚拟化为主机增加了很少或没有开销,因此基于容器的虚拟化具有接近本机的性能
    • 主机上的所有容器共享主机的调度程序,从而节省了额外资源的需求。
    • 与虚拟机映像相比,容器状态(DockerLXC映像)的大小很小,因此容器映像很容易分发
    • 容器中的资源管理是通过cgroup实现的。CGroups不允许容器消耗比分配给它们更多的资源。虽然主机的所有资源都在虚拟机中可见,但无法使用。
  • 容器内部机制?
    • 大约在2006年,包括Google的一些员工在内的人们实现了名为命名空间的新的Linux内核级功能(不过这个想法早在FreeBSD中就已存在)。操作系统的一个功能是允许将全局资源(如网络和磁盘)共享到进程。如果将这些全局资源包装在命名空间中,以使它们仅对在同一命名空间中运行的那些进程可见,该怎么办?比如说,你可以获得一大块磁盘并将其放在命名空间X中,然后在命名空间Y中运行的进程无法查看或访问它。类似地,名称空间X中的进程无法访问分配给名称空间Y的内存中的任何内容。当然,X中的进程无法查看或与名称空间Y中的进程通信。这为全局资源提供了一种虚拟化和隔离
    • 这就是Docker的工作原理:每个容器都在自己的命名空间中运行,但使用与所有其他容器完全相同的内核。发生隔离是因为内核知道分配给进程的命名空间,并且在API调用期间确保进程只能访问其自己的命名空间中的资源。
  • CI(持续集成)服务器的功能是什么?
    • CI服务器功能是不断地集成所有正在进行的更改并由不同的开发人员提交到存储库,并检查编译错误。它需要每天多次构建代码,最好是在每次提交之后,以便它可以检测在问题发生时是哪个提交Bug了。
  • 如何使用Docker构建与环境无关的系统?
    • 有三个主要功能有助于实现这一目标:
      • Volumes
      • 环境变量注入
      • 只读文件系统
  • 什么是Docker镜像
    • Docker镜像用于创建容器。使用build命令创建映像,并且在使用run启动时它们将生成容器。
    • 镜像存储在Docker注册表``http://registry.hub.docker.com中,因为它们可能变得非常大,镜像被设计为由其他镜像层组成,允许在通过网络传输镜像时发送最少量的数据。
  • 什么是Docker容器
    • Docker容器包括应用程序及其所有依赖项,但与其他容器共享内核,作为主机操作系统上用户空间中的独立进程运行。Docker容器不依赖于任何特定的基础架构:它们可以在任何计算机,任何基础架构和任何云中运行。
  • 什么是Docker Hub
    • Docker hub是一个基于云的注册表服务,允许您链接到代码存储库,构建镜像并测试它们,存储手动推送的镜像以及指向Docker云的链接,以便您可以将镜像部署到主机。它为整个开发流程中的容器镜像发现,分发和变更管理,用户和团队协作以及工作流自动化提供了集中资源。
  • Docker容器有几种状态?
    • Docker容器可以有四种状态:
      • 运行
      • 已暂停
      • 重新启动
      • 已退出
    • 我们可以通过运行命令来识别Docker容器的状态:docker ps –a
      • 这将依次列出所有可用的docker容器及其在主机上的相应状态。从那里我们可以很容易地识别感兴趣的容器,以相应地检查其状态。
  • Docker适合什么类型的应用程序 - 无状态有状态更适合Docker容器?​​​​​​​
    • 最好为Docker Container创建无状态应用程序。我们可以从应用程序中创建一个容器,并从应用程序中取出可配置的状态参数。现在我们可以在生产和具有不同参数的QA环境中运行相同的容器。这有助于在不同场景中重用相同的image。使用Docker Containers比使用有状态应用程序更容易扩展无状态应用程序。
  • 解释基本的Docker使用流程
    • 一切都从Dockerfile开始。Dockerfile是镜像的源代码(这解释合理吗?我觉得更像是Makefile或者CMake脚本)。
    • 创建Dockerfile后,您可以构建它以创建容器的镜像。镜像只是“源代码”的“编译版本”,即Dockerfile
    • 获得容器的镜像后,应使用注册表重新分发容器。注册表就像一个git存储库 - 你可以推送和拉取镜像。
    • 接下来,您可以使用该镜像来运行容器。在许多方面,正在运行的容器与虚拟机(但没有管理程序)非常相似。
  • Dockerfile中最常见的指令是什么?​​​​​​​
    • Dockerfile中的一些常用指令如下:
      • FROM:我们使用FROM为后续指令设置基本镜像。在每个有效的Dockerfile中,FROM是第一条指令。
      • LABEL:我们使用LABEL按照项目,模块,许可等组织我们的镜像。我们也可以使用LABEL来帮助实现自动化。在LABEL中,我们指定一个键值对,以后可用于以编程方式处理Dockerfile
      • RUN:我们使用RUN命令在当前镜像之上的新图层中执行任何指令。使用每个RUN命令,我们在镜像顶部添加一些内容,并在Dockerfile的后续步骤中使用它。
      • CMD:我们使用CMD命令提供执行容器的默认值。在Dockerfile中,如果我们包含多个CMD命令,则只使用最后一条指令。
  • Dockerfile中的命令COPYADD命令有什么区别?
    • 一般而言,虽然ADD并且COPY在功能上类似,但是COPY是优选的。
    • 那是因为它比ADD更透明。COPY仅支持将本地文件基本复制到容器中,而ADD具有一些功能(如仅限本地的tar提取远程URL支持),这些功能并不是很明显。因此,ADD的最佳用途是将本地tar文件自动提取到镜像中,如ADD rootfs.tar.xz /中所示。
  • 解释一下dockerfileONBUILD指令?
    • 当镜像用作另一个镜像构建的基础时,ONBUILD指令向镜像添加将在稍后执行的触发指令。如果要构建将用作构建其他镜像的基础的镜像(例如,可以使用特定于用户的配置自定义的应用程序构建环境或守护程序),这将非常有用。
  • Docker镜像和层有什么区别?
    • 我觉得应该是:镜像是一个配置信息,内部是一个使用的层的列表以及一些元信息,而层存储实际的内容

    • 镜像:Docker镜像是由一系列只读层构建的

      • 层:每个层代表镜像Dockerfile中的一条指令(不对吧,有些指令不会创建层,只会加入元信息)。
    • 下面的Dockerfile包含四个命令,每个命令都创建一个层。

      FROM ubuntu:15.04
      COPY . /app
      RUN make /app
      CMD python /app/app.py
      
    • 重要的是,每个层只是与之前一层的一组差异层(相同的就不再放到新层中)。

  • 什么是Docker Swarm
    • Docker SwarmDocker的本机群集。它将Docker主机池转变为单个虚拟Docker主机。Docker Swarm提供标准的Docker API,任何已经与Docker守护进程通信的工具都可以使用Swarm透明地扩展到多个主机。
  • 如何在生产中监控Docker
    • Docker提供docker statsdocker事件等工具来监控生产中的Docker。我们可以使用这些命令获取重要统计数据的报告。
    • Docker统计数据:当我们使用容器ID调用docker stats时,我们获得容器的CPU,内存使用情况等。它类似于Linux中的top命令。
    • Docker事件Docker事件是一个命令,用于查看Docker守护程序中正在进行的活动流。
      • 一些常见的Docker事件是:attachcommitdiedetachrenamedestroy等。我们还可以使用各种选项来限制或过滤我们感兴趣的事件。
  • Docker如何在非Linux系统中运行容器?
    • 通过添加到Linux内核版本2.6.24的名称空间功能,可以实现容器的概念。容器将其ID添加到每个进程,并向每个系统调用添加新的访问控制检查。它由clone()系统调用访问,该调用允许创建先前全局命名空间的单独实例。
    • 如果由于Linux内核中可用的功能而可以使用容器,那么显而易见的问题是非Linux系统如何运行容器。Docker for MacWindows都使用Linux VM来运行容器(window好像默认不是linux了吧)。Docker Toolbox用于在Virtual Box VM中运行容器。但是,最新的DockerWindows中使用Hyper-V,在Mac中使用Hypervisor.framework
  • 如何在多个环境中使用Docker
    • 可以进行以下更改:
    • 删除应用程序代码的任何卷绑定,以便代码保留在容器内,不能从外部更改
    • 绑定到主机上的不同端口
    • 以不同方式设置环境变量(例如,减少日志记录的详细程度,或启用电子邮件发送)
    • 指定重启策略(例如,重启:始终)以避免停​​机
    • 添加额外服务(例如,日志聚合器)
    • 因此,您可能希望定义一个额外的Compose文件,例如production.yml,它指定适合生产的配置。此配置文件只需要包含您要从原始Compose文件中进行的更改。
  • 为什么Docker Compose不会等待容器准备就绪,然后继续以依赖顺序启动下一个服务?
    • Compose按照依赖顺服启动和停止容器,决定依赖关系语句有 depends_on, links, volumes_from, 和network_mode: "service:...".
    • 但是,对于启动,Compose不会等到容器“准备好它运行“。这里有一个很好的理由:
      • 等待数据库(例如)准备就绪的问题实际上只是分布式系统更大问题的一个子集。在生产中,您的数据库可能随时变得不可用或移动主机。您的应用程序需要能够适应这些类型的故障。
      • 要处理此问题,请将应用程序设计为在发生故障后尝试重新建立与数据库的连接。如果应用程序重试连接,它最终可以连接到数据库。
      • 最佳解决方案是在启动时以及出于任何原因丢失连接时,在应用程序代码中执行此检查

Reference

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352