近期在学习IBM全栈应用开发微学士课程,故此记录学习笔记。

在现代软件开发中,容器技术已经成为了一种必备的技术。它们通过提供一种轻量级、可移植且独立于平台的方式来封装和运行应用程序,解决了许多传统软件开发和部署过程中的问题。

1. 容器和容器化

尽管容器化(Containerization)并非全新的技术概念,但直到2013年——随着Docker的出现——它才开始引起人们的广泛关注并迅速成长。如今,容器已经成为现代云原生(Cloud Native)开发的核心技术之一。

容器技术解决了长期困扰软件开发的一个重要问题:软件的可移植性。它使得应用程序能在多种不同的平台上无缝运行。

容器是由容器化引擎支持的标准软件单元,它封装了应用程序代码、运行时环境(Runtime)、系统工具,以及开发者构建、发布和运行应用程序所需的所有设置。由于容器的体积小巧,开发者可以在几乎无需等待的情况下,立即开始容器化他们的应用程序。

在传统的软件开发环境中,开发人员往往难以有效地隔离应用程序,也无法为物理服务器上的应用程序指定或分配特定的存储和内存资源。这导致服务器的资源往往被未充分利用或过度利用,从而降低了效率并减少了投资的回报。

此外,传统的部署方式需要对资源进行全面的配置,并且维护成本高昂。物理服务器的限制可能会在高峰工作负载期间影响应用程序的性能。容器技术通过虚拟化操作系统并管理容器的运行,有效地解决了这些问题。

1.1. 容器引擎

容器引擎提供了运行和管理容器的基础设施。这些引擎使得容器能够独立于平台、操作系统、编程语言和IDE运行,降低了部署时间和成本,提高了资源利用率,并实现了跨不同环境的端口。

  • Docker:最流行的容器引擎,它提供了一种轻量级的、可移植的、自包含的容器化解决方案。Docker容器可以在任何地方运行,无论是开发人员的笔记本电脑、数据中心的物理服务器,还是云服务提供商的虚拟机
  • Podman:开源的容器引擎,它提供了一个简单的命令行界面,用于管理容器和镜像。Podman可以在不需要守护进程的情况下运行容器,这使得它更适合于在生产环境中使用、对比Docker更安全
  • LXC:Linux容器(Linux Containers,LXC),一个操作系统级的虚拟化技术,它允许多个Linux系统共享同一个内核。LXC适合进行数据密集型应用程序的测试和开发
  • Vagrant:开源的虚拟化工具,它在正在运行的物理机上提供更高级别的隔离

1.2. Docker

Docker是目前最流行的容器平台:

  • 它是一个开发平台,用于以容器形式开发、发布和运行应用程序
  • 架构简单,具有巨大的可扩展性和可移植性,得到了开发者的欢迎
  • 将应用程序和基础设施隔离,包括硬件、操作系统和容器运行时
  • 使用Go语言编写,利用Linux内核特性提供其功能,并使用命名空间来提供称为容器的隔离工作区
  • 为每个容器创建一组命名空间

Docker通过提供一致且隔离的环境实现稳定的应用程序部署,部署快速:

  • 镜像小且可重用
  • 自动化功能有助于消除错误,简化维护周期,并支持敏捷和CI/CD DevOps实践
  • 简单版本控制可以加快测试、回滚和重新部署的速度
  • 有助于对应用程序进行分段,以便于刷新、清理和修复

Docker激发了更多的创新,例如:

  • Docker CLI:Docker命令行界面
  • Docker Compose:用于定义和运行多容器Docker应用程序的工具
  • Prometheus:用于监控和警报的开源系统
  • Docker Swarm或者Kubernetes的编排技术
  • 使用微服务和Serverless的开发方式

然而,Docker并不适合所有的应用场景。例如,对于需要高性能或安全性、基于Monolith、需要丰富GUI功能、或者执行标准桌面或有限功能的应用程序,就可能不适合使用Docker。

1.2.1. Docker的工作流程

在Docker中,开发者首先创建一个Dockerfile,然后使用Dockerfile创建容器镜像,最后使用容器镜像创建正在运行的容器。

Dockerfile中的FROM命令用于定义基础镜像,CMD命令用于定义容器启动后需要执行的命令。

