vlambda博客
学习文章列表

读书笔记《hands-on-docker-for-microservices-with-python》使用Kubernetes协调微服务

Using Kubernetes to Coordinate Microservices

在本章中,我们将讨论 Kubernetes 背后的基本概念,它是一种允许您管理多个容器并协调它们的工具,从而使部署在每个容器上的微服务能够协同工作。

本章将介绍容器编排器是什么以及特定的 Kubernetes 命名法,例如 pod、服务、部署等之间的区别。我们还将学习如何分析正在运行的集群并执行其他常见操作,以便您可以将它们应用到我们的微服务示例中。

在本章中,我们将介绍以下主题:

  • Defining the Kubernetes orchestrator
  • Understanding the different Kubernetes elements
  • Performing basic operations with kubectl
  • Troubleshooting a running cluster

在本章结束时,您将了解 Kubernetes 的基本要素,并能够执行基本操作。您还将了解基本的故障排除技巧,以便检测可能出现的问题。

Technical requirements

如果您使用 macOS 或 Windows,默认 Docker 桌面安装可以启动本地 Kubernetes 集群。只需确保在 Kubernetes 的首选项中启用此功能:

读书笔记《hands-on-docker-for-microservices-with-python》使用Kubernetes协调微服务

对于 Linux,在本地安装 Kubernetes 最简单的方法是使用 k3s (https://k3s.io/ )。

k3s is a nod to Kubernetes (that is, k8s) but is a simplified version of it.

k3s 是 Kubernetes 的简约安装,您可以使用它来运行包含在单个二进制文件中的集群。查看 安装页面(https://github. com/rancher/k3s/blob/master/README.md)如果你想下载并运行它。

为了能够使用在 k3s 集群中运行的 Docker 版本,我们需要使用以下代码:

$ # Install k3s
$ curl -sfL https://get.k3s.io | sh -
$ # Restart k3s in docker mode
$ sudo systemctl edit --full k3s.service
# Replace `ExecStart=/usr/local/bin/k3s` with `ExecStart=/usr/local/bin/k3s server --docker`
$ sudo systemctl daemon-reload
$ sudo systemctl restart k3s
$ sudo systemctl enable k3s
$ # Allow access outside of root to KUBECTL config
$ sudo chmod 644 /etc/rancher/k3s/k3s.yaml
$ # Add your user to the docker group, to be able to run docker commands
$ # You may need to log out and log in again for the group to take effect
$ sudo usermod -a -G docker $USER

确保安装 kubectl (k3s 默认安装一个单独的版本)。安装 kubectl 的步骤可以在 https:// 找到kubernetes.io/docs/tasks/tools/install-kubectl/kubectl 命令控制 Kubernetes 操作。

Check the instructions on the aforementioned page to add Bash completion, which will allow us to hit Tab to complete some commands.

如果一切都已正确安装,您应该能够使用以下命令检查正在运行的 pod:

$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
docker compose-89fb656cf-cw7bb 1/1 Running 0 1m
docker compose-api-64d7d9c945-p98r2 1/1 Running 0 1m
kube-system etcd-docker-for-desktop 1/1 Running 0 260d
kube-system kube-apiserver-docker-for-desktop 1/1 Running 0 2m
kube-system kube-controller-manager-docker-for-desktop 1/1 Running 0 2m
kube-system kube-dns-86f4d74b45-cgpsj 3/3 Running 1 260d
kube-system kube-proxy-rm82n 1/1 Running 0 2m
kube-system kube-scheduler-docker-for-desktop 1/1 Running 0 2m
kube-system kubernetes-dashboard-7b9c7bc8c9-hzpkj 1/1 Running 1 260d

注意不同的命名空间。它们都是 Kubernetes 自己创建的默认值。

转到以下页面安装 Ingress 控制器:https: //github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md。在 Docker 桌面中,您需要运行以下两个命令:

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
$
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml

这将创建一个带有控制器 pod 的 ingress-nginx 命名空间。 Kubernetes 将使用该 pod 来设置 Ingress 配置。

现在,让我们来看看使用 Kubernetes 的优势。

Defining the Kubernetes orchestrator

Kubernetes 是一种流行的容器编排工具。它允许我们管理和部署多个以协调方式相互交互的容器。由于每个微服务都存在于一个单独的容器中,正如我们在 第 1 章中提到的,< em>让行动-设计、计划和执行,他们可以协同工作。

For a more in-depth introduction to Kubernetes, you can check out the following comic, which was released by Scott McCloud: https://cloud.google.com/kubernetes-engine/kubernetes-comic/.

Kubernetes 针对的是生产系统。它旨在能够控制大型部署并抽象出大部分基础设施的细节。 Kubernetes 集群中的每个元素都以编程方式配置,Kubernetes 本身根据可用容量管理集群的部署位置。

Kubernetes 可以完全使用配置文件进行配置。这使得复制集群成为可能,例如,在发生导致所有物理服务器停机的全面灾难时。您甚至可以使用不同的硬件来执行此操作,而传统部署可能非常困难。

此示例假定数据已存储并可检索;例如,在备份设备中。显然,这可能很困难——灾难恢复总是如此。但是,如果您希望复制集群,它会简化许多所需的步骤。

鉴于 Kubernetes 与容器一起工作并且非常容易安装它们,因此有一个庞大的容器生态系统可以为 Kubernetes 本身添加功能。最好的例子可能是 Kubernetes 仪表板(https://kubernetes .io/docs/tasks/access-application-cluster/web-ui-dashboard/),一个显示 Kubernetes 操作概览的 UI。默认情况下不安装它,但可以以与安装服务相同的方式安装它。此类用例的其他示例包括监控和日志记录。这使得 Kubernetes 非常可扩展。

Comparing Kubernetes with Docker Swarm

Kubernetes 不是唯一可用的编排器。正如我们在第 3 章中提到的,构建、运行和测试您的使用 Docker 服务,有 docker-compose。 Docker Compose 还可以编排不同的容器并协调它们,但无需处理多个服务器。

Docker 有一个名为 Docker Swarm 的 docker-compose 原生扩展。这允许我们使用机器集群来运行 docker-compose,同时重用相同的 YAML 文件,但添加了一些细节来描述您希望它们如何运行。

您可以在官方文档中了解更多关于 Docker Swarm 的信息( https://docs.docker.com/engine/swarm/ ).

假设您必须管理服务器,Docker Swarm 比 Kubernetes 更容易设置。当您扩展 Docker Compose 的功能时,您会发现它的学习曲线很低。

另一方面,Kubernetes 更加强大和可定制。它拥有更大的社区和更高的创新步伐。它也更擅长处理问题。最大的问题是建立一个集群,但正如我们将在 Chapter 7配置和保护生产系统,如今,我们可以使用简单的商业部署在几分钟内创建一个集群,这降低了 Kubernetes 的进入门槛。

当您处理从旧系统迁移和展望未来时,这使得 Kubernetes(可以说)成为更​​好的解决方案。对于小型部署,或者如果您需要部署和管理自己的服务器,Docker Swarm 可能是一个有趣的替代方案。

帮助您继续使用 docker-compose.yaml 文件要使用等效的 Kubernetes YAML 文件,您可以使用 组合 ( https://github.com/kubernetes/kompose <跨度>)。 快速启动 Kubernetes 集群并将 docker-compose.yaml 文件中描述的服务转换为等效的 Kubernetes 元素可能很有用,但两个系统之间总是存在差异,可能需要进行调整。

让我们从描述 Kubernetes 的具体元素和命名法开始。

Understanding the different Kubernetes elements

Kubernetes 对不同的元素有自己的命名法。 我们将在本书中经常使用该术语,Kubernetes 文档也使用它们。 了解它们之间的差异很重要,因为其中一些可能很微妙。

Nodes

Kubernetes 的主要基础设施元素称为节点。一个 Kubernetes 集群由一个或多个节点组成,这些节点是支持其余元素抽象的物理机(或虚拟机)。

每个节点都需要能够与其他节点通信,并且它们都运行在 容器运行时——通常是 Docker——但它们可以使用其他系统,例如 rktlet (< a href="https://github.com/kubernetes-incubator/rktlet" target="_blank">https://github.com/kubernetes-incubator/rktlet)。

节点在它们之间创建一个网络,将所有已发送到集群的请求路由,以便发送到集群中任何节点的任何请求都将得到充分响应。 Kubernetes 将处理哪些可部署到哪个节点,甚至在节点出现故障时恢复节点,或者如果存在资源问题,将它们从一个节点移动到另一个节点。

节点不一定需要相同,在特定节点中部署特定元素时需要一定程度的控制,但为简单起见,它们通常是相同的。

虽然节点是支持集群的骨干,但 Kubernetes 通过定义期望的结果并让 Kubernetes 完成决定什么去哪里以及确保将内部网络通道的请求发送到适当的服务。

Kubernetes Control Plane

Kubernetes 控制平面是 Kubernetes 用来将服务器集合正确配置为 Kubernetes 集群中节点的所有进程所在的位置。服务器允许节点相互连接,允许我们监控它们的当前状态,并允许我们在部署、规模等方面进行任何必要的更改。

负责注册和进行这些更改的节点称为主节点。可以有多个主节点。

所有这些控制通常在幕后顺利运行。它的网络与其他网络是分开的,这意味着这个级别的问题不会影响集群的当前运行,除了我们无法进行更改。

Kubernetes Objects

Kubernetes 对象是表示部署在集群中的服务状态的抽象。它们主要处理运行容器和这些容器的路由,以及持久存储。

让我们来看看不同的元素,从最小到最大。此列表并不详尽;查看 Kubernetes 文档了解更多详细信息:

  • Container: A single Docker container. These are the building blocks of Kubernetes, but they're never present on their own.
  • Pod: A basic unit that can be deployed in Kubernetes. A pod is a collection of one or more containers that work as a unit, normally from different images. Normally, a pod has a single container, but sometimes it may be useful to have more. All of the containers in the same pod share the same IP address (the pod IP), meaning that a container that accesses a port in localhost may be accessing a different container instead. This is actually the recommended way of communicating with them.
This will all be a bit strange to you at first, but normally, multi-container pods will have a main container and something else that performs auxiliary tasks, such as exporting metrics.
  • ConfigMap: This defines a set of key-value pairs that can be injected into pods, typically as environment variables or files. This allows us to share configurations between different defined pods, for example, to make all the containers log debug information. Note that pods can have their own configuration, but ConfigMaps are a convenient way to share the same values so that they are available to different pods.
  • Volume: The files that are inside a container are ephemeral and will be lost if the container stops its execution. A volume is a form of persistent storage that can be used to maintain data information between starts and to share information between containers in a pod.
As a general principle, try to have as few volumes as possible. Most of your applications should be stateless anyway, and any variable data should be stored in a database. If containers in the same pod need to communicate, it is better to do so through HTTP requests. Remember that any immutable data, such as static files, can be stored inside the container image.
  • Deployment: This is a grouping of one or more identical pods. The definition of the deployment will state the desired number and Kubernetes will work to get to this, according to whatever strategy is defined. The pods in a single deployment can be deployed to different nodes, and normally will be. If any of the pods are deleted, finished, or have any kind of problem, the deployment will start another until the defined number is reached.
  • Job: A job creates one or more pods that are expected to finish. While a deployment will assume that any pod that's finishing is a problem and will start another, jobs will retry until the proper number of successes is met. The finished pods are not deleted, which means we can check their logs. Jobs are one-off executions. There are also Cron Jobs, which will run at specific times.
  • Service. Since pods are created and recreated and have different IPs, to allow services to access them, a service needs to define the name that other elements can use to discover it. In other words, it routes requests to the proper pods. Normally, a service and a deployment will be related, with the service making the deployment accessible and round-robin between all the defined pods. A service can also be used to create an internal name for an external service.
Services in Kubernetes solve an old problem in distributed systems, that is, service discovery. This problem occurs when nodes in a cluster need to know where a service lives, even if the nodes change; that is, when we add a node or remove it without changing the configuration settings of all the nodes.

Kubernetes will do this automatically if you create a service.
  • Ingress: While a service is internal, an Ingress is external. It routes any external requests to the appropriate service so that they can be served. You can define different Ingresses by host name, which ensures that the cluster is routed to different services by the target host of the request, or a single Ingress is hosted in terms of its path. Internally, an Ingress is implemented as a container that implements the Ingress controller, which is nginx by default.
根据您的 Kubernetes 安装,您可能需要安装默认控制器。要安装默认控制器,请按照以下说明进行操作 https://github.com/kubernetes/ingress-nginx /blob/master/docs/deploy/index.md
  • Namespace: This is the definition of a virtual cluster. You can define more than one namespace in the same physical Kubernetes cluster. Every name that's defined under a namespace needs to be unique, but another namespace could use the same definition. Objects in different namespaces can't communicate with each other internally, but they can do so externally.
Generating different namespaces with very similar definitions can be useful if you wish to create different environments for purposes, such as testing, development, or demo concepts. The main advantage of Kubernetes is that you can replicate a whole system and take advantage of this to create similar environments with small changes in terms of details, such as a new version of an environment.

对象可以在.yaml文件中找到,可以加载到系统中。单个 .yaml 文件可以定义多个对象,例如,定义包含容器的 pod 的部署。

下图总结了可用的不同对象:

读书笔记《hands-on-docker-for-microservices-with-python》使用Kubernetes协调微服务

作业和卷不存在,但有两种服务可用:一种指向部署,另一种指向外部服务。外部服务针对内部元素,不对外暴露。

Performing basic operations with kubectl

通过使用 kubectl,我们可以对所有不同的元素执行操作。我们已经对 get 进行了初步了解,以了解可用的元素。

欲了解更多信息并快速了解其中可用的最常见操作 kubectl,查看 kubectl 备忘单在 https://kubernetes.io/docs/reference/kubectl/cheatsheet/

我们可以使用 kubectlcreate 一个新元素。例如,要创建和列出命名空间,我们可以使用以下代码:

$ kubectl create namespace example
namespace/example created
$ kubectl get namespaces
NAME STATUS AGE
default Active 260d
docker Active 260d
example Active 9s
kube-public Active 260d
kube-system Active 260d

我们可以创建各种元素,其中一些我们将在本书中看到。

Defining an element

命名空间是一种特殊情况,因为它不需要任何配置。要创建一个新元素,需要创建一个描述该元素的 YAML 文件。例如,我们可以使用 Docker Hub 中的官方 NGINX 镜像创建一个新的 pod:

---
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: example
spec:
containers:
- name: nginx
image: library/nginx:latest

至少,一个元素应包含以下内容:

  • The API version of the element.
  • The element's type.
  • A name for the element, as well as a namespace for it.
  • A spec section that includes configuration details. For a pod, we need to add the necessary containers.
YAML files can be a bit temperamental sometimes, especially when it comes to indentation and syntax. You can use a tool such as Kubeval ( https://kubeval.instrumenta.dev/) to check that the file is correct and that you're following Kubernetes good practices before using a file.

我们将此文件保存为 example_pod.yml。我们将使用 apply 命令创建它,并使用以下命令监控它是否正在运行:

$ kubectl apply -f example_pod.yml
pod/nginx created
$ kubectl get pods -n example
NAME READY STATUS RESTARTS AGE
nginx 0/1 ContainerCreating 0 2s
$ kubectl get pods -n example
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 51s
Note the usage of the -n parameter to determine the namespace.

我们现在可以 exec 进入容器并在其中运行命令。例如,要检查 NGINX 服务器是否正在运行并提供文件,我们可以使用以下代码:

$ kubectl exec -it nginx -n example /bin/bash
root@nginx:/# apt-get update
...
root@nginx:/# apt-get install -y curl
...
root@nginx:/# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

可以通过两种方式更改吊舱。第一种方法是手动运行 edit,它会打开预定义的终端编辑器,以便您可以编辑文件:

$ kubectl edit pod nginx -n example

您将看到 pod 及其所有默认参数。这种更改 pod 的方式对于小型测试很有用,但一般来说,最好更改原始 YAML 文件,以便您可以跟踪发生的更改。例如,我们可以更改 NGINX 以便我们使用它的早期版本:

---
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: example
spec:
containers:
- name: nginx
image: library/nginx:1.16

然后,我们可以再次应用这些更改,这将重新启动 pod:

$ kubectl apply -f example_pod.yml
pod/nginx configured
$ kubectl get pods -n example
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 1 36s

Getting more information

get 命令接受更多配置。您可以使用 wide 输出选项检索更多信息:

$ kubectl get pods -n example -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 1 30m 10.1.0.11 docker-for-desktop

如果您进行了更改并对由此产生的更改感兴趣,则可以使用 -w 参数来观察任何更改。例如,下面的代码展示了一个 Pod 的重启结果。由于对容器的映像进行了更改,因此产生了此重新启动:

$ kubectl get pods -n example -w
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 2 32m
nginx 1/1 Running 3 32m

如果您需要有关特定元素的更多信息,可以 describe 它:

$ kubectl describe pod nginx -n example
Name: nginx
Namespace: example
Node: docker-for-desktop/192.168.65.3
Start Time: Sun, 23 Jun 2019 20:56:23 +0100
Labels: <none>
Annotations: ...
Status: Running
IP: 10.1.0.11
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 40m default-scheduler Successfully assigned nginx to docker-for-desktop
...
Normal Created 4m43s (x5 over 40m) kubelet, docker-for-desktop Created container
Normal Started 4m43s (x5 over 40m) kubelet, docker-for-desktop Started container

这会返回很多信息。最有用的信息通常是关于事件的,它会返回有关元素生命周期的信息。

Removing an element

delete 命令 删除一个元素及其下的所有内容:

$ kubectl delete namespace example
namespace "example" deleted
$ kubectl get pods -n example
No resources found.

请注意,有时,删除一个元素会导致它被重新创建。这在通过部署创建 pod 时很常见,因为部署将努力使 pod 的数量达到配置的数量。

Troubleshooting a running cluster

我们可以用来解决 Kubernetes 问题的主要工具是 getdescribe 命令。

根据我的经验,运行 Kubernetes 最常见的问题是,有时某些 pod 无法启动。故障排除步骤如下:

  1. Is the container image correct? A problem with downloading the image will show ErrImagePull. This could be caused if the image can't be downloaded from the registry due to an authentication problem.
  2. A status of CrashLoopBackOff means that the process for the container has been interrupted. The pod will try to restart over and over. This is normally caused by an underlying issue with the container. Check that the configuration is correct. You can check the stdout logs of a container by using the following command:
$ kubectl logs <pod> -n <namespace> -c <container>

确保容器可运行。尝试使用以下命令手动运行它:

$ docker run <image>
  1. A pod is not exposed externally. This is typically due to a problem in the service and/or Ingress that exposes them. You can detect whether a pod is responsive inside the cluster by using exec to get into another container and then try to access the service and the internal IP of the pod, normally using curl.
As we saw previously, curl is not installed in containers by default because, normally, they only install a minimal set of tools. Don't worry—you can install it using whatever package manager your operating system uses, with the advantage that, once the container is recycled (which will happen soon enough in a normal Kubernetes operation), it won't be using up any space! For the same reason, you may need to install it each time you need to debug a problem.

记住我们讨论过的 Ingress、服务、部署和 Pod 的链条,并从内到外找出错误配置的位置。

在进行故障排除时,请记住可以通过 exec 命令访问 pod 和容器,这将允许我们检查正在运行的进程、文件等等。这类似于访问物理服务器的终端。您可以使用以下代码执行此操作:

$ kubectl exec -it <pod> -n <namespace> /bin/sh

请注意,如果在同一个 pod 中运行多个容器,则 Kubernetes 集群的性质可能要求您检查 pod 中的特定容器。

Summary

在本章中,我们了解了 Kubernetes 的基本概念,以及它如何管理和协调包含我们微服务的多个容器。

首先,我们介绍了 Kubernetes 是什么以及它的一些高级优势。然后,我们描述了在 Kubernetes 命名法中定义集群的不同元素。这包括物理方面,其中节点是主要定义元素,以及抽象方面,例如 pod、部署、服务和入口,它们是我们生成工作集群所需的构建块。

我们描述了 kubectl 以及我们可以用来定义元素和通过 YAML 文件检索信息的常见操作。 我们还描述了处理 Kubernetes 集群时可能出现的一些常见问题。

在下一章中,我们将定义可以在 YAML 文件中使用的不同选项,以生成集群并学习如何为我们的微服务示例生成 Kubernetes 集群。

Questions

  1. What is a container orchestrator?
  2. In Kubernetes, what is a node?
  3. What is the difference between a pod and a container?
  4. What is the difference between a job and a pod?
  5. When should we add an Ingress?
  6. What is a namespace?
  7. How can we define a Kubernetes element in a file?
  8. What is the difference between the get and describe commands of kubectl?
  9. What does a CrashLoopBackOff error indicate?