60分钟

第4章 容器技术

【学习目标】

知识目标

掌握容器的基本概念。

掌握容器和虚拟机的区别。

了解Docker的基础架构。

了解Kubernetes集群和编排工具。

技能目标

安装Docker引擎。

使用Docker镜像和容器。

搭建私有仓库服务。

安装集群管理工具。

配置Kubernetes集群。

使用Kubernetes部署Web项目。

【认证考点】

掌握容器的基本概念。

理解Docker的基础架构。

掌握Docker的安装与使用。

搭建私有仓库服务。

安装和配置集群。

使用Kubernetes集群部署项目。

项目引导:企业自建容器平台

【项目描述】

某企业拟自建跨主机集群的自动部署、扩展以及运行应用程序容器的平台,以实现将应用拆分成多个微服务。该平台的构建拟采用开源的应用容器引擎Docker,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows机器上,也可以实现虚拟化。采用Kubernetes构建容器集群,让部署容器化的应用简单并且高效。Kubernetes凭借着其优良的架构,灵活的扩展能力,丰富的应用编排模型,成为了容器编排领域的事实标准。越来越多的企业拥抱这一趋势,选择Kubernetes作为容器化应用的基础设施,逐渐将自己的核心服务迁移到Kubernetes之上。

知识储备

4.1 容器技术概述

容器(Container)从根本上改变了人们开发、发布以及运行软件的方式。软件开发者可以在本地构建软件,因为他们知道软件能够在任何主机环境下运行,无论是IT部门的机房、用户的笔记本电脑,还是云端集群,而且运行时并无差异。而运维工程师只需专注于维护网络和资源,保证服务的正常运行时间,从而减少了花在配置环境及解决系统依赖关系上的时间。

4.1.1 容器技术简介

容器是对应用程序及其依赖关系的封装。容器能与主机的操作系统共享资源,它的启动和停止均只需一瞬间。相比在主机上直接运行程序,容器的性能损耗非常低,甚至是零损耗,其具有以下特点。

(1)容器能与主机的操作系统共享资源,因此它运行效率较高。

(2)容器具有可移植性。这可以解决由于运行环境的些许改变而导致的问题。

(3)容器是轻量的。这意味着开发者能同时运行数十个容器,并能模拟分布式系统在真实运行环境下的情况。

(4)对于最终用户及开发者而言,容器的优势不仅仅体现在云端部署。用户可以下载并执行复杂的应用程序,而无需花费大量时间在配置和安装的问题上,也无需担心对系统本身的改动。另一方面,应用程序的开发者不用再操心用户环境的差异,以及依赖关系是否满足。

虚拟机与容器都可以把主机上的应用程序隔离开来。但容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统的虚拟化则是在硬件的基础上,创建虚拟机,再在系统上部署相关的App应用。

如图4-1-1所示展示了传统虚拟化方案中应用程序在虚拟机中运行的情况,每个虚拟化应用不仅包含了应用程序、基本的二进制文件和库,还包含了操作系统。

图4-1-1 传统虚拟化方案

如图4-1-2所示展示了容器虚拟化方案中应用程序在容器中运行的情况,与在虚拟机中运行不同,主机的内核与容器共享,这意味着容器只能运行与主机一样的内核。应用程序使用相同的程序库,该程序库可以被不同的应用程序同时使用,只需一份,不用复制。容器引擎(Container Engine)负责启动及停止容器,与虚拟机管理程序差不多。但是,容器中的进程与主机自身的进程是等价的,因此没有类似虚拟机管理程序执行所带来的损耗。

图4-1-2 容器虚拟化方案

4.1.2 Docker的基础架构

Docker利用现有的Linux容器技术,主要通过提供可移植的镜像,以及一个用户友好的接口来创建一套完整的容器创建及发布方案。Docker重新定义了应用程序的开发、测试、交付和部署的过程,提出了“构建一次,到处运行(Build once,Run anywhere)”的理念。

Docker是由Docker Inc.公司于2013年推出的构建在Linux容器(Linux Container,LXC)技术上的应用容器引擎,是一个基于Go语言实现的并遵从Apache2.0协议的开源项目。LXC是一种内核虚拟化技术,提供了轻量级的虚拟化来隔离进程和资源,而不需要去虚拟整个操作系统。

1. 镜像、容器和仓库的基本概念

Docker包括三个基本概念,分别是镜像(Image)、容器(Container)和仓库(Repository)。

(1) 镜像(Image)

Docker镜像是用于创建Docker容器的模板。它是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。

(2) 容器(Container)

容器是独立运行的一个或一组应用,是镜像运行时的实体。容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。

(3) 仓库(Repository)

镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,就需要一个集中的存储、分发镜像的服务,Docker公开服务(Registry)就是这样的服务。Docker Registry公开服务是开放给用户使用、允许用户管理镜像的Registry服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。最常使用的Registry公开服务是官方的Docker Hub,这也是默认的Registry,并拥有大量的高质量的官方镜像。还有Red Hat的Quay.io;Google的Google Container Registry,Kubernetes的镜像使用的就是这个服务。在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对Docker Hub的镜像服务(Registry Mirror),这些镜像服务被称为加速器。除了使用公开服务外,用户还可以在本地搭建私有Docker Registry。Docker官方提供了Docker Registry镜像,可以直接使用做为私有Registry服务。

一个Docker Registry中可以包含多个仓库(Repository),每个仓库可以包含多个标签(Tag)每个标签对应一个镜像。仓库可看成一个代码控制中心,用来保存镜像。一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过“<仓库名>:<标签>”的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以latest作为默认标签。每个仓库可以包含多个标签(Tag),每个标签对应一个镜像。

也就是说,镜像用来创建Docker容器的,它可以包含一个完整的操作系统环境和用户需要的其他应用程序环境。一个镜像可以创建多个容器。

2. Docker的基础架构

Docker采用典型的客户端/服务器(Client/Server,C/S)架构,使用远程的Client来管理容器。Docker平台其拥有两个不同部分:负责创建与运行容器的Docker引擎,以及用来发布容器的注册服务器(Registry)。

(1)Docker Client通过相应的接口与Docker守护进程(Docker Daemon)进行通信,从而实现容器的构建、运行和发布。Docker Client用来运行docker命令。

(2)Docker主机包含多个容器和镜像文件。Docker主机上运行的Docker Daemon负责容器的创建、运行和监控,负责镜像的构建和存储,Docker守护进程的启动命令一般由主机操作系统负责执行。

(3)Registry是来管理镜像仓库的,仓库包含镜像,默认的Registry是Docker Hub。Docker Hub提供大量的公共容器镜像以供下载,方便用户快速上手,并且避免重复劳动。

(4)Docker还进一步开发了更多的工具,如集群管理工具Swarm、用于处理容器的图形用户界面Kitematic,以及部署Docker主机的命令行工具Machine等。

4.1.3 Docker的安装与使用

Docker分为社区版(CE)和企业版(EE)两大版本。官方网站上有各种环境下的安装指南,这里主要介绍Docker CE在CentOS上的安装方式。

1. Docker的安装

在测试或开发环境中Docker官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS系统上可以使用这套脚本安装,另外可以通过--mirror选项使用国内源进行安装,具体的安装命令如下。

curl -fsSL get.docker.com -o get-docker.sh

2. 镜像的使用

Docker运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker会从镜像仓库下载该镜像。

(1)Docker Hub上有大量的高质量的镜像可以用,从Docker镜像仓库获取镜像的命令是docker pull,其命令格式如下。

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

其中,这里的仓库名是两段式名称,即<用户名>/<软件名>。对于Docker Hub,如果不给出用户名,则默认为library,也就是官方镜像名。

(2)查看已经下载下来的镜像,可以使用docker images命令,具体的命令格式如下。

docker images

(3)删除本地的镜像,可以使用docker image rm命令,具体的命令格式如下。

docker image rm [选项] <镜像1> [<镜像2> ...]

(4)使用docker save命令可将指定镜像保存成归档文件,具体的命令格式如下。

docker save -o 镜像归档文件名 镜像名

(5)使用docker load可将镜像归档文件导入为镜像,具体的命令格式如下。

docker load -i 镜像归档文件名

3. 容器的使用

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。因为Docker容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。

(1)新建容器并启动,具体命令格式如下。

docker run -it 镜像名 /bin/bash

其中参数-it中表示交互式终端操作。/bin/bash放在镜像名后的是命令,这里是希望有个交互式Shell,因此用的是/bin/bash。

(2)使用docker start启动一个已停止的容器,具体命令格式如下。

docker start 容器ID 

其中,容器ID可以通过docker ps –a命令查看。

(3)使用docker stop停止一个容器,具体的命令格式如下。

docker stop 容器ID

(4)删除容器使用docker rm命令,具体的命令格式如下。

docker rm –f 容器ID

4. 仓库管理

目前Docker官方维护了一个公共仓库Docker Hub。大部分需求都可以通过在Docker Hub中直接下载镜像来实现。

(1)使用Docker Hub需要在官方网站注册一个账号。登录需要输入用户名和密码,登录成功后可以从docker hub上拉取自己账号下的全部镜像。登录docker hub具体的命令格式如下。

docker login

(2)退出登录的具体命令格式如下。

docker logout

(3)从镜像仓库拉取镜像文件,具体命令格式如下。

docker pull 镜像名

(4)用户登录后,通过docker tag命令标记本地镜像,再使用docker push命令将自己的镜像推送到Docker Hub,具体的命令格式如下。

docker tag 镜像名 username/镜像名
docker push username/镜像名

其中username为Docker hub用户名。

(5)docker save命令将一个或多个镜像归档,例如归档镜像httpd并输出到文件httpd.tar,具体命令如下。

docker save -o httpd httpd.tar

其中选项通常为-o,表示输出到文件。

(6)docker load命令加载一个镜像归档文件,例如加载一个使用docker 命令导出的归档文件httpd.tar。具体命令如下。

docker load –i httpd.tar

4.1.4 Kubnetes集群和编排工具介绍

容器是打包和运行应用程序的好方式。生产环境中,运维人员需要管理运行应用程序的容器,并确保不会停机。例如,如果一个容器发生故障,则需要启动另一个容器。Kubernetes则可以提供一个可弹性运行分布式系统的框架,满足扩展要求、故障转移、部署模式等。Kubernetes是一个可移植的,可扩展的开源平台,用于管理容器化的工作负载和服务,方便了声明式配置和自动化,它可以实现以下功能。

(1)服务发现和负载均衡。Kubernetes可以使用DNS名称或自己的IP地址公开容器,如果到容器的流量很大,Kubernetes可以负载均衡并分配网络流量,从而使部署稳定。

(2)存储编排。Kubernetes允许自动挂载存储系统,例如本地存储系统、公共云存储系统等。

(3)自动部署和回滚。可以使用Kubernetes描述已部署容器的所需状态,它可以以受控的速率将实际状态更改为所需状态。

(4)自动二进制打包。Kubernetes允许指定每个容器所需CPU和内存(RAM)。当容器指定了资源请求时,Kubernetes可以做出更好的决策来管理容器的资源。

(5)自我修复。Kubernetes可以重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器,并且在准备好服务之前不将其通告给客户端。

(6)密钥与配置管理。Kubernetes允许存储和管理敏感信息,可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。

1. Kubernetes的基本概念

Kubernetes比较坚持自己的一套设计理念,它对容器的组成和联网方式有强制要求。接下来是一些需要清楚的几个基本概念。

(1)微服务(Pod)。Kubernetes有很多技术概念,其中最重要的也是最基础的是Pod。Pod是在Kubernetes集群中运行部署应用或服务的最小单元,它是可以支持多容器的。Pod的设计理念是支持多个容器在一个Pod中共享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。一个Pod也可以包含0个或者多个磁盘卷组(volumes),这些卷组将会以目录的形式提供给一个容器,或者被所有Pod中的容器共享。

(2)复制控制器(Replication Controller,RC)。RC是Kubernetes集群中最早的保证Pod高可用的应用程序接口对象。通过监控运行中的Pod来保证集群中运行指定数目的Pod副本。指定的数目可以是多个也可以是1个;少于指定数目,RC就会启动运行新的Pod副本;多于指定数目,RC就会杀死多余的Pod副本。

(3)节点(Node)。Kubernetes集群中的计算能力由Node提供,最初Node称为服务节点(Minion),后来改名为Node。Kubernetes集群中的Node是所有Pod运行所在的工作主机,可以是物理机也可以是虚拟机。

(4)扁平的网络空间。Kubernetes的联网方式与默认的Docker网桥联网相比有着明显的不同。在默认的Docker联网中,容器的网络是私有的子网,不能与其他主机上的容器直接通信,除非通过端口转发或代理。在Kubernetes中,一个Pod内的容器共同使用一个IP地址,但对于所有Pod,它们的地址空间是“扁平”的,也就是说,所有Pod无需任何网络地址转换就能互相通信。这使得多主机集群更易于管理,而代价是Docker连接无法使用,并且单个Pod的联网也变得复杂。

(5)Kubernetes服务(Service)是一个定义了一组Pod的策略的抽象。

2. Kubernetes集群架构

Kubernetes节点有运行应用容器必备的服务,而这些都是受一个主节点(Master)的控制。每个节点上都要运行Docker,Docker来负责所有具体的映像下载和容器运行。Kubernetes主要由以下几个核心组件组成。

(1)Etcd。Etcd用于存储Kubernetes集群的网络配置和对象的状态信息,是整个集群的数据中心。

(2)API Server。API Server提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制。

(3)Controller Manager。Controller Manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等。

(4)Scheduler。Scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上。

(5)Kubelet。Kubelet负责维护容器的生命周期,同时也负责卷(Volume)和网络的管理。

(6)Container Runtime。Container负责镜像管理以及Pod和容器的真正运行。