1
2
FROM nginx:latest
CMD ["nginx", "-g", "daemon off;"]

以上Dockerfile用于创建一个基于Nginx的容器镜像,它使用了nginx:latest作为基础镜像,并在容器启动后执行nginx -g daemon off;命令、也就是启动Nginx服务

Docker提供了一套命令行工具,用于管理容器和镜像:

  • docker build -t my-app:v1 .命令用于使用Dockerfile中的标签创建容器镜像
    • -t参数用于指定镜像的标签
    • .参数用于指定Dockerfile所在的目录
    • my-app:v1用于指定镜像的名称和版本
  • docker run -p 8080:80 nginx命令用于从镜像创建并运行容器
    • -p参数用于指定容器端口和主机端口的映射
    • nginx用于指定容器镜像的名称
    • 8080:80用于指定主机端口和容器端口的映射
  • docker push my-app:v1命令用于将镜像存储在配置的注册表中
  • docker pull nginx命令用于从配置的注册表中检索镜像

Docker主机包含了Dockerfile、镜像(Image)、容器、网络(Network)、存储卷(Storage)等对象,以及其他的插件和附加组件。其中,网络用于隔离容器通信,存储卷(Volume)和绑定挂载(Bind mount)用于在容器停止运行后保留数据。

Docker的工作原理如下:

  1. 用户通过Docker命令行接口(CLI)或Docker的REST API客户端向Docker主机服务器(Host)发送指令
  2. Docker主机内部运行着一个被称为dockerd的守护进程
  3. 守护进程负责监听并处理来自Docker API的请求或命令
  4. 守护进程承担了构建、运行和分发Docker容器的重要任务
  5. Docker将构建好的容器镜像存储在注册表(Registry)中,以便于后续的提取和运行

1.2.2. Docker的客户端-服务器架构

Docker客户端具有与本地或远程的Docker主机进行通信的能力。

在一个系统上,Docker客户端和Docker守护进程可以同时运行,或者Docker客户端可以连接到远程的Docker守护进程进行操作。同时,Docker守护进程也能与其他守护进程进行交互,以共同管理Docker服务。

Docker利用注册表来存储和分发镜像。这些注册表可以是公开的,如Docker Hub,也可以是私有的。注册表的托管位置可以由第三方供应商提供,或者在私有数据中心或云环境中自我托管。

将镜像推送到注册表的过程如下:

  1. 开发人员首先构建镜像,然后将其推送到注册表。这个过程可以手动执行,也可以通过自动化构建管道实现。在这个过程中,Docker将这些镜像存储在注册表中
  2. 之后,本地计算机、云系统或其他本地系统就可以从注册表中拉取这些镜像进行使用

2. Kubernetes基础

每个人的容器之旅都是从一个容器开始的。然而,随着时间的推移,新的应用程序被编写出来,项目被部署到全球范围内以增加可用性。最初的一个容器不可避免地会变成多个容器。此时,我们需要考虑如何将数百或数千个容器连接、管理和扩展为一个大型应用程序,例如数据库或Web应用程序。为了创建、扩展和管理大量容器,容器编排(Container orchestration)是必要的。

容器编排是一种自动化容器管理的方法,它可以帮助开发人员和系统管理员在大规模容器环境中管理容器。容器编排系统可以自动化容器的部署、扩展、管理和运行,从而简化了容器的管理和维护。

在大型动态环境中,容器编排是必不可少的:

  • 简化复杂性,自动化复杂的管理、协调任务
  • 无需人工干预的部署和扩展
  • 提高我们的开发速度、敏捷性和效率,无缝地集成到我们的 CI/CD 工作流程和 DevOps 实践中

容器编排使用以YAML或JSON格式编写的配置文件来定义容器的部署、扩展和管理。这些配置文件可以用于定义容器的资源、网络、存储、服务发现、负载均衡、监控和日志记录等方面。

