利用Kubernetes特性简化系统架构
1. 实现Kubernetes特性以简化系统架构
在简化系统架构时,我们可以利用Kubernetes的一些特性。以下是一个Ingress对象的配置示例:
annotations:
cert-manager.io/issuer: selfsigned
spec:
tls:
- hosts:
- "minikube.me"
secretName: tls-certificate
rules:
- host: "minikube.me"
http:
paths:
- path: /oauth2
pathType: Prefix
backend:
service:
name: auth-server
port:
name: http
- path: /product-composite
pathType: Prefix
backend:
service:
name: product-composite
port:
name: http
- path: /actuator/health
pathType: Prefix
backend:
service:
name: product-composite
port:
name: http
这里部分路由规则已被移除,以提高可读性。接下来,我们需要了解包含证书的Secret是如何创建的。
2. 自动化证书供应
cert-manager
工具(https://cert-manager.io/docs/)是Kubernetes的证书管理控制器,它可以促进证书的自动创建、供应和轮换。它支持多种证书来源,例如:
- 符合RFC8555(https://tools.ietf.org/html/rfc8555)的ACME服务器,如Let’s Encrypt(https://letsencrypt.org)
- HashiCorp Vault PKI Secrets Engine(https://www.vaultproject.io/docs/secrets/pki)
- 由
cert-manager
自身颁发的自签名证书
完整的可用颁发者列表请参考:https://cert-manager.io/docs/configuration/ 。由于自签名证书不需要与任何外部资源进行通信,因此它们是开发期间使用的理想选择。
在Kubernetes集群中安装
cert-manager
后,至少需要注册一个颁发者。颁发者可以是命名空间本地的,也可以是集群范围可访问的。我们将使用在现有
hands-on
命名空间中注册的本地颁发者。
环境图表
dev-env
和
prod-env
负责注册合适的颁发者,两个环境都将使用自签名颁发者。在通用图表中添加了一个名为
_issuer.yaml
的模板,内容如下:
{{- define "common.issuer" -}}
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned
spec:
selfSigned: {}
{{- end -}}
apiVersion
和
kind
字段指定这是由
cert-manager
定义的颁发者,其名称设置为
selfsigned
。在上面替换Spring Cloud Gateway所需的更改部分,我们看到了如何使用这个名称来注释Ingress清单:
ingress:
annotations:
cert-manager.io/issuer: selfsigned
tls:
secretName: tls-certificate
在生产环境中使用
cert-manager
通常需要使用像Let’s Encrypt这样的颁发者,它可以为API客户端(如Web浏览器和外部系统)信任的外部API颁发证书。
只要满足上述配置,
cert-manager
就会开始为Ingress对象提供证书。
cert-manager
会监听带有
cert-manager.io/issuer
注释的Ingress对象的注册,并使用注释值中引用的颁发者(这里是
selfsigned
)开始颁发证书。
cert-manager
工具将使用颁发者创建证书,并将其存储在Ingress对象指定名称的Secret中,在我们的例子中,名称设置为
tls-certificate
。还会创建一个同名的
Certificate
对象,其中包含管理信息,如
cert-manager
何时需要更新证书。
由于命名模板
common.issuer
不接受任何配置,因此在
dev-env
和
prod-env
图表中应用它所需的只是在每个图表中添加一个使用该命名模板的模板。这个模板名为
issuer.yaml
,内容如下:
{{- template "common.issuer" . -}}
有了这些,我们就拥有了用原生Kubernetes组件和
cert-manager
替换Spring Cloud Config Server和Gateway所需的一切。接下来,我们将进行部署和测试!
3. 使用Kubernetes ConfigMaps、Secrets、Ingress和cert-manager进行测试
在完成上述更改后,我们准备测试用Kubernetes ConfigMaps、Secrets、Ingress对象和
cert-manager
替换Spring Cloud Config Server和Spring Cloud Gateway后的系统架构。与之前使用Spring Cloud Gateway作为边缘服务器时一样,外部API将通过HTTPS进行保护。在这次部署中,将由Ingress控制器使用
cert-manager
提供的证书通过HTTPS保护外部API。
Ingress控制器在Minikube实例的默认HTTPS端口443上公开。在运行Minikube实例作为Docker容器的主机上,我们通过
localhost
与Minikube实例进行通信。创建Minikube实例时,配置了从
localhost
的8443端口到Minikube实例的443端口的端口转发。执行
minikube addons enable ingress
命令时安装了Ingress控制器。
一个有趣的问题是,Ingress控制器如何在Minikube实例上使用端口443?我们知道
NodePort
类型的服务可以分配从30000开始的端口,那么Ingress控制器如何使用HTTPS的标准端口443呢?
Ingress控制器由
ingress-nginx
命名空间中的
ingress-nginx-controller
部署对象组成。答案是该部署对象使用
hostPort
配置其Pod,将Kubernetes主机(即Minikube实例)中的443端口映射到Pod中运行的容器的443端口。部署对象定义的核心部分如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-nginx-controller
spec:
template:
spec:
containers:
image: registry.k8s.io/ingress-nginx/controller:v1.5.1
ports:
- containerPort: 443
hostPort: 443
部署对象使用的命令与之前部署微服务到Kubernetes时使用的命令类型相同。在本节中,我们还将安装
cert-manager
并在
/etc/hosts
文件中为
minikube.me
主机名添加一个条目。
执行以下步骤部署系统架构并验证其按预期工作:
1. 在
cert-manager
命名空间中安装
cert-manager
并等待部署完成。在安装
cert-manager
之前,我们需要添加其Helm仓库。运行以下命令:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--create-namespace \
--namespace cert-manager \
--version v1.11.0 \
--set installCRDs=true \
--wait
使用以下命令验证
cert-manager
命名空间中有三个Pod就绪:
kubectl get pods --namespace cert-manager
-
通过在
/etc/hosts文件中添加一行,将minikube.me映射到我们可以用来访问Minikube实例的IP地址。运行以下命令:
sudo bash -c "echo 127.0.0.1 minikube.me | tee -a /etc/hosts"
使用
cat /etc/hosts
命令验证结果,期望看到包含
127.0.0.1 minikube.me
的行。如果
/etc/hosts
文件中包含多个
minikube.me
的行(例如,之前尝试的结果),则需要手动删除旧的行。
3. 从源代码构建Docker镜像,步骤如下:
cd $BOOK_HOME/Chapter17
eval $(minikube docker-env -u)
./gradlew build
eval $(minikube docker-env)
docker-compose build
-
解析Helm图表依赖:
-
首先,更新
components文件夹中的依赖:
-
首先,更新
for f in kubernetes/helm/components/*; do helm dep up $f; done
- 然后,更新 `environments` 文件夹中的依赖:
for f in kubernetes/helm/environments/*; do helm dep up $f; done
-
将
hands-on命名空间设置为kubectl的默认命名空间:
kubectl config set-context $(kubectl config current-context) --namespace=hands-on
-
在单独的终端窗口中,运行以下命令监视
cert-manager如何创建证书对象:
kubectl get certificates -w --output-watch-events
- 使用Helm部署系统架构并等待所有部署完成:
helm install hands-on-dev-env \
kubernetes/helm/environments/dev-env \
-n hands-on \
--create-namespace \
--wait
-
注意在部署期间
cert-manager如何创建证书。期望从kubectl get certificates命令获得如下输出。 -
使用
Ctrl + C停止kubectl get certificates命令。 - 运行测试以验证系统架构按预期工作:
HOST=minikube.me PORT=8443 USE_K8S=true ./test-em-all.bash
4. 证书轮换
在完成开发环境的测试之前,我们来尝试
cert-manager
创建的证书对象,并看看如何使用它来影响证书的保留时间。
通过执行以下命令开始熟悉证书对象:
kubectl describe cert tls-certificate
在该命令的输出末尾,我们会找到有关证书有效期的信息。可以看到证书的有效期为90天(
Not After - Not Before
),并且
cert-manager
将在60天后尝试更新它(
Renewal Time - Not Before
)。由于我们使用的自签名颁发者不允许任何配置,这些是
cert-manager
使用的默认值:90天的有效期,并且在有效期的2/3后启动更新过程。
但我们不想等待60天来观察证书的更新。如果我们研究证书对象的API规范(https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.Certificate),会在
spec
部分找到一个有趣的字段
renewBefore
,它可以用于指定
cert-manager
应多早开始更新过程。如果我们希望证书每分钟更新一次,可以将
renewBefore
指定为
90天 - 1分钟 = 90*24小时 - 1分钟 = 2160小时 - 1分钟 = 2159小时59分钟
。
在单独的终端窗口中启动
kubectl get events -w
命令,并运行以下补丁命令将
renewBefore
字段添加到证书中:
kubectl patch certificate tls-certificate --type=json \
-p='[{"op": "add", "path": "/spec/renewBefore", "value": "2159h59m"}]'
在1分钟内,
get events
命令应该开始报告证书更新情况。对于每次更新,
get events
命令应该打印相关信息。
等待几分钟以验证证书是否每分钟更新一次。如果您想知道下一次更新的时间,可以执行以下命令:
kubectl get cert tls-certificate -o json | jq .status.renewalTime
它应该返回一个日期,如
2023-05-07T05:58:40Z
。
如果您不再需要自定义保留时间,可以使用以下命令删除
renewBefore
字段:
kubectl patch certificate tls-certificate --type=json \
-p='[{"op": "remove", "path": "/spec/renewBefore"}]'
完成使用
dev-env
图表部署的系统架构的测试后,我们可以使用以下命令删除该系统架构:
kubectl delete namespace hands-on
5. 部署到Kubernetes进行预生产和生产
使用
prod-env
图表部署到预生产和生产环境的步骤与之前部署微服务到Kubernetes的步骤相同,现简要概括如下:
1. 在Kubernetes外部启动MySQL、MongoDB和RabbitMQ:
eval $(minikube docker-env)
docker-compose up -d mongodb mysql rabbitmq
-
为Docker镜像添加
v1版本标签:
docker tag hands-on/auth-server hands-on/auth-server:v1
docker tag hands-on/product-composite-service hands-on/product-composite-service:v1
docker tag hands-on/product-service hands-on/product-service:v1
docker tag hands-on/recommendation-service hands-on/recommendation-service:v1
docker tag hands-on/review-service hands-on/review-service:v1
-
使用
prod-envHelm图表部署微服务:
helm install hands-on-prod-env \
kubernetes/helm/environments/prod-env \
-n hands-on --create-namespace \
--wait
- 运行测试以验证系统架构按预期工作:
HOST=minikube.me PORT=8443 USE_K8S=true ./test-em-all.bash
完成测试后,使用以下命令清理Kubernetes和Docker中创建的资源:
1. 如果
kubectl get cert -w
和
kubectl get events -w
命令仍在运行,使用
Ctrl + C
停止它们。
2. 使用以下命令删除Kubernetes中的命名空间:
kubectl delete namespace hands-on
- 使用以下命令停止MySQL、MongoDB和RabbitMQ:
eval $(minikube docker-env)
docker-compose down
6. 验证微服务在无Kubernetes环境下的运行情况
在前面的操作中,我们看到了Kubernetes平台的特性,如ConfigMaps、Secrets、Services和Ingress对象如何简化开发协作式微服务架构的工作。但重要的是要确保微服务的源代码在功能上不依赖于该平台。避免这种锁定使得在未来需要时可以轻松切换到其他平台,更改平台不应需要更改源代码,而只需更改微服务的配置。
使用Docker Compose测试微服务并运行
test-em-all.bash
验证脚本将确保它们在无Kubernetes的情况下从功能角度正常工作。在无Kubernetes的情况下运行微服务时,我们将缺乏Kubernetes提供的非功能特性,例如监控、扩展和重启容器。
使用Docker Compose时,我们将替换以下Kubernetes特性:
| Kubernetes特性 | Docker Compose替换方案 |
| — | — |
| ConfigMaps | 使用卷将配置文件直接从主机文件系统映射 |
| Secrets | 将敏感信息(如凭据)保存在Docker Compose的
.env
文件中 |
| Ingress | 使用Spring Cloud Gateway |
| Services | 将客户端使用的主机名直接映射到容器的主机名,即不进行服务发现,也无法扩展容器 |
这种使用Docker Compose的方式在非功能方面与使用Kubernetes相比存在显著劣势,但考虑到Docker Compose仅用于运行功能测试,这是可以接受的。
7. Docker Compose文件的更改
为了在Kubernetes外部使用Docker Compose运行微服务,对
docker-compose*.yml
文件进行了以下更改:
- 移除了配置服务器定义
- 移除了以下配置服务器环境变量的使用:
CONFIG_SERVER_USR
和
CONFIG_SERVER_PWD
- 将
config-repo
文件夹作为卷映射到每个需要从配置仓库读取配置文件的容器中
- 定义
SPRING_CONFIG_LOCATION
环境变量,指向配置仓库中的配置文件
- 将敏感信息(如凭据和TLS证书中的密码)存储在Docker Compose的
.env
文件中
- 根据
.env
文件中定义的变量,定义用于访问资源管理器的凭据环境变量
例如,
docker-compose.yml
中产品微服务的配置如下:
product:
build: microservices/product-service
image: hands-on/product-service
environment:
- SPRING_PROFILES_ACTIVE=docker
- SPRING_CONFIG_LOCATION=file:/config-repo/application.yml,file:/config-repo/product.yml
- SPRING_RABBITMQ_USERNAME=${RABBITMQ_USR}
- SPRING_RABBITMQ_PASSWORD=${RABBITMQ_PWD}
- SPRING_DATA_MONGODB_AUTHENTICATION_DATABASE=admin
- SPRING_DATA_MONGODB_USERNAME=${MONGODB_USR}
- SPRING_DATA_MONGODB_PASSWORD=${MONGODB_PWD}
volumes:
- $PWD/config-repo:/config-repo
对上述源代码的解释如下:
-
config-repo
文件夹作为卷映射到容器的
/config-repo
路径
-
SPRING_CONFIG_LOCATION
环境变量告诉Spring在哪里找到属性文件,这里是
/config-repo/application.yml
和
/config-repo/product.yml
文件
- 访问RabbitMQ和MongoDB的凭据作为环境变量,基于
.env
文件中的内容设置
上述源代码中引用的凭据在
.env
文件中定义如下:
RABBITMQ_USR=rabbit-user-prod
RABBITMQ_PWD=rabbit-pwd-prod
MONGODB_USR=mongodb-user-prod
MONGODB_PWD=mongodb-pwd-prod
8. 使用Docker Compose进行测试
要使用Docker Compose进行测试,我们将使用Docker Desktop而不是Minikube。执行以下步骤:
1. 运行以下命令,使Docker客户端使用Docker Desktop而不是Minikube:
eval $(minikube docker-env --unset)
- 为避免8443端口冲突,停止Minikube实例:
minikube stop
- 使用以下命令在Docker Desktop中构建Docker镜像:
docker-compose build
- 使用RabbitMQ(每个主题一个分区)运行测试:
COMPOSE_FILE=docker-compose.yml ./test-em-all.bash start stop
- 测试应先启动所有容器,运行测试,最后停止所有容器。期望输出类似于我们在之前章节中看到的内容(为提高可读性已简化输出)。
- 可选地,使用每个主题多个分区的RabbitMQ运行测试:
COMPOSE_FILE=docker-compose-partitions.yml ./test-em-all.bash start stop
期望输出与前面的测试类似。
7. 或者,使用每个主题多个分区的Kafka运行测试:
COMPOSE_FILE=docker-compose-kafka.yml ./test-em-all.bash start stop
通过以上步骤,我们可以全面测试微服务在不同环境下的运行情况,确保其功能的稳定性和可移植性。
利用Kubernetes特性简化系统架构
8. 使用Docker Compose进行测试(续)
使用Docker Compose进行测试的流程可以用以下mermaid流程图表示:
graph LR
A[设置Docker客户端使用Docker Desktop] --> B[停止Minikube实例]
B --> C[构建Docker镜像]
C --> D[使用RabbitMQ单分区测试]
D --> E{是否进行多分区测试}
E -- 是 --> F[使用RabbitMQ多分区测试]
E -- 否 --> G{是否使用Kafka测试}
F --> G
G -- 是 --> H[使用Kafka多分区测试]
G -- 否 --> I[测试结束]
在测试过程中,每个步骤都有其重要性。首先,设置Docker客户端使用Docker Desktop是为了确保测试环境的一致性。停止Minikube实例则是为了避免端口冲突,保证测试的顺利进行。构建Docker镜像是为后续的测试提供基础,只有镜像构建成功,才能启动容器进行测试。
在选择不同的消息队列进行测试时,我们可以根据实际需求来决定。使用RabbitMQ单分区测试可以验证系统在基本消息传递场景下的功能。而多分区测试则可以模拟更复杂的生产环境,测试系统的扩展性和负载均衡能力。使用Kafka进行测试则可以验证系统在高吞吐量场景下的性能。
9. 总结与对比
下面我们将Kubernetes和Docker Compose在微服务部署和测试中的使用进行一个总结对比:
| 特性 | Kubernetes | Docker Compose |
|---|---|---|
| 配置管理 | 使用ConfigMaps和Secrets | 使用卷和.env文件 |
| 服务发现 | 有内置的服务发现机制 | 手动映射主机名,无服务发现 |
| 扩展性 | 支持水平和垂直扩展 | 无法方便地进行扩展 |
| 非功能特性 | 提供监控、扩展、重启等功能 | 缺乏这些非功能特性 |
| 适用场景 | 生产环境和大规模测试 | 功能测试 |
从这个表格中可以看出,Kubernetes在功能和扩展性上具有明显优势,适合用于生产环境和大规模的测试。而Docker Compose则更适合用于功能测试,虽然在非功能方面存在劣势,但可以快速搭建测试环境,验证微服务的基本功能。
10. 操作步骤回顾
为了方便大家回顾,我们将前面提到的操作步骤进行一个整理:
Kubernetes部署和测试步骤
:
1. 安装
cert-manager
:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--create-namespace \
--namespace cert-manager \
--version v1.11.0 \
--set installCRDs=true \
--wait
- 映射主机名:
sudo bash -c "echo 127.0.0.1 minikube.me | tee -a /etc/hosts"
- 构建Docker镜像:
cd $BOOK_HOME/Chapter17
eval $(minikube docker-env -u)
./gradlew build
eval $(minikube docker-env)
docker-compose build
- 解析Helm图表依赖:
for f in kubernetes/helm/components/*; do helm dep up $f; done
for f in kubernetes/helm/environments/*; do helm dep up $f; done
- 设置默认命名空间:
kubectl config set-context $(kubectl config current-context) --namespace=hands-on
- 监视证书创建:
kubectl get certificates -w --output-watch-events
- 部署系统架构:
helm install hands-on-dev-env \
kubernetes/helm/environments/dev-env \
-n hands-on \
--create-namespace \
--wait
- 运行测试:
HOST=minikube.me PORT=8443 USE_K8S=true ./test-em-all.bash
Docker Compose测试步骤
:
1. 设置Docker客户端使用Docker Desktop:
eval $(minikube docker-env --unset)
- 停止Minikube实例:
minikube stop
- 构建Docker镜像:
docker-compose build
- 运行测试:
COMPOSE_FILE=docker-compose.yml ./test-em-all.bash start stop
- 可选的多分区测试:
COMPOSE_FILE=docker-compose-partitions.yml ./test-em-all.bash start stop
- 可选的Kafka测试:
COMPOSE_FILE=docker-compose-kafka.yml ./test-em-all.bash start stop
通过以上的操作步骤,我们可以在不同的环境中对微服务进行部署和测试,确保微服务在各种场景下都能正常工作。同时,我们也了解了Kubernetes和Docker Compose的优缺点,在实际应用中可以根据具体需求选择合适的工具。
利用Kubernetes简化系统架构及测试
超级会员免费看
171万+

被折叠的 条评论
为什么被折叠?



