【注】所有图片资源来源于网络

 

本教程总共分6部分,内容如下:

  1. 基本概念与软件安装
  2. 应用的构建、测试、交付、运行流程
  3. 将应用转为规模可缩放的服务
  4. 让服务横跨多台机器
  5. 数据持久化方式
  6. 生产环境的集群部署
  7. 构建私有镜像仓库registry
  8. 底层技术原理与总结

应用代码只是很简单的DEMO,你可以把注意力放到docker的使用流程上,比如它是如何构建、交付、运行应用的。

另外从开发角度讲,系统的开发实现了从手工操作到全流程自动化的转变;从运维角度讲,容器技术及其相关工具也极大地解放了运维人员。

一. 基本概念与软件安装

1.1基本概念认识

Docker是种分布式容器技术,要说容器得先说镜像。“镜像”是轻量的、独立的、可执行的文件包,它含有运行软件的所有东西,包括代码、运行时环境、环境变量、配置文件——也就是说,镜像是面向业务的概念,是个业务系统的完全体。而Docker的基本目标也就是将环境相关的业务系统抽象化,就像java虚拟机对java语言抽象、统一了不同的硬件平台。抽象之后,从交付角度看,系统的交付也就从程序交付变成了镜像交付,镜像里不仅有程序代码,还有语言环境、配置文件、第三方库等,其本质也就是统一了开发环境和生产环境
“容器”是镜像的运行时实例,容器默认是跟宿主环境隔离的,除非你在配置中指定要访问宿主文件和端口等资源。容器在宿主机内核之上原生执行应用,相比虚拟机运行应用来说具有很好的性能。虚拟机是通过虚拟机操作系统经过中间虚拟化层来实现对宿主机资源的虚拟访问,而容器是在单独的进程中实现原生访问的,所以不会有额外的内存消耗。如下图所示

docker容器中多线程起作用吗_命名空间

 

1.2 安装

基本流程也就是下载软件然后安装,但是Docker的工作原理产生了对环境的特殊要求——比如操作系统的版本centos 要7.x, macOS要10.x,windows要10.x

mac安装    centos安装    ubuntu安装    windows安装

二. 应用的构建、测试、运行、交付流程

docker本身的使用流程如下:

docker容器中多线程起作用吗_运维_02

那么应用容器技术后的整个软件工程的流程基本都会有相应变化。

docker容器中多线程起作用吗_操作系统_03

 

三、将应用转为可伸缩的服务

所谓服务是对应用进行业务划分得出的概念,同“微服务架构”中的“服务”含义,其目标是为整个系统提供某种特定的功能,如账号中心、api网关、订单处理、网站首页等。在docker中是通过docker-compose.yml文件来配置服务的,如下所示:


version: "3"
services:
  web:#网站服务
    # replace username/repo:tag with your name and image details
    image: localhost:5000/myname/testapp:latest#指定服务所用镜像
    deploy: #部署相关
      replicas: 5 #副本数目
      resources: 
        limits:#资源限制
          cpus: "0.1"#CPU最大占用
          memory: 50M#内存最大占用
      restart_policy:#重启策略
        condition: on-failure
    ports:#映射端口
      - "80:80"
    networks:#网络类型
      - webnet
  visualizer:#可视化服务
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]
    networks:
      - webnet
networks:
  webnet:


由上面配置文件可知,一个服务只能运行一个镜像,并要设置资源限制、重启策略、网络类型等。启动服务命令为:


docker stack deploy -c docker-compose.yml $YOUR_APP_NAME docker service ls #查看已启动的service


 

上面指令中的 "stack" 是什么概念呢?docker官方说明是

A stack is a group of interrelated services that share dependencies, and can be orchestrated and scaled together. A single stack is capable of defining and coordinating the functionality of an entire application (though very complex applications may want to use multiple stacks).

也就是说服务栈(stack)是多个相关服务的群组,这些服务共享依赖、同时伸缩——从实现上来讲,定义在同一个docker-compose.yml文件中的服务就组成一个服务栈(stack)。而一个服务栈就定义了一个完整应用的功能,不过非常复杂的应用可能会用多个服务栈。

那应用部署后怎么伸缩呢?很简单,调整docker-compose.yml中的副本数配置项replicas,然后重部署(redeploy)就好了,


docker stack deploy -c docker-compose.yml $YOUR_APP_NAME


 可见确实很简单,只要保证 YOUR_APP_NAME 跟之前写的一致,再deploy一次即可。

四、让服务横跨多台机器

 

docker_gwbridge:

This is the network created by Docker. It allows the containers to connect to the host that it is running on.

ingress:

This is the network created by Docker. Docker swarm uses this network to expose services to the external network and provide the routing mesh.

五、数据持久化方式