容器编排工具有:

  • Marathon:Apache Mesos的一个框架,是一个开源的集群管理器,允许通过自动化管理和监控来扩展容器的基础设施任务
  • Nomad:HashiCorp的一个开源的集群管理和调度工具,支持Docker和其他容器化工作负载
  • Docker Swarm:Docker的一个编排工具,它允许用户在Docker主机上运行和管理多个容器;经过专业设计,它可以与Docker Engine无缝集成
  • Kubernetes:Google开源的容器编排工具,它提供了一种自动化容器部署、扩展和操作的平台,支持多个云环境;因其广泛的社区支持和丰富的功能,Kubernetes已经成为了最流行的容器编排工具

2.1. Kubernetes

Kubernetes被官方描述为一个开源系统,用于自动化容器化应用程序的部署、扩展和操作。它是由Google设计并捐赠给Cloud Native Computing Foundation(CNCF)的,它促进了声明式管理、自动化部署、自我修复、水平扩展和服务发现等功能。

Kubernetes的主要概念是:

  • Pod:Kubernetes中最小的部署单元,它是一个或多个容器的集合,它们共享网络和存储资源
    • 每个Pod都有一个唯一的IP地址,它们可以通过这个IP地址进行通信
  • 服务:一组Pod的抽象概念,它们共享一个策略,定义了如何访问这些Pod
    • 一组具有相同Service的Pod会有一个共享的DNS名称
  • Persistent Volume(持久卷):Kubernetes提供的持久存储
  • 资源配置:决定了Pod的资源使用情况,包括CPU、内存、存储等
  • 安全措施:保护云原生工作负载
  • 调度和驱逐:资源紧张的情况下,Kubernetes的调度器可以决定在哪个 Node 上启动 Pod,或者在需要的时候驱逐(Evict)Pod
    • Kubernetes 还支持抢占(Preemption),即在资源紧张的情况下,可以终止优先级较低的 Pod,以便优先级较高的 Pod 可以运行
  • 集群管理:Kubernetes提供了一系列的工具和资源,用于管理集群的状态和配置

2.1.1. Kubernetes生态系统

在容器化应用程序的生态系统中,除了 Kubernetes 这样的容器编排工具外,还需要一些其他的工具来完成各种任务:

  • 构建容器镜像
  • 容器注册表
  • 应用程序日志记录和监控
  • 持续集成/持续部署(CI/CD)

在 Kubernetes 生态系统中,有许多不同类型的提供商都在为这个生态系统做出贡献。这些提供商包括:

  • 公共云提供商如 Prisma、IBM、Google 和 AWS 提供了 Kubernetes 服务,使得用户可以在他们的云平台上轻松部署和管理 Kubernetes 集群。这些公共云提供商通常会提供一些附加的服务,如托管的数据库服务、存储服务和网络服务,以支持 Kubernetes 的运行

  • 开源框架提供商如 Red Hat、VMWare、SUSE、Mesosphere 和 Docker 提供了一些开源的 Kubernetes 发行版,这些发行版包含了一些额外的功能,如更强大的网络插件、存储插件和安全插件,以满足特定的企业需求

  • 管理提供商如 Digital Ocean、Ioodse、SUPERGIANT、CloudSoft、Turbonomic、Techtonic 和 Weaverworks 提供了一些工具和服务,以帮助用户更轻松地管理 Kubernetes 集群。这些工具和服务可以帮助用户自动化一些常见的管理任务,如集群的创建和删除、节点的添加和移除、以及应用的部署和更新

  • 工具提供商如 Jfrog、Univa、Aspen Mesh、Bitnami 和 Cloud 66 提供了一些工具,以帮助用户更轻松地使用 Kubernetes。这些工具包括容器镜像构建工具、CI/CD 工具、网络插件、存储插件等

  • 监控和日志记录提供商如 Sumo Logic、DATADOG、New Relic、Iguazio、Grafana、SignalFX、Sysdig 和 Dynatrace 提供了一些工具和服务,以帮助用户收集和分析 Kubernetes 集群的日志和性能指标

  • 安全提供商如 GUARDCORE、BLACKDUCK、Yubico、Cilium、Aqua、Twistlock 和 Alcide 提供了一些工具和服务,以帮助用户保护 Kubernetes 集群的安全。这些工具和服务可以帮助用户防止未经授权的访问,以及检测和防止安全威胁

  • 负载平衡提供商如 AVI Networks、VMWare 和 NGiNX 提供了一些工具和服务,以帮助用户在 Kubernetes 集群中实现负载均衡。这些工具和服务可以帮助用户在多个 Pod 或 Node 之间分配流量,以实现高可用性和伸缩性

2.1.2. Kubernetes系统的主要组件

Kubernetes 系统由一系列的组件构成,这些组件共同协作,以管理和运行容器化应用程序。以下是 Kubernetes 系统的主要组件:

  • 集群(Cluster):这是运行容器化应用程序的节点(Node)集群。每个集群都由一个主节点(也称为控制面板)和一个或多个工作节点组成

  • 控制面板(Control Plane):控制面板维护着集群的预期状态。它负责调度和启动新的应用程序,监控应用程序的运行状态,以及在应用程序出现问题时进行自动恢复

  • 节点(Node):节点是运行应用程序的工作机器,它可以是虚拟机或物理机。每个节点都由控制面板管理,并运行着 Kubernetes 的一些组件,如 kubelet 和 kube-proxy

  • API 服务器(API Server):API 服务器公开了 Kubernetes API,它是控制面板的前端,所有的集群通信都通过这个 API 进行

  • etcd:etcd 是一个高可用的分布式键值存储系统,它存储了集群的所有配置数据,定义了集群的状态

  • 调度器(Scheduler):调度器负责将新创建的 Pod 分配给节点。它会根据各种调度策略,如资源需求、硬件/软件/策略约束、亲和性和反亲和性规格、数据位置等,选择一个最适合的节点

  • 控制器管理器(Controller Manager):控制器管理器运行所有的控制器进程,包括节点控制器、复制控制器、端点控制器、服务账户和令牌控制器等。它持续监控集群的状态,并确保实际状态与预期状态一致

  • 云控制器管理器(Cloud Controller Manager):云控制器管理器运行与底层云服务商交互的控制器,将集群链接到云提供商的 API 中

  • Pod:Pod 是 Kubernetes 的最小部署单元,每个 Pod 包含一个或多个紧密相关的容器。这些容器共享相同的网络命名空间,可以通过 localhost 互相通信

  • kubelet:kubelet 是工作节点上最重要的组件,它与 API 服务器通信,接收新的和修改的 Pod 规范,并确保 Pod 及其关联的容器按需运行

  • 容器运行时(Container Runtime):容器运行时是负责运行容器的软件。它提供了容器的可插拔性,也就是说,你可以选择使用 Docker、containerd、CRI-O 或其他兼容 Kubernetes 的容器运行时

  • kube-proxy:kube-proxy 是一个在集群中每个节点上运行的网络代理,它维护网络规则,使得从网络外部访问 Pod 的请求可以正确路由到目标 Pod,同时也实现了服务的负载均衡

2.1.3. Kubernetes对象

在 Kubernetes 中,对象是一种持久化的实体,它表示了 Kubernetes 集群中的一种状态。这些对象包括 Pod、Service、Volume、Namespace 等,它们定义了你在集群中运行的应用程序、应用程序可用的资源、应用程序的行为方式(如重启策略)、以及应用程序应该如何处理更新。

Kubernetes 对象由两个主要字段构成:规范(Spec)和状态(Status)。规范描述了对象的期望状态,这是由用户提供的。状态描述了对象的当前状态,这是由 Kubernetes 系统提供的。

要使用 Kubernetes 对象,你可以直接使用 Kubernetes API,或者使用 kubectl 命令行界面,或者两者结合使用。例如,你可以使用 kubectl create 命令来创建一个新的对象,或者使用 kubectl get 命令来查看对象的状态。

标签(Labels)是附加到 Kubernetes 对象上的键值对,用于识别和组织对象。一个对象可以有多个标签,而且多个对象可以有相同的标签。这使得你可以轻松地组织和选择你的对象。标签选择器(Label Selectors)是 Kubernetes 中的一种核心分组机制,它允许你根据标签来选择一组对象。

命名空间(Namespace)在 Kubernetes 中提供了一种资源隔离的机制。一个 Kubernetes 集群可以包含多个命名空间,每个命名空间代表了集群中的一个逻辑分区。你可以在不同的命名空间中运行不同的应用程序,这些应用程序的资源是彼此隔离的。这使得你可以在一个集群中运行多个独立的应用程序,而不需要担心它们会相互干扰。

YAML文件可以定义要创建的对象。例如我们来定义一个简单的Pod:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

ReplicaSet是Pod的一组副本,它确保指定数量的Pod副本在任何时间都是可用的。例如我们来定义一个简单的ReplicaSet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-replicaset
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
specs:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

ReplicaSet其实并不建议直接创建,而是通过Deployment来创建。Deployment是ReplicaSet的一种控制器,它提供了一种声明式的方式来管理Pod的副本。例如我们来定义一个简单的Deployment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

对比ReplicaSet,Deployment提供了一个关键功能:滚动更新。滚动更新是指在不中断服务的情况下,逐步更新Pod的副本。这样可以确保应用程序在更新过程中保持可用性。

Deployment适合无状态的应用程序,而StatefulSet适合有状态的应用程序。StatefulSet是Pod的一组有序的副本,它确保每个Pod副本都有唯一的标识符和稳定的网络标识符。

Service是一个REST对象,它定义了一组Pod的访问策略。Service可以通过标签选择器来选择一组Pod,然后通过Service的IP地址和端口号来访问这些Pod。Service有四种类型:ClusterIP、NodePort、ExternalLoadBalancer和ExternalName。

  • ClusterIP:Service的默认类型,它将Service暴露为集群内部的IP地址
  • NodePort:在静态端口上暴露Service,这样就可以通过Node的IP地址和静态端口来访问Service;不建议在生产环境中使用NodePort
  • ExternalLoadBalancer(又称ELB):自动将流量引导到NodePort
  • ExternalName:将Service映射到外部的DNS名称

Ingress是一个API对象,它定义了一组HTTP和HTTPS路由规则,用于将外部流量引导到集群内部的Service。Ingress可以提供负载均衡、SSL终止、主机名基于路由、路径基于路由等功能。

DaemonSet是一个确保每个Node上都运行一个Pod的控制器。它通常用于运行一些系统级的服务,如日志收集器、监控代理、网络代理等。

Job是一个确保Pod成功运行一次的控制器。它通常用于运行一些独立的任务,如数据备份、数据迁移、数据处理等。如果Pod失败,Job会自动重试,直到Pod成功运行。

  • aCronJob是一个确保Pod周期性运行的控制器

2.2. kubectl命令行工具

kubectl是Kubernetes的命令行工具,它允许你与Kubernetes集群进行交互。kubectl可以用于创建、删除、更新、查看和监控Kubernetes对象。

kubectl命令类型有:

  • 命令式命令(Imperative Commands)
  • 命令式对象配置(Imperative Object Configuration)
  • 声明式对象配置(Declarative Object Configuration)

2.2.1. 命令式命令

命令式命令是一种直接操作Kubernetes对象的方式。例如,你可以使用 kubectl run 命令来创建一个新的Pod:

1
kubectl run nginx --image nginx
  • 适合用于开发和测试环境,但不适合用于生产环境
  • 并不灵活,选项相当有限
  • 持久性差,不容易追踪和管理
  • 不易于在团队中共享和协作

2.2.2. 命令式对象配置

命令式对象配置是一种指定必需的操作、参数和选项来创建Kubernetes对象的方式。例如,你可以使用 kubectl create 命令来创建一个新的Pod:

1
kubectl create -f nginx.yaml
  • 多个环境中使用相同的配置文件将产生相同的结果
  • 对比命令式命令结构更清晰,更易于维护
  • 可重用也可共享
  • 状态管理不易,需要手动维护
  • 如果需要管理多个对象,使用命令式对象配置会变得非常复杂

2.2.3. 声明式对象配置

声明式对象配置是一种将配置数据存储在文件中,然后使用 kubectl apply 命令来应用这些配置数据的方式。操作由kubectl自动完成,而不是由用户手动完成:

1
kubectl apply -f nginx/
  • 声明式对象配置关注状态而非操作,用户只需要描述所需的最终状态
  • 可以轻松地在多个环境中共享和重用
  • 学习曲线较陡,需要一定的学习成本
  • 适合用于生产环境
  • 调试和故障排除较为困难