(7)Kube-proxy。Kube-proxy负责为Service提供集群内部的服务发现和负载均衡。

项目实施

构建集群的第一步是将拥有的服务器按节点功能进行划分,如表4-1所示展示了该环境下的节点规划情况。本项目使用三台主机构建Kubernetes集群,其中一台作为Master,其余两台作为Node。

表4- 1 节点规划情况

需要完成的任务:

Kubernetes集群部署。

4.2 任务:Kubernetes集群部署

本任务采用Kubeadm工具部署Kubernetes集群,Kubeadm是一个由Kubernetes官方所提供的专门部署集群的管理工具。每一个节点主机上都要安装并运行Docker,同时也都要安装并运行Kubelet。Docker是运行容器的引擎,Kubelet是负责能运行Pod化容器的核心组件。使用Kubeadm将第一台主机初始化成Master,即通过Kubeadm工具将APIServer、Etcd、Controller-Manager、Scheduler各组件运行为Pod。

4.2.1 安装Docker引擎

每个主机上都要安装并运行Docker。本书案例使用CentOS 7系统,将从Docker官方网站下载的安装包及依赖包上传到每台主机的/tmp/docker-packages路径下并进行安装。使用root账户登录到主机。

(1)设置3台服务器的主机名分别为master、node1和node2,具体的命令如下。

hostnamectl set-hostname master
hostnamectl set-hostname node1
hostnamectl set-hostname node2

(2)在所有节点/etc/hosts中添加解析,将这三台服务器的IP地址与主机名之间的映射关系写入每台服务器的/etc/hosts文件,编辑该文件,具体命令如下。

vi /etc/hosts

打开文件后,输入i进入编辑模式,添加以下内容,然后输入:wq!保存退出。

192.168.209.128 master
192.168.209.129 node1
192.168.209.130 node2

(3)检查系统内核版本,具体命令如下。

$ uname –r 

uname -r命令执行结果如图4-2-1所示。

图4-2-1 uname -r命令执行结果

(4)检查系统架构类型,具体命令如下。

$ uname -m

uname –m命令执行结果如图4-2-2所示。

图4-2- 2 uname -m命令执行结果

(5)刚开始使用Docker时,建议以宽容(Permissive)模式运行SELinux,这样SELinux将只把错误写进日志,而非强制执行。如果以强制(Enforcing)模式运行SELinux,那么很有可能在执行书中的范例时,会遇到各种莫名其妙的“权限不足”(Permission Denied)错误。将SELinux设置为Permissive,具体命令如下。

$ setenforce 0

(6)关闭防火墙,具体命令如下。

$ systemctl stop firewalld

(7)设置防火墙开机不自启动,具体命令如下。

$ systemctl disable firewalld

(8)进入/tmp/docker-packages/yilai路径,具体命令如下。

$ cd /tmp/docker-packages/yilai

(9)安装Docker依赖安装文件,具体安装的命令、文件列表及进度如下所示。

$ rpm -Uvh *.rpm
               1:policycoreutils-2.5-34.el7     ################## [ 9%]
  2:setools-libs-3.3.8-4.el7      ################## [ 18%]
  3:python-IPy-0.75-6.el7        ################## [ 27%]
  4:libsemanage-python-2.5-14.el7   ################## [ 36%]
  5:libcgroup-0.41-21.el7         ############## [ 45%]
  6:checkpolicy-2.5-8.el7         ############## [ 55%]
  7:audit-libs-python-2.8.5-4.el7    ############## [ 64%]
  8:policycoreutils-python-2.5-34.el7 ############## [ 73%]
  9:container-selinux-2:2.119.2-1.911 ############## [ 82%]
setsebool: SELinux is disabled.
 10:libseccomp-2.3.1-4.el7        ############## [ 91%]
正在清理/删除...
 11:policycoreutils-2.5-33.el7     ############## [100%]

(10)进入/tmp/docker-packages路径下,具体命令如下。

$ cd /tmp/docker-packages

(11)安装Docker,具体安装的命令、文件列表及进度如下所示。

$rpm -Uvh *.rpm
1:docker-ce-cli-1:19.03.9-3.el7  #################### [ 33%]
2:containerd.io-1.2.13-3.2.el7  #################### [ 67%]
3:docker-ce-3:19.03.9-3.el7    #################### [100%]

(12)启动Docker服务器,具体命令如下。

systemctl start docker

(13)设置Docker服务器开机自启动,具体命令如下。

systemctl enable docker

(14)通过执行查看Docker版本的命令得知Docker是否已正确安装并且可用,具体命令如下。

docker version

如果docker version执行结果如图4-2-3所示,说明Docker已经安装成功。

图4-2-3 docker version命令执行结果

4.2.2 搭建私有仓库

