为了快速开始学习Kuma
,我们需要的最重要的东西之一就是集群。然后,我们还需要一个命令来找出 Kubernetes(又名k8s
)中 pod 的状态,我们还需要能够安装Kuma
,最后,我们还需要能够发出一些Kuma
命令。
简而言之,我们需要安装 4 个基本命令,以便为Kuma
做好一切准备。这些命令是:
kind
- 在 Docker 中也称为 Kubernetes。这是一个利用仅使用kubectl
创建内容的权重的命令。
kubectl
- 如果你已经习惯使用k8s
,那么这可能是此列表中最受期待的一个。这就是我们可以向k8s
集群发出命令的方式。
helm
-Helm 允许我们执行一些非常方便的脚本,其中包括安装Kuma
控制平面。
kumactl
- 我们不会在本指南中频繁使用此命令,但了解如何使用它很重要。
本指南将告诉您如何在Ubuntu
中执行此操作。所有这些都已在Ubuntu
系统中进行了测试。如果您对如何在Mac-OS
或Windows
或您可能拥有的任何其他操作系统中安装此软件的指南感兴趣,请在我的 YouTube 频道JESPROTECH
社区中给我留言。
k8s
)为了安装 kind,我们需要发出以下命令:
[ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64 chmod +x ./kind sudo mv ./kind /usr/local/bin/kind
需要注意的是,命令kind
将安装在/usr/local/bin/kind
中。这可能因系统而异,即使在 Linux 发行版中也是如此。
helm
和kubectl
命令都需要在某些GPG
密钥存在的情况下进行安装。我们可以这样将它们添加到我们的 Linux apt
发行版的本地存储库中:
sudo apt-get install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list sudo apt-get update
一旦完成上一步, Kubectl
的安装就非常简单:
sudo apt-get install -y kubelet kubeadm kubectl
kubelet
、 kubeadm
和kubectl
命令不是强制性的,但安装它们是个好主意。
你可能已经猜到了, helm
现在也非常容易安装:
sudo apt-get install -y helm
Kuma
安装可能有点麻烦,因为它涉及一个手动步骤,但首先,我们需要下载我们的依赖项:
cd ~ || exit; curl -L https://kuma.io/installer.sh | VERSION=2.6.1 sh -
发出此命令之前,请确保位于HOME
文件夹中。将Kuma
安装在易于访问且易于发现的位置非常重要,例如,如果我们决定删除它。
完成后,将bin
文件夹添加到我们的 PATH 中也非常重要:
export PATH=~/kuma-2.6.1/bin:$PATH;
将此行添加到启动脚本的末尾或中间的任何地方将使此过程变得简单。您的启动脚本可能是.bashrc
、 .zshrc
、 .profile
中的任何一个,也可能采用其他形式。
安装k9s
也与其他应用程序有很大不同。在这种情况下,我们可以使用pacman
或brew
for Linux
。我主要在Mac-OS
上使用brew
,在 Linux 中几乎不需要它,但在这种情况下,它非常需要,因此首先,我们需要像这样安装 brew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
一旦 brew 安装完成,我们要做的就是安装k9s
(“ kanines
”):
brew install derailed/k9s/k9s
需要考虑的一件重要的事情是,当您第一次安装并开始运行k9s
时您可能会注意到这一点,那就是如果它监控的集群被删除和/或添加, k9s
将崩溃。
kind create cluster --name=wlsm-mesh-zone kubectl cluster-info --context kind-wlsm-mesh-zone
第一个命令创建一个名为wlsm-mesh-zone
集群。这只是我们将用来安装 Kuma 的集群。第二个命令用于检查集群的状态。
正如我之前提到的,我们可以非常轻松地创建一个 docker 注册表。虽然创建它听起来很容易,但执行此操作的脚本却很繁琐。因此,最好的办法就是复制并粘贴他们网站上已有的脚本。在这里,我们可以下载此脚本:
#!/bin/sh # Original Source # https://creativecommons.org/licenses/by/4.0/ # https://kind.sigs.k8s.io/docs/user/local-registry/ set -o errexit # 1. Create registry container unless it already exists reg_name='kind-registry' reg_port='5001' if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then docker run \ -d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \ registry:2 fi # 2. Create kind cluster with containerd registry config dir enabled # TODO: kind will eventually enable this by default and this patch will # be unnecessary. # # See: # https://github.com/kubernetes-sigs/kind/issues/2875 # https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration # See: https://github.com/containerd/containerd/blob/main/docs/hosts.md cat <<EOF | kind create cluster --config=- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 containerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d" EOF # 3. Add the registry config to the nodes # # This is necessary because localhost resolves to loopback addresses that are # network-namespace local. # In other words: localhost in the container is not localhost on the host. # # We want a consistent name that works from both ends, so we tell containerd to # alias localhost:${reg_port} to the registry container when pulling images REGISTRY_DIR="/etc/containerd/certs.d/localhost:${reg_port}" for node in $(kind get nodes); do docker exec "${node}" mkdir -p "${REGISTRY_DIR}" cat <<EOF | docker exec -i "${node}" cp /dev/stdin "${REGISTRY_DIR}/hosts.toml" [host."http://${reg_name}:5000"] EOF done # 4. Connect the registry to the cluster network if not already connected # This allows kind to bootstrap the network but ensures they're on the same network if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then docker network connect "kind" "${reg_name}" fi # 5. Document the local registry # https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: local-registry-hosting namespace: kube-public data: localRegistryHosting.v1: | host: "localhost:${reg_port}" help: "https://kind.sigs.k8s.io/docs/user/local-registry/" EOF
该脚本可以在项目的根文件夹中找到。要安装本地docker registry,我们只需要运行这个bash脚本。
关于我为本博文示例提供的代码可能有很多话要说。但是,在这种情况下,我们只关注几个关键方面。让我们从侦听器服务开始,到收集器,再到数据库。当我们在本地运行服务或甚至使用docker-compose
配置来启动容器时,通常,我们使用 DNS 属性名称,这些名称会自动分配为容器名称或我们使用hostname
配置的名称。
使用k8s
,还有一组规则使主机名在整个集群中可用。让我们看一下侦听器和收集器示例:
监听器是使用Spring framework
用Java
开发的应用程序。与所有以这种方式创建的应用程序一样,也有一个application.properties
文件:
spring.application.name=wlsm-listener-service server.port=8080 spring.main.web-application-type=reactive spring.webflux.base-path=/app/v1/listener wslm.url.collector=http://localhost:8081/api/v1/collector
在所有这些属性中,目前需要关注的最重要的属性是wslm.url.collector
属性。使用default
配置,我们可以在本地运行此服务,而无需使用任何容器化环境。但是,在k8s
集群中,我们需要能够访问collector
,为此,我们有一个prod
配置文件,其定义文件为application-prod.properties
:
wslm.url.collector=http://wlsm-collector-deployment.wlsm-namespace.svc.cluster.local:8081/api/v1/collector
此属性尝试访问主机wlsm-collector-deployment.wlsm-namespace.svc.cluster.local
。此文件遵循以下配置:
<Service Name>.<Namespace>.svc.cluster.local
我们有 5 个以点分隔的元素。最后三个是静态的,前两个取决于我们尝试访问的机器。在左侧,我们放置服务名称,后跟命名空间。这对于了解集群内容器如何相互连接非常重要。
代码中最值得关注的部分当然是控制器和服务。控制器如下所示:
@RestController @RequestMapping public class ListenerController { private final ListenerService listenerService; ListenerController(ListenerService listenerService) { this.listenerService = listenerService; } @GetMapping("info") public String info() { return "Listener Service V1"; } @PostMapping("create") public Mono<AnimalLocationDto> sendAnimalLocation( @RequestBody AnimalLocationDto animalLocationDto) { return listenerService.persist(animalLocationDto); } }
该服务如下所示:
@Service public class ListenerService { @Value("${wslm.url.collector:http://localhost:8080}") private String collectorUrl; private final WebClient client = WebClient.create(collectorUrl); HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(); List<AnimalLocationDto> cache = hazelcastInstance.getList("data"); public Mono<AnimalLocationDto> persist(AnimalLocationDto animalLocationDto) { cache.add(animalLocationDto); return client.post() .uri(collectorUrl.concat("/animals")) .contentType(MediaType.APPLICATION_JSON) .bodyValue(animalLocationDto) .retrieve() .bodyToMono(AnimalLocationDto.class); } }
您可能已经注意到,这个第一个应用程序与此存储库中使用Spring Framework
实现的所有应用程序一样,都是响应式的,并且它们都使用netty
而不是tomcat
。目前,我们可以忽略此代码中的hazelcast
使用。这将用于该项目的后续版本。
此时,收集器的工作方式与侦听器完全相同。它目前唯一的职责是将数据从侦听器中继到数据库,为此,收集器只需要知道数据库的确切位置。让我们application.properties file of this project
进行同样的分析:
spring.application.name=wlsm-collector-service server.port=8081 spring.main.web-application-type=reactive spring.webflux.base-path=/api/v1/collector spring.r2dbc.url=r2dbc:postgresql://localhost:5432/wlsm spring.r2dbc.username=admin spring.r2dbc.password=admin spring.data.r2dbc.repositories.naming-strategy=org.springframework.data.relational.core.mapping.BasicRelationalPersistentEntityNamingStrategy spring.data.r2dbc.repositories.naming-strategy.table=org.springframework.data.relational.core.mapping.SnakeCaseNamingStrategy spring.data.r2dbc.repositories.naming-strategy.column=org.springframework.data.relational.core.mapping.SnakeCaseNamingStrategy
这些属性是启动服务所需的最低要求。但是,这只能在本地运行。对于此服务,我们还有prod
配置文件,我们可以在此处的application-prod.properties
中查看它:
spring.r2dbc.url=r2dbc:postgresql://wlsm-database-deployment.wlsm-namespace.svc.cluster.local:5432/wlsm
在这种情况下,数据库连接是指数据库的主机:
wlsm-database-deployment.wlsm-namespace.svc.cluster.local
这又遵循了我们之前看到的相同分析。在左侧,我们看到服务名称,后面是命名空间,并在末尾附加了svc.cluster.local
。
对于此服务,我们还使用了一个控制器和一个服务。控制器如下所示:
@RestController @RequestMapping class CollectorController( val collectorService: CollectorService ) { @PostMapping("animals") suspend fun listenAnimalLocation(@RequestBody animalLocationDto: AnimalLocationDto): AnimalLocationDto = run { collectorService.persist(animalLocationDto) animalLocationDto } }
该服务如下所示:
@Service class CollectorService( val applicationEventPublisher: ApplicationEventPublisher ) { fun persist(animalLocationDto: AnimalLocationDto) = applicationEventPublisher.publishEvent(AnimalLocationEvent(animalLocationDto)) }
该服务使用一个名为applicationEventPublisher
的事件发布者,它遵循事件流架构,稍后在此事件侦听器中处理,我们可以很容易地看到它使用r2dbc
来保持反应式架构实现范例:
@Service class EventHandlerService( val animalLocationDao: AnimalLocationDao ) { @EventListener fun processEvent(animalLocationEvent: AnimalLocationEvent){ println(animalLocationEvent) runBlocking(Dispatchers.IO) { animalLocationDao.save(animalLocationEvent.animalLocationDto.toEntity()) } } }
使用k8s
进行部署通常是一个非常简单的任务。但是,了解我们的服务所需的配置也很重要。例如,让我们看一下侦听器的实现:
apiVersion: v1 kind: Namespace metadata: name: wlsm-namespace labels: kuma.io/sidecar-injection: enabled --- apiVersion: apps/v1 kind: Deployment metadata: name: wlsm-listener namespace: wlsm-namespace spec: replicas: 1 selector: matchLabels: app: wlsm-listener template: metadata: labels: app: wlsm-listener spec: containers: - name: wlsm-listener-service image: localhost:5001/wlsm-listener-service:latest imagePullPolicy: Always ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: wlsm-listener-deployment namespace: wlsm-namespace spec: selector: app: wlsm-listener ports: - protocol: TCP appProtocol: http port: 8080
此配置中有三个块。第一个块是命名空间块。命名空间配置对于允许 Kuma 能够注入应用策略所需的 Envoy Sidecar 至关重要。如果没有定义的命名空间, kuma
将无法执行此操作。配置 kuma 时我们需要注意的另一件事是,命名空间必须包含 kuma 将识别的正确标签:
kuma.io/sidecar-injection: enabled.
具有正确标签的命名空间定义对于 Kuma 的运行至关重要。在第二个块中,我们找到了部署的定义。这就是我们定义 Pod 在 Kubernetes 集群中的部署方式的方式。这里需要重点关注的是image
、 imagePullPolicy
和containerPort
是我们正在使用的 Docker 镜像的完整标签。
使用kind
创建的 Docker 注册表配置的端口是 5001,它包含在我们镜像的标签中。它既可以用作标签,也可以用作与 Docker 注册表的连接。这样,我们就可以提取镜像并创建容器以在 Kubernetes 环境中运行。
但是,当然,为了能够使用图像,我们需要创建它们,为此,让我们看看如何在listener
示例和database
示例中完成此操作。 listener
的 docker 映像定义如下:
FROM eclipse-temurin:21-jdk-alpine WORKDIR /root ENV LANG=C.UTF-8 COPY entrypoint.sh /root COPY build/libs/wlsm-listener-service.jar /root/wlsm-listener-service.jar ENTRYPOINT ["/root/entrypoint.sh"]
这一切都从名为eclipse-temurin:21-jdk-alpine
基础镜像开始。之后,我们只需复制通过构建项目创建的 jar,然后将其复制到我们的镜像中。在此之前,我们还将entrypoint.sh
复制到容器中并定义ENTRYPOINT
以使用它。 entrypoint
只需像这样调用 jar:
#!/usr/bin/env sh java -jar -Dspring.profiles.active=prod wlsm-listener-service.jar
database
服务非常不同,因为它使用了一些开源且可在线获取的脚本:
FROM postgres:15 COPY . /docker-entrypoint-initdb.d COPY ./multiple /docker-entrypoint-initdb.d/multiple ENV POSTGRES_USER=admin ENV POSTGRES_PASSWORD=admin ENV POSTGRES_MULTIPLE_DATABASES=wlsm EXPOSE 5432
该脚本将以下文件和文件夹复制到 docker init 目录: create-multiple-postgresql-databases.sh
和multiple
。最后,我们只需定义这些脚本中使用的变量来定义我们的数据库和用户名/密码组合。
该数据库是使用以下模式创建的:
CREATE TABLE families( id uuid DEFAULT gen_random_uuid(), name VARCHAR(100), PRIMARY KEY(id) ); CREATE TABLE genuses( id uuid DEFAULT gen_random_uuid(), name VARCHAR(100), PRIMARY KEY(id) ); CREATE TABLE species( id uuid DEFAULT gen_random_uuid(), common_name VARCHAR(100), family uuid, genus uuid, PRIMARY KEY(id), CONSTRAINT fk_species FOREIGN KEY(family) REFERENCES families(id), CONSTRAINT fk_genus FOREIGN KEY(genus) REFERENCES genuses(id) ); CREATE TABLE animal ( id uuid DEFAULT gen_random_uuid(), name VARCHAR(100), species_id uuid, PRIMARY KEY(id), CONSTRAINT fk_species FOREIGN KEY(species_id) REFERENCES species(id) ); CREATE TABLE animal_location ( id uuid DEFAULT gen_random_uuid(), animal_id uuid, latitude BIGINT, longitude BIGINT, PRIMARY KEY(id), CONSTRAINT fk_animal FOREIGN KEY(animal_id) REFERENCES animal(id) );
作为数据示例,我们将注册一种名为piquinho
的动物。Piquinho 只是一种环游世界的旅行信天翁的名字,它身上附有传感器,我们正在读取传感器发送给我们的数据。有两个定义物种的表。即物种和定义物种的属。这些是families
和genuses
表。
表species
定义了动物所属的物种。最后,我们在同名表中定义一种animal
,其中注册了动物的物种和名称。数据库如下所示:
为了构建、创建图像并启动我们的项目,我们可以运行Makefile
中提供的以下命令:
make make create-and-push-images make k8s-apply-deployment
第一个 make 只是一个gradle build
命令。第二个命令使用了变量:
MODULE_TAGS := aggregator \ collector \ listener \ management \ database
跑步:
docker images "*/*wlsm*" --format '{{.Repository}}' | xargs -I {} docker rmi {} @for tag in $(MODULE_TAGS); do \ export CURRENT=$(shell pwd); \ echo "Building Image $$image..."; \ cd "wlsm-"$$tag"-service"; \ docker build . --tag localhost:5001/"wlsm-"$$tag"-service"; \ docker push localhost:5001/"wlsm-"$$tag"-service"; \ cd $$CURRENT; \ done
这只是遍历每个模块并使用标准通用命令(该命令根据MODULE_TAGS
中给出的每个值进行更改)来创建映像并将其推送到端口 5001 上的本地注册表。按照相同的策略,我们可以使用第三个命令来部署我们的 pod。第三个命令使用不同的循环,如下所示:
@for tag in $(MODULE_TAGS); do \ export CURRENT=$(shell pwd); \ echo "Applying File $$tag..."; \ cd "wlsm-"$$tag"-service"; \ kubectl apply -f $$tag-deployment.yaml --force; \ cd $$CURRENT; \ done
在这种情况下,它将每个部署脚本应用于每个服务。如果我们运行命令kubectl get pods --all-namespaces
,我们应该得到这个输出:
NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-76f75df574-dmt5m 1/1 Running 0 5m21s kube-system coredns-76f75df574-jtrfr 1/1 Running 0 5m21s kube-system etcd-kind-control-plane 1/1 Running 0 5m38s kube-system kindnet-7frts 1/1 Running 0 5m21s kube-system kube-apiserver-kind-control-plane 1/1 Running 0 5m36s kube-system kube-controller-manager-kind-control-plane 1/1 Running 0 5m36s kube-system kube-proxy-njzvl 1/1 Running 0 5m21s kube-system kube-scheduler-kind-control-plane 1/1 Running 0 5m36s kuma-system kuma-control-plane-5f47fdb4c6-7sqmp 1/1 Running 0 17s local-path-storage local-path-provisioner-7577fdbbfb-5qnxr 1/1 Running 0 5m21s wlsm-namespace wlsm-aggregator-64fc4599b-hg9qw 1/1 Running 0 4m23s wlsm-namespace wlsm-collector-5d44b54dbc-swf84 1/1 Running 0 4m23s wlsm-namespace wlsm-database-666d794c87-pslzp 1/1 Running 0 4m22s wlsm-namespace wlsm-listener-7bfbcf799-f44f5 1/1 Running 0 4m23s wlsm-namespace wlsm-management-748cf7b48f-8cjh9 1/1 Running 0 4m23s
此时我们应该观察到kuma-control-plane
、 kube-controller-manager
和在我们自己的自定义wlsm-namespace
中运行的所有服务的存在。我们的集群与外界隔离,为了能够访问不同的端口,我们需要为想要访问的每个 pod 创建port-forwarding
。为此,我们可以在单独的选项卡中发出以下命令:
我们还可以通过查看k9s
来看一下这一点:
kubectl port-forward svc/wlsm-collector-deployment -n wlsm-namespace 8081:8081 kubectl port-forward svc/wlsm-listener-deployment -n wlsm-namespace 8080:8080 kubectl port-forward svc/wlsm-database-deployment -n wlsm-namespace 5432:5432 kubectl port-forward svc/kuma-control-plane -n kuma-system 5681:5681
为了运行该应用程序,我们应该打开所有端口,当所有端口都打开时,我们应该在屏幕上看到如下内容:
我们可以使用localhost
和端口5432
连接到数据库。连接字符串如下: jdbc:postgresql://localhost:5432/wlsm
。然后,为了访问它,我们使用用户名/密码组合admin
/ admin
。
在进行任何测试之前,我们需要做的第一件事就是知道Piquinho
的 id,我们可以通过使用 Intellij 数据库工具来完成此操作,如下所示:
在项目的根文件夹中,有一个名为test-requests.http
文件。这是一个针对我们开放的端口创建 REST 请求的临时文件:
### GET http://localhost:8080/app/v1/listener/info ### POST http://localhost:8080/app/v1/listener/create Content-Type: application/json { "animalId": "2ffc17b7-1956-4105-845f-b10a766789da", "latitude": 52505252, "longitude": 2869152 } ### POST http://localhost:8081/api/v1/collector/animals Content-Type: application/json { "animalId": "2ffc17b7-1956-4105-845f-b10a766789da", "latitude": 52505252, "longitude": 2869152 }
为了能够使用此文件,我们只需要替换 ID,在本例中,从2ffc17b7-1956-4105-845f-b10a766789da
替换为d5ad0824-71c0-4786-a04a-ac2b9a032da4
。在这种情况下,我们可以从收集器或侦听器发出请求。两个请求都应该有效,之后我们应该看到每个请求的这种响应:
{ "animalId": "d5ad0824-71c0-4786-a04a-ac2b9a032da4", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T001024.200.json Response code: 200 (OK); Time: 7460ms (7 s 460 ms); Content length: 91 bytes (91 B)
由于两个端口都已打开,并且此时它们共享相同的有效负载类型,因此我们可以向侦听器和收集器执行相同的请求。发出这两个请求后,我们应该在表animal_locations
中找到结果:
因此,这仅确认集群正在正确运行,现在,我们准备使用 Kuma 网格测试策略。
MeshTrafficPermission
是我们可以在 Kuma 中选择的功能之一,而且它可能是最常用的功能。
但首先,让我们花点时间探索一下 Kuma 控制平面。启用所有转发后,我们只需转到localhost:5681/gui并可视化我们的 Kuma 网格。在主页上,我们应该看到类似以下内容:
目前还没有什么可看的,但是现在让我们应用MeshTrafficPermission
:
echo "apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: kuma-system name: mtp spec: targetRef: kind: Mesh from: - targetRef: kind: Mesh default: action: Allow" | kubectl apply -f -
一旦我们应用这个,我们应该得到这样的响应: meshtrafficpermission.kuma.io/mtp created
。
在集群设置方面,应用网格不会带来太大变化。它的作用是让我们设置流量路由策略。
我们可以选择的东西有很多,但最明显的选择之一是mTLS.
也称为相互 TLS,简而言之,这意味着证书被相互接受和验证,以便在各方之间建立身份并建立加密数据流量。
使用这个简单的Mesh
配置我们可以自动完成这一点:
echo "apiVersion: kuma.io/v1alpha1 kind: Mesh metadata: name: default spec: mtls: enabledBackend: ca-1 backends: - name: ca-1 type: builtin" | kubectl apply -f -
应用此策略后,我们可能会遇到类似这样的警告:
Warning: resource meshes/default is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
目前,我们可以忽略这个警告。
现在到了有趣的部分,我们要做的第一件事就是禁用所有 pod 之间的所有流量:
echo " apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: wlsm-namespace name: mtp spec: targetRef: kind: Mesh from: - targetRef: kind: Mesh default: action: Deny" | kubectl apply -f -
在我们收到确认消息meshtrafficpermission.kuma.io/mtp configured
后,如果我们尝试使用任何端口转发发出任何请求,我们将得到:
HTTP/1.1 500 Internal Server Error Content-Type: application/json Content-Length: 133 { "timestamp": "2024-04-12T07:09:26.718+00:00", "path": "/create", "status": 500, "error": "Internal Server Error", "requestId": "720749ce-56" } Response file saved. > 2024-04-12T090926.500.json Response code: 500 (Internal Server Error); Time: 10ms (10 ms); Content length: 133 bytes (133 B)
这意味着 Pod 之间的所有流量都被拒绝。我们现在拥有一个内部系统,可以防止组织内可能的不良行为者,但我们现在也阻止了所有 Pod 之间的流量。因此, mTLS
是一件好事,但阻止所有流量则完全不是好事。
实现此目的的方法很简单,就是对DENY
all 规则进行例外处理,为此,我们需要一个策略来允许侦听器和收集器以及收集器和数据库之间的通信。让我们从收集器和数据库之间的通信开始:
echo " apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: kuma-system name: wlsm-database spec: targetRef: kind: MeshService name: wlsm-database-deployment_wlsm-namespace_svc_5432 from: - targetRef: kind: MeshService name: wlsm-collector-deployment_wlsm-namespace_svc_8081 default: action: Allow" | kubectl apply -f -
在这种情况下,我们所做的是允许数据流量从targetRef
收集器流向targetRef
数据库。如果您不知道这一点,也许重要的是注意 Kuma 如何解释name
,就像hostname
创建一样,它也用于功能目的。
构建这些name
的通用方法是这样的:
<service name>_<namespace>_svc_<service port>
在这种情况下,分隔符是下划线,通过这种方式创建名称可以让Kuma
确切地知道什么是允许的。在这种情况下,如果我们应用此策略,我们将能够在收到此响应后向收集器发送请求: meshtrafficpermission.kuma.io/wlsm-database created
。
在进行这些操作时,响应现在应该是200
,确认位置记录已发送给收集器:
POST http://localhost:8081/api/v1/collector/animals HTTP/1.1 200 OK Content-Type: application/json Content-Length: 91 { "animalId": "a3a1bc1c-f284-4876-a84f-f75184b6998f", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T091754.200.json Response code: 200 (OK); Time: 1732ms (1 s 732 ms); Content length: 91 bytes (91 B)
但是,我们仍然没有定义侦听器和收集器之间的流量的例外情况,因此以这种方式发出请求将导致以下结果:
HTTP/1.1 500 Internal Server Error Content-Type: application/json Content-Length: 133 { "timestamp": "2024-04-12T07:18:54.149+00:00", "path": "/create", "status": 500, "error": "Internal Server Error", "requestId": "e8973d33-62" } Response file saved. > 2024-04-12T091854-1.500.json Response code: 500 (Internal Server Error); Time: 10ms (10 ms); Content length: 133 bytes (133 B)
这当然是意料之中的。现在让我们对此数据流量应用另一项策略:
echo " apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: kuma-system name: wlsm-collector spec: targetRef: kind: MeshService name: wlsm-collector-deployment_wlsm-namespace_svc_8081 from: - targetRef: kind: MeshService name: wlsm-listener-deployment_wlsm-namespace_svc_8080 default: action: Allow" | kubectl apply -f -
现在就可以从侦听器向收集器执行请求:
POST http://localhost:8080/app/v1/listener/create HTTP/1.1 200 OK Content-Type: application/json Content-Length: 91 { "animalId": "a3a1bc1c-f284-4876-a84f-f75184b6998f", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T092039-2.200.json Response code: 200 (OK); Time: 14ms (14 ms); Content length: 91 bytes (91 B)
最后,仅提供另一个功能作为示例,我们还可以使用另一个名为MeshFaultInjection
的功能,该功能在使用Kuma
执行测试时非常有用。例如,我们可以模拟网格中的潜在问题,并检查错误处理是否正确完成。
我们还可以检查其他事项,例如我们配置的断路器如何对故障连接或高速率请求做出反应。
那么,让我们尝试一下。应用MeshFaultInjection
的一种方法如下:
echo " apiVersion: kuma.io/v1alpha1 kind: MeshFaultInjection metadata: name: default namespace: kuma-system labels: kuma.io/mesh: default spec: targetRef: kind: MeshService name: wlsm-collector-deployment_wlsm-namespace_svc_8081 from: - targetRef: kind: MeshService name: wlsm-listener-deployment_wlsm-namespace_svc_8080 default: http: - abort: httpStatus: 500 percentage: 50" | kubectl apply -f -
使用此策略,我们可以说从侦听器发出并传入收集器的流量有 50% 的成功率。请求结果是不可预测的,因此在应用此策略后,我们可能会预期侦听器端点的请求出现错误或成功。
POST http://localhost:8080/app/v1/listener/create HTTP/1.1 500 Internal Server Error Content-Type: application/json Content-Length: 133 { "timestamp": "2024-04-12T07:28:00.008+00:00", "path": "/create", "status": 500, "error": "Internal Server Error", "requestId": "2206f29e-78" } Response file saved. > 2024-04-12T092800.500.json Response code: 500 (Internal Server Error); Time: 8ms (8 ms); Content length: 133 bytes (133 B)
POST http://localhost:8080/app/v1/listener/create HTTP/1.1 200 OK Content-Type: application/json Content-Length: 91 { "animalId": "a3a1bc1c-f284-4876-a84f-f75184b6998f", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T092819.200.json Response code: 200 (OK); Time: 13ms (13 ms); Content length: 91 bytes (91 B)
最后,出于兴趣,我们可以看看我们的animal_location
表现在是什么样子:
我希望您能够读完本文,并且能够在您的机器上运行集群。无论如何,感谢您阅读本文,并花一点时间来了解和学习更多关于 Kuma 的知识。我个人认为Kuma
的用途很好,前景光明,因为它可以配置和更精细地控制我们的网络和环境。
它的企业版本Kong-Mesh
似乎相当完善。Kuma 是开源的。它非常适合测试,也非常适合企业。我发现网格这个主题非常有趣,我认为Kuma
提供了一种很好的方式来了解网格的工作原理,并让我们了解如何更好地控制网络内的数据流。
如果我们想要查看服务状态,我们可以直接转到此localhost
位置的Kuma
控制平面: http://localhost:5681/gui/meshes/default/services?page=1&size=50 :
在 Kuma 控制平面中,我们还可以查看已安装的策略、检查 Pod 的状态、监视后台正在发生的事情,并且通常只需概览一下网格中正在发生的事情及其配置方式。我邀请您浏览该应用程序,看看您是否可以检查已安装策略的状态。Kuma 控制平面(又称 GUI)的设计非常精确,易于理解和跟踪我们的网格。
我还在我的JESPROTECH YouTube 频道上制作了有关它的视频: