微服务设计模式 - 12 部署微服务应用
部署: 包含两个相关联的概念: 流程和架构。
部署流程: 包括一些由开发人员和运维人员执行的步骤, 以便将软件投入到生产环境.
手动部署已经一去不复返了, 部署的发展.
DevOps 跟部署息息相关.
部署模式: 编程语言特定发布包格式
比如 spring Boot 部署为 Jar 或 War. 例如 GoLang 服务是特定于操作系统某个路径下的可执行文件.
这种方式的好处是: 快速发布
这种方式的弊端是:
缺乏对技术栈的封装: 比如运维人员需要了解每一个服务的运行版本, 例如 Tomcat 的版本, JDK 的版本.
无法约束服务实例消耗资源
在同一个机器上运行多个服务实例时缺少隔离
很难自动判定放置服务的位置
部署模式: 将服务部署为容器
作为容器镜像打包的服务部署到生产环境中, 每个服务实例是一个容器.
创建容器时, 可以指定它的 CPU 和 内存资源. 以及依赖于容器实现的 I/O 资源.
使用Docker 构建镜像
1. 创建 Docker file. (可以理解为启动镜像docker image的脚本)
2. 构建 docker 镜像(docker image)一旦编写了 Dockerfile, 就可以构建docker镜像.
docker build 有两个参数: -t 指定镜像名称, .指定Docker调用上下文内容, (上下文在此就是指当前目录) 由Docker file 和用于构建镜像的文件组成.
docker build 命令将上下文上载到 Docker 守护进程, 后者构建镜像.
3. 把 Docker 镜像推送到镜像仓库
运行命令 docker tag 打抱歉和 docker push
4. 运行 Docker 容器
docker run 命令.
部署为容器的好处:
- 封装技术栈, 可以用容器的 API 实现对服务的管理.
- 服务实例是隔离的
- 服务实例的资源受到限制.
部署为容器的弊端: 你需要承担大量的容器镜像管理工作, 你必须负责给操作系统和运行时打补丁.
使用 Kubernetes 部署应用程序
单一 Docker 非常适合开发和测试, 但是要在生产环境可靠运行容器化服务, 需要使用更复杂的容器运行时, 例如 Kubernetes.
Kubernetes 是一个Docker 编排框架, 是 Docker 之上的一个软件层, 它将一组计算机硬件资源转变为用于运行服务的单一资源池.
它努力保持每个服务所需的实例数量, 并确保它们一直在线。
你只需要告诉 K8S 编排运行你的 N 个实例, 它会自动把其余的事搞定.
K8S 主要有3个功能:
- 资源管理: 将一组计算机视为由 CPU, 内存和存储构成的资源池, 将计算机集群视为一台计算机.
- 调度: 选择要运行容器的机器, 默认情况下, 调度考虑容器的资源需求和每个节点的可用资源.它还可以实现在同一个节点上部署具有亲和性的容器,或者确保特定的几个容器分散部署在不同的节点之上.
- 服务管理: 实现命名和版本化服务的概念,这个概念可以直接映射到微服务架构中的具体服务,K8S 始终保持运行所需数量的正常实例,它实现请求的负载均衡,K8S 也可以执行服务的滚动升级, 并允许你回滚到旧版本.
K8S 架构
K8S 在一组机器上运行. 分为主节点(负责管理)和普通节点(运行Pod)
主节点运行多个组件:
- API 服务器: 用于部署和管理服务的 REST API, (相当于对外部暴露的管理接口), 例如,可被 kubectl 命令行使用.
- Etcd: 存储集群数据键值的 NoSQL 数据库
- 调度器: 选择要运行 Pod 的节点.
- 控制器管理器: 运行控制器, 确保集群状态和预期匹配, 例如通过启动和终止实例来确保运行所需数量的服务实例.
普通节点(工作节点) 组件, 包括以下内容:
- Kubelet: 创建和管理节点上运行的 Pod.
- Kube-proxy: 管理网络, 包括跨 Pod 的负载均衡
- Pods: 应用程序
K8S 关键概念
- Pod, 基本部署单元, 它由一个或多个共享 IP 地址和存储卷的容器组成. (一个或多个容器, 这些容器共享IP地址和存储卷), 在某些情况下, Pod 包含一个或多个实现支持功能的边车(sidecar)容器, 例如 Nginx 服务器可以有一个边车容器, 定期执行 git pull 以下载最新版本的网站, Pod 生命周期很短, 因为Pod 的容器或它运行的节点可能会崩溃.
- Deployment: Pod 的声明性规范, Deployment 是一个控制器, 可确保始终运行所需数量的 Pod 实例. 它通过滚动升级和回滚支持版本控制.
- Service: 向应用程序服务的客户端提供一个静态/稳定 的网络地址. 它是基础设施提供服务发现的一种形式. 每个 service 具有一个IP地址和一个可解析为该IP地址的 DNS 名称, 并跨一个或多个 Pod 对 TCP 和 UDP 流量进行负载均衡. IP 地址和 DNS 名称只能在 Kubernetes 内部访问.
- ConfigMap: 名称与值对的命名集合. 用于定义一个或多个应用程序服务的外部化配置,Pod容器的定义可以引用ConfigMap 来定义容器的环境变量。它还可以使用 ConfigMap 在容器内创建配置文件. 可以使用 Secret 来存储敏感信息(如密码)。
在 K8S 上部署服务
1. 创建一个部署对象 (deployment), 通过 YAML 文件.
编写 YAML 文件后, 可以使用 kubectl apply 命令创建/更新 Deployment 对象.
1 个 DNS 对应了 2个 Pod 副本.
此命令向 Kubernetes API 服务器发出请求, 该请求将完成 Deployment 和 Pod 创建.
此时 Pod 正在运行, K8S Deployment 对象将尽最大努力保持其运行, 问题是 Pod 已经动态分配了 IP地址, 因此对于想要向服务发出HTTP请求的客户端来说, 必须设法获得这个地址. (服务发现机制)一种方法是使用客户端发现机制, 并运行服务发现功能(例如 Eureka), 幸运的是,我们可以通过 K8S 内置的服务发现机制。
Service 也是 K8S 一个对象, 它为一个或多个 Pod 的客户端提供稳定的网络访问端点, 它具有IP地址和解析该IP地址的 DNS 名称. 服务跨 Pod 对到该 IP 地址的流量进行负载均衡. 例如在上面的 YAML 文件中:
此 Service 将来自于 http://ftgo-restaurant-service:8080 的流量路由到 Deployment 的 Pod 中.
Service 对象定义的关键部分是 selector, 它选择目标 Pod.
部署 API Gateway
K8S Service 对象只能从集群内部访问, 而 API Gateway 的作用是将来自外部的流量路由到这个服务. 因此, 需要能够从集群外部访问服务.
幸运的是 K8S Service 也支持这个场景,Service 对象类型 有 ClusterIP(默认类型), NodePort 和 LoadBalancer.
NodePort Service 对象可通过集群中所有节点上的集群范围的端口访问.
API Gateway 在集群中使用 URL http://ftgo-api-gateway, 在集群外面使用 URL http://node-ip-address:3000/ 其中 node-ip-address 是集群某一个节点的IP地址, 配置 NodePort Service 对象后, 可以配置 AWS Elastic Load Balancer(ELB) 以跨节点对来自互联网的请求进行负载均衡. 这种方法的一个好处是 ELB 完全在你的控制之下.
但是, NodePort 类型不是唯一的选择, 还可以使用 LoadBalancer 类型的对象, 该 Service 对象自动配置特定于云的负载均衡器. 此类服务的好处是你不再需要配置自己的负载均衡器. 然而, 缺点是尽管 K8S 提供了配置 ELB 的选项, 例如 SSL 证书, 但你对配置的控制要少的多.