为了更好的管理镜像,Docker不仅提供了一个仓库服务,同时也允许搭建本地私有仓库服务。本小节介绍私有仓库服务的搭建,并将Kubernetes安装需要的部分镜像和一些基本镜像推至私有仓库中。Docker官方提供了一个搭建私有仓库的镜像Registry,只需通过docker pull命令把镜像下来,运行容器就可以使用了。

1.搭建仓库服务

(1)本案例已经在一台可以连上互联网的主机将Registry镜像保存为归档文件registry.tar,然后将registry.tar上传至master节点的/tmp/images路径下,再使用docker load将镜像归档文件导入为镜像,具体的导入命令如下。

$ cd /tmp/images
$ docker load -i registry.tar

(2)使用Registry镜像后台运行容器,Registry服务默认会将上传的镜像保存在容器的/var/lib/registry,将主机的/opt/registry目录挂载到该目录,即可实现将镜像保存到主机的/opt/registry目录,设置当Docker重启时,该容器自动启动,使用的端口号为5000,容器名为registry,使用的镜像为registry,具体的命令如下。

$ docker run -d -v /opt/registry:/var/lib/registry -p 5000:5000 \
 --restart=always --name registry registry

(3)修改master、node1和node2的配置文件,添加Docker信任私有仓库地址,具体的命令及修改内容如下。

$ vi /etc/docker/daemon.json
{
  "insecure-registries": ["192.168.209.128:5000"]
}

(4)重新加载配置文件并重启服务,具体命令如下。

$ systemctl daemon-reload && systemctl restart docker

2. 上传镜像到仓库

Kubernetes配置和运行过程中会到仓库k8s.gcr.io去拉取kube-proxy镜像,本案例已经在一台可以连上互联网的主机将Kubernetes工具需要用到的镜像保存为归档文件,然后将这些归档文件上传至Master节点的/tmp/kube-images路径下,再使用docker load将镜像归档文件导入为镜像。这些镜像的文件列表如下。

coredns.tar
etcd.tar
kube-apiserver.tar
kube-controller-manager.tar
kube-proxy.tar
kube-scheduler.tar
pause.tar

(1)进入/tmp/kube-images路径下,具体命令如下。

$ cd /tmp/kube-images

(2)上传镜像kube-proxy到私有仓库

①导入kube-proxy镜像,具体命令如下。

$ docker load -i kube-proxy.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images
REPOSITORY TAG   IMAGE ID    CREATED     SIZE
Registry  latest  2d4f4b5309b1 2 months ago  26.2MB
<none>   <none>  3439b7546f29  3 months ago  117MB

③使用docker tag命令将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag 3439b7546f29 192.168.209.128:5000/kube-proxy:v1.18.3

④使用docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/kube-proxy

⑤删除本地镜像,具体命令如下。

$ docker rmi 192.168.209.128:5000/kube-proxy:v1.18.3

(3)上传镜像kube-apiserver到私有仓库

①导入kube-apiserver镜像,具体命令如下。

$ docker load -i kube-apiserver.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images
REPOSITORY TAG   IMAGE ID    CREATED     SIZE
Registry  latest 2d4f4b5309b1 2 months ago  26.2MB
<none>   <none> 7e28efa976bd 3 months ago  173MB

③使用docker tag命令将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag 7e28efa976bd \
192.168.209.128:5000/kube-apiserver:v1.18.3

④使用docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/kube-apiserver:v1.18.3

⑤删除本地镜像,具体命令如下。

$ docker rmi 192.168.209.128:5000/kube-apiserver:v1.18.3

(4)上传镜像kube-controller-mananger到私有仓库

①导入kube-controller-mananger镜像,具体命令如下。

$ docker load -i kube-controller-manager.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images
REPOSITORY TAG   IMAGE ID    CREATED     SIZE
Registry  latest 2d4f4b5309b1 2 months ago  26.2MB
<none>   <none> da26705ccb4b  3 months ago  162MB

③使用docker tag命令将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag da26705ccb4b \
192.168.209.128:5000/kube-controller-manager:v1.18.3

④通过docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push \
192.168.209.128:5000/kube-controller-manager:v1.18.3

⑤删除本地镜像,具体命令如下。

$ docker rmi 192.168.209.128:5000/kube-controller-manager:v1.18.3

(5)上传镜像kube-scheduler到私有仓库

①导入kube-scheduler镜像,具体命令如下。

$ docker load -i kube-scheduler.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images
REPOSITORY TAG   IMAGE ID    CREATED     SIZE
Registry  latest 2d4f4b5309b1 2 months ago  26.2MB
<none>   <none> 76216c34ed0c 3 months ago  95.3MB