因为容器是即生即死的——关闭一个容器后其中的状态、数据不会保存。所以要实现数据持久化需要专门的方法。在docker中容器中数据持久化是通过与宿主机的数据共享来实现的,这个机制叫做“Volume"——数据卷。

数据卷是经过特殊设计的目录,可以绕过联合文件系统(UFS),为一个或多个容器提供访问。数据卷的设计目的,在于数据的持久化,它完全独立于容器的生命周期,因此,docker不会在删除容器时删除其挂载的数据卷,也不会存在任何垃圾收集机制对容器引用的数据卷进行“垃圾回收”。

上面说数据卷可以为多个容器提供访问,也就是容器之间的数据访问,其实是通过数据卷的相关“衍生”概念“数据卷容器”来实现的。数据卷容器类似于一个中介平台,也就是说数据卷容器与宿主机共享数据,而其他容器则通过--volumes-from来设置锚定数据卷容器来实现自身与宿主机、自身与兄弟容器间的数据共享。其概念关系如图:

docker容器中多线程起作用吗_docker容器中多线程起作用吗_04

六、生产环境的集群部署

 

七、构建私有registry镜像 

 

八、底层技术原理与总结

docker是运用linux内核的几个特性来实现功能的,其中包括命名空间NameSpace、控制组Control Groups、联合文件系统Union FileSystem,并通过对它们的封装来实现容器格式Container Format。namespace实现资源隔离,CGroups实现访问控制,UFS实现文件组织。

8.1 命名空间

在虚拟化的系统中,一台物理计算机可以运行多个内核,可能是并行的多个不同的操作系统。而命名空间则只使用一个内核在一台物理计算机上运作,所有全局资源都通过命名空间抽象起来。这使得可以将一组进程放置到容器中,各个容器彼此隔离。类似于绝对标识与相对标识的关系。linux内核提供以下几个命名空间:

  •  pid namespace: 进程隔离 (PID: Process ID).
  •  net namespace: 管理网络接口-网络设备接口,IPv4和IPv6协议栈,IP路由表,防火墙规则,sockets等等 (NET: Networking).
  •  ipc namespace: 管理进程间通信资源的访问 (IPC: InterProcess Communication).
  •  mnt namespace: 管理文件系统的挂载点 (MNT: Mount).
  •  uts namespace: 隔离内核与版本标识 (UTS: Unix Timesharing System).

以pid命名空间举例来说,

docker容器中多线程起作用吗_docker容器中多线程起作用吗_05

在上图有四个命名空间,一个父命名空间衍生了两个子命名空间,其中的一个子命名空间又衍生了一个子命名空间。由于各个命名空间彼此隔离,所以每个命名空间都可以有 PID 号为 1 的进程;但又由于命名空间的层次性,父命名空间是知道子命名空间的存在,因此子命名空间要映射到父命名空间中去,因此上图中 level 1 中两个子命名空间的六个进程分别映射到其父命名空间的PID 号5~10。

命名空间是怎么实现的呢?新的命名空间可以用下面两种方法创建。

(1) 在用fork或clone系统调用创建新进程时,有特定的选项可以控制是与父进程共享命名空间,还是建立新的命名空间。

(2) unshare系统调用将进程的某些部分从父进程分离,其中也包括命名空间

linux 中clone函数的原型为:int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

其中flags标识符有以下几个:

标志                   含义

 CLONE_PARENT  创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”

 CLONE_FS          子进程与父进程共享相同的文件系统,包括root、当前目录、umask

 CLONE_FILES     子进程与父进程共享相同的文件描述符(file descriptor)表

 CLONE_NEWNS  在新的namespace启动子进程,namespace描述了进程的文件hierarchy

 CLONE_SIGHAND  子进程与父进程共享相同的信号处理(signal handler)表

 CLONE_PTRACE  若父进程被trace,子进程也被trace

 CLONE_VFORK    父进程被挂起,直至子进程释放虚拟内存资源

 CLONE_VM          子进程与父进程运行于相同的内存空间

 CLONE_PID         子进程在创建时PID与父进程一致

 CLONE_THREAD   Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群

其中这些flag标识可以同时使用,docker就是使用这些标识来实现。

8.2 cgroups

cgroup限制应用可访问的资源,docker中还用它对容器共享、限制CPU核数、最大内存等硬件资源。

8.3 UFS

联合文件系统是通过创建文件层来操作的文件系统,docker就是用UFS来提供容器的构建块(building blocks )以构建容器。docker支持多个UFS实现,如AUFS, btrfs, vfs, 和 DeviceMapper


8.4总结

容器技术的使用概括如下:

软件环境可以打包为镜像,镜像运行为容器,服务使镜像可伸缩地运行为多个容器副本,每个服务可以以集群模式(swarm mode)分布式部署并支持负载均衡,服务栈(stack)管理多个服务。