③使用docker tag命令将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag 76216c34ed0c \
192.168.209.128:5000/kube-scheduler:v1.18.3

④使用docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/kube-scheduler:v1.18.3

⑤删除本地镜像,具体命令如下。

$ docker rmi 192.168.209.128:5000/kube-scheduler:v1.18.3

(6)上传镜像coredns到私有仓库

①导入coredns镜像,具体命令如下。

$ docker load -i coredns.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images                  
REPOSITORY TAG   IMAGE ID    CREATED     SIZE
Registry  latest 2d4f4b5309b1 2 months ago  26.2MB
<none>   <none>  67da37a9a360 7 months ago  43.8MB

③使用docker tag命令将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag 67da37a9a360 192.168.209.128:5000/coredns:1.6.7

④使用docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/coredns:1.6.7

⑤删除本地镜像,具体命令如下。

$ docker rmi 192.168.209.128:5000/coredns:1.6.7

(7)上传镜像etcd到私有仓库

①导入etcd镜像,具体命令如下。

$ docker load -i etcd.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images                  
REPOSITORY TAG   IMAGE ID    CREATED     SIZE
Registry  latest 2d4f4b5309b1 2 months ago  26.2MB
<none>   <none> 303ce5db0e90 10 months ago 288MB

③使用docker tag命令将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag 303ce5db0e90 192.168.209.128:5000/etcd:3.4.3-0

④使用docker tag命令docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/etcd:3.4.3-0

⑤删除本地镜像,具体命令如下。

$ docker rmi 192.168.209.128:5000/etcd:3.4.3-0

(8)上传镜像pause到私有仓库

①导入pause镜像,具体命令如下。

$ docker load -i pause.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images
REPOSITORY TAG   IMAGE ID    CREATED     SIZE
Registry  latest 2d4f4b5309b1 2 months ago  26.2MB
<none>   <none> 80d28bedfe5d 6 months ago  683KB

③使用docker tag命令将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag 80d28bedfe5d 192.168.209.128:5000/pause:3.2

④使用docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/pause:3.2

⑤删除本地镜像,具体命令如下。

$ docker rmi 192.168.209.128:5000/pause:3.2

(9)测试镜像是否上传到私有仓库中,具体的命令及执行结果显示如下。

$ curl http://192.168.209.128:5000/v2/_catalog
{"repositories":["coredns","etcd","kube-apiserver","kube-controller-manager","kube-proxy","kube-scheduler","pause"]}

4.2.3 安装集群管理工具

Kubeadm是Kubernetes官方社区推出的一套用于简化快速部署Kubernetes集群的工具,Kubeadm的设计目的是为新用户开始尝试Kubernetes提供一种简单的方法。Kubelet运行在集群中的所有节点上,负责启动Pod和容器。Kubectl是Kubernetes命令行工具,通过Kubectl可以部署和管理应用,查看各种资源,创建、删除和更新各种组件。

本书案例将从Kubernetes官方网站下载的安装包及依赖包上传到每台主机的/tmp/kube-packages路径下。

(1)配置系统路由参数,在/etc/sysctl.d/目录下新建一个Kubernetes的配置文件kubernetes.conf并编辑,具体命令如下。

$ vi /etc/sysctl.d/kubernetes.conf

打开文件后,输入i进入编辑模式,添加以下内容,然后输入:wq!保存退出。

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

(2)加载配置文件,具体命令如下。

$ sysctl --system

(3)如果开启了swap分区,kubelet会启动失败,故需要在每台机器上关闭swap分区,具体命令如下。

$ swapoff -a

同时需要注释文件/etc/fstab中相应的条目,防止开机自动挂载swap分区,编辑该文件,具体命令如下。

$ vi /etc/fstab

打开文件后,输入i进入编辑模式,注释swap相关配置行,内容如下,然后输入:wq!保存退出。

#/dev/mapper/centos-swap swap   swap  defaults    0 0

(4)进入/tmp/kube-packages路径,具体命令如下。

$ cd /tmp/kube-packages/

(5)具体安装的命令及文件列表、进度如下所示。

$ rpm -Uvh *.rpm
 1:socat-1.7.3.2-2.el7            ############# [ 10%]
 2:libnetfilter_queue-1.0.2-2.el7_2    ############# [ 20%]
 3:libnetfilter_cttimeout-1.0.0-7.el   ############# [ 30%]
 4:libnetfilter_cthelper-1.0.0-11.el   ############# [ 40%]
 5:conntrack-tools-1.4.4-7.el7       ############# [ 50%]
 6:kubelet-1.18.8-0             ############# [ 7%]
 7:kubernetes-cni-0.8.6-0          ############# [ 70%]
 8:kubectl-1.18.8-0             ############# [ 80%]
 9:cri-tools-1.13.0-0             ############# [ 90%]
 10:kubeadm-1.18.8-0             ############# [100%]

(6)启动kubelet服务,具体命令如下。

$ systemctl start kubelet

(7)设置kubelet服务开机自启动,具体命令如下。

$ systemctl enable kubelet

4.2.4 配置Kubernetes集群

配置Kubernetes集群的步骤包括初始化主节点,安装和配置网络,节点加入集群,主节点查看集群状态。

1.初始化主节点

(1)在Master主机上执行初始化。将IP地址为192.168.209.128的主机初始化为Master,并设置镜像仓库为搭建好的私有仓库服务,指定Kubernetes版本为v1.18.3,指定Pod网络的IP地址范围为10.244.0.0/16,具体的命令如下。

$ kubeadm init --image-repository=192.168.209.128:5000 \
--apiserver-advertise-address=192.168.209.128 \
--kubernetes-version v1.18.3 \
--pod-network-cidr 10.244.0.0/16

如果初始化成功,会得到以下命令执行结果。

Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
 mkdir -p $HOME/.kube
 sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
 sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
 https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.209.128:6443 --token mywyyd.r8pplbm9wf2ekxat \
  --discovery-token-ca-cert-hash sha256:22a6205eac1ad5b8a15eb8b50efe235e26a034eb79c92abdb9741b19d2a50a48

(2)根据第(1)步初始化命令得到的结果提示,启动集群前,需要运行以下命令。

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

(3)此时root用户还不能使用Kubelet控制集群,需要按照以下方法配置环境变量将信息写入bash_profile文件,具体命令如下。

$ echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> \
~/.bash_profile

(4)为使配置立即生效,执行下列命令。

$ source ~/.bash_profile

(5)记录第(1)步初始化命令得到的结果中加入集群的标记(Token)部分内容,后面可以通过以root身份在每个工作节点上运行以下命令来连接任意数量的工作节点,具体内容如下。

kubeadm join 192.168.209.128:6443 \
--token mywyyd.r8pplbm9wf2ekxat \
--discovery-token-ca-cert-hash \
sha256:22a6205eac1ad5b8a15eb8b50efe235e26a034eb79c92abdb9741b19d2a50a48

2.安装网络插件

Kubernetes支持多种网络方案,本案例使用Flannel。Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。本案例已将下载好的镜像归档文件flannel.v0.12.0-amd64.tar上传至Master节点的/tmp/images路径下,并将下载好的典型配置文件kube-flannel.yml上传至Master节点的/root路径下。

(1)在Master主机上,上传镜像flannel到私有仓库,具体步骤如下。

①进入/tmp/images路径,具体命令如下。

$ cd /tmp/images/

②导入flannel镜像,具体命令如下。

$ docker load –i flannel.v0.12.0-amd64.tar

③通过将该镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag 4e9f801d2217 \
192.168.209.128:5000/flannel:v0.12.0-amd64

④通过docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/flannel:v0.12.0-amd64

(2)修改flannel镜像拉取的Registry服务为192.168.209.128:5000/flannel:v0.-

12.0-amd64,编辑文件kube-flannel.yml,具体命令如下。

$ vi kube-flannel.yml

打开文件后,输入i进入编辑模式,修改配置,具体修改的内容如下,然后输入:wq!保存退出。

image: 192.168.209.128:5000/flannel:v0.12.0-amd64 
image: 192.168.209.128:5000/flannel:v0.12.0-amd64

配置前与配置后的结果分别如图4-2-4和4-2-5所示。

图4-2-4 kube-flannel.yml文件镜像原配置
图4-2-5 kube-flannel.yml文件镜像修改后配置

(3)从kube-flannel.yml文件创建一个或多个资源,具体命令如下。

$ kubectl create -f kube-flannel.yml

(4)重启Kubelet服务,具体命令如下。

systemctl restart kubelet

3.节点加入集群

在主机Node1和Node2执行加入节点的命令,具体命令如下。

$ kubeadm join 192.168.209.128:6443 \
--token mywyyd.r8pplbm9wf2ekxat \
--discovery-token-ca-cert-hash \
sha256:22a6205eac1ad5b8a15eb8b50efe235e26a034eb79c92abdb9741b19d2a50a48

执行结果出现如下信息说明节点成功加入了集群。

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

4.主节点查看集群

在Master节点上执行kubectl get nodes查看节点状态。如果节点状态(STATUS)的值为NotReady,这是因为每个节点都需要启动若干组件,这些组件都是在Pod中运行,需要一段时间。具体命令和执行结果如下所示。

$ kubectl get node
NAME   STATUS  ROLES  AGE  VERSION
master  Ready  master  26m  v1.18.3
node1  Ready  <none>  12m  v1.18.3
node2  Ready  <none>  12m  v1.18.3

4.2.5 部署静态网页项目

Kubernetes是通过控制器来管理Pod的生命周期。为了满足不同的业务场景,Kubernetes开发了Deployment等多种控制器。本案例现利用Kubernetes的Deployment部署一个基于httpd服务的Web静态网页项目。本案例已将制作好的镜像归档文件httpd.tar上传至Master节点的/tmp/images路径下。

(1)上传镜像httpd到私有仓库

①导入httpd镜像,具体命令如下。

$ docker load -i httpd.tar

②查看镜像ID,具体命令和执行显示结果如下。

$ docker images
REPOSITORY TAG  IMAGE ID     CREATED    SIZE
httpd    latest a6ea92c35c43  3 weeks ago  166MB

③使用docker tag命令将镜像标志为要推送到私有仓库的镜像,具体命令如下。

$ docker tag a6ea92c35c43 192.168.209.128:5000/httpd:latest

④使用docker push命令将该镜像上传到私有仓库中,具体命令如下。

$ docker push 192.168.209.128:5000/httpd:latest

(2)Kubernetes集群接受Json和Yaml格式的文件对资源进行描述。在/root目录下创建一个httpd.yaml文件,具体命令如下。

$ cd
$ vi httpd.yaml

打开文件后,输入i进入编辑模式,添加以下内容,然后输入:wq!保存退出。

apiVersion: apps/v1                                   #指定API的版本为v1
kind: Deployment                                        #指定资源的类型 
metadata:                                            #资源的元数据属性
 name: demo-httpd                                      #指定资源的名称
 labels:                                                  #资源的标签
  app: demo
spec:                                                 #资源的规范字段
 replicas: 2                                            #声明副本的数目
 selector:                                                    #选择器
  matchLabels:                                             #匹配标签
   app: demo
 template:                                             #资源的模板属性
  metadata:                                                        
   labels:                                           #设定资源的标签
    app: demo
  spec:
   containers:
   - name: httpd                                      #指定容器的名字
    image: 192.168.209.128:5000/httpd                       #指定容器镜像
    ports:
   - containerPort: 80                          #指定容器对外开放的端口

(3)通过文件名或控制台输入,对资源进行配置,具体命令如下。

$ Kubectl apply -f httpd.yaml

(4)查看资源运行情况,状态(Status)如果显示运行(Running)表明配置成功,具体命令及执行结果如下。

$ kubectl get pods -o wide
NAME                STATUS    IP      NODE  
demo-httpd-587796cf84-8jlvw  Running  10.244.1.8  node1  
demo-httpd-587796cf84-zdgsw  Running   10.244.2.7  node2

(5)根据第(4)步查询的结果中IP地址一栏就可以访问该网页了,具体的测试命令如下。

$ curl 10.244.1.8

本章小结

本章主要介绍了容器技术,包括容器的基本概念、Docker的基础架构、Docker的安装与使用、Kubernetes集群的编排工具、Kubernetes集群的部署以及Web静态网页项目在集群中的部署。通过本章的学习,读者应掌握容器的基本概念、容器和虚拟机的区别、了解Docker的基础架构、了解Kubernetes集群和编排工具,还应能够安装Docker引擎、使用Docker镜像和容器、搭建私有仓库服务、安装集群管理工具、配置Kubernetes集群和使用Kubernetes部署Web项目。

本章习题

一、单项选择题

(1)关于计算虚拟化的描述不正确的是( )。

A.虚拟机与容器都可以把主机上的应用程序隔离开来

B.容器和虚拟化都是在操作系统层面上实现虚拟化

C.每个虚拟化应用不仅包含了应用程序和基本的二进制文件与库,还包含了操作系统

D. 容器能与主机的操作系统共享资源,因此它运行效率较高。

(2)下列说法正确的是( )。

A. Docker容器是用于创建Docker镜像的模板

B.一个Docker Registry中包含一个仓库(Repository)

C.镜像是容器运行时的实体

D.容器可以被创建、启动、停止、删除、暂停等

二、多选题

(1)下列说法正确的是( )。

A.从Docker镜像仓库获取镜像的命令是docker pull

B.删除本地的镜像,可以使用docker image rm命令

C.使用docker save命令可将指定镜像保存成归档文件

D.使用docker load可将镜像归档文件导入为镜像

(2)Kubernetes是一个可移植的,可扩展的开源平台,用于管理容器化的工作负载和服务,方便了声明式配置和自动化,它可以实现以下哪些功能( )

A.服务发现和负载均衡

B.存储编排

C.自动部署和回滚

D.自动二进制打包