本文讲的是用微服务创建可扩展API【编者的话】想尝试用微服务创建一个API吗?微服务很适合用于创建API,开发人员可以集中精力于创建小巧,相互无关的组件,组合成一个特定API调用;可以用不同语言开发不同服务点(endpoint),提供不同服务级别(SLAs),甚至独立扩展微服务,可以参考我的某些微服务讲座。
讲座视频
我也讲过在一个Kubernetes集群内部署和运行不同服务是非常容易的, 如下代码 演示了如何简单发起一个前端和后端服务,他们互相通讯并且独立扩展。这个示例唯一没有展示出来的是这个服务是用不同语言开发的,对终端用户无缝透明地工作。最近,我的同事Sara Robinson和我演示了一个基于NGINX如何创建这种服务,我们 开源了代码 。耐心读完这篇深入报道(报道很长,可以直接跳到自己关心的部分)。这个演示依赖于Kubernetes和Google Container Engine运行这个集群。我们开始之前,确保已经创建了一个Google Cloud project,如果需要从Kubernetes开始,可以查看 这篇博客 。
我会使用标准三节点集群。
登陆
集群就简单生成了。
Ruby - Arrayify :
Python - Reverse :
Node.js - To Lower :
Go - To Upper :
是不是很简单?
首先是创建叫做Dockerfile的配置文件,如下是我们使用的Dockerfiles。
Ruby:
Python:
Node.js:
Go:
这些就是所需的所有配置文件了。你的依赖包可能会复杂些,其实Dockerfile的基本概念就是写下需要在Linux中运行的命令,指出哪些文件需要挂载或者copy到容器中。可以从 这儿 学习更多。
创建应用,需要在包含Dockerfile的目录下运行docker build命令。开发者可以给这些影响打上标签(tag),以便安全地使用 Google Container Registry 保存在云端。
用容器名(例如reverser)以及版本(例如0.1)替换Google Cloud Project ID,从西面开始,我会将gcr.io//: 用 开代替。为以上四个微服务都执行以上命令,就创建容器完毕。可以用如下命令测试:
如果使用Linux,可以访问localhost:8080来使用微服务。如果不使用linux,可以使用docker-machine运行本机Docker引擎(很快Mac和Windows会自带Docker功能)。通过docker-machine,能得到实例名:
并且得到机器IP地址:
此时应当可以看到如下显示:
这条命令会将本地映像推送到集群访问的私有代码池(repository),记住要推送四次,然后就可以从Container Registry页面看到并且管理所有推送的容器了。下面需要部署容器到集群,最容易的方式是运行一下命令:
这条命令将你的容器以Kubernetes部署方式部署为一个实例,如果发生任何事情都将会重启或者重新调度容器。一篇之前的 博客 讨论过复制控制(部署的老版本名词)以及为什么很重要。
你可以在这停止了,但是我想还是继续为我的部署生成几个配置文件因为后面会比较容易记住我做了什么修改。
如下是我用于arraify微服务的YAML文件,给部署一个名字叫arrayify,规定了复制次数(3),一起容器名和开放端口。
保存文件叫做“deployment.yaml”,然后部署它。
为四个微服务都重复以上步骤,每个微服务都生成yaml文件,改变容器映像和标签(一般就是将arrayify替换为其它对应名字)。此刻,可以看到集群内所有部署和容器。
NGINX可以做很多需要创建可扩展API的事情。可以设置为API网关,可以很好控制API粒度,包括rate limiting、security、access control和其它。我会使用很多基础NGI配置,这样大家可以直接使用它们。
服务目标是所有带“arrayify-pods”标签pod上的端口80,保存此文件为"service.yaml",然后用如下命令部署:
为所有四个微服务重复以上过程,创建文件,改变标签(一般就是替代arrayify)。此时,可以看见集群内运行的所有服务:
首先看第一行:
这一行定义NGINX用来搜索微服务的DNS服务,对集群来说可能并不是必须的,但是包含这一行却是很安全的。你可能很奇怪IP地址从哪里来,其实是Kubernetes内置的DNS服务,如果你自己有DNS服务,也可以用如下命令获得IP地址:
下一步,需要设置upstreams(实现同一功能的服务器集合,例如一个微服务),因为可以使用DNS,因此很容易搭建,参见如下arrayify微服务的upstream:
Arrayify.default.svc.cluster.local是Kubernetes服务的全名;为四个微服务重复以上过程(一般就是讲arrayify替换为其它名字)。注意server字段,这里会告诉NGINX哪个路径可以重新映射到哪个微服务:
这里,告诉NGINX任何一/arrayify/开始的服务请求应该被转发到arrayify微服务。为其它微服务创建一个字段(一般就是替换arrayify为其它微服务名字),可以参考完整 nginx.conf 。然后跟推送其它微服务一样,推送客制化NGINX映像。也可以从GitHub查看所有内容。
一旦部署服务,可以用如下命令看到外部IP地址:
使用NGINX公开了单一API服务点和代理负载到四个不同微服务,每个都有三个实例。
缩减也是一样的过程。如果想减少到一个容器,运行如下命令:
也可以实现自动扩展,根据CPU负载动态调整集群内容器数量。使用如下命令:
如你所愿,这条命令确保至少一个容器在运行,如果需要最多可以扩展到五个,并且会确保每个容器至少CPU占用80%以上。也可以用此命令扩展NGINX!需要注意的是虚机(节点)自动扩展现在在Kubernetes内并不支持,然而,可以使用Google Cloud Managed Instance Group来实现此需求。
然后推送:
打开arrayify微服务的deployment.yaml 文件,改变容器版本(从0.1到0.2),然后部署新版本。
Kubernetes会自动扩展部署新版本,缩减下电旧版本。如果新版本有bug,可以用单一命令回滚:
(替换arrayify为其它需要更新或者回滚微服务名字)如果想了解详细信息,可以读其它 文档 。
可以从中看出流量、错误以及每个不同微服务的健康状态。可以参看详细的 nginx.conf 。
最后,我强烈推荐使用Google Stackdriver来为微服务配置自动告警和触发。Stackdriver是一个监控应用的一站式商店;默认的,每个容器的stdout和stderr都被发往stackdriver日志。stackdriver监控可以查看Kubernetes集群和监控各自pods,stackdriver监控可以帮助模拟代码debug而不需要真的命中bug。
感谢读完了这么长的博客,如果有什么需要分享的,可以跟我在 Twitter @SandeepDinesh互动。
讲座视频
我也讲过在一个Kubernetes集群内部署和运行不同服务是非常容易的, 如下代码 演示了如何简单发起一个前端和后端服务,他们互相通讯并且独立扩展。这个示例唯一没有展示出来的是这个服务是用不同语言开发的,对终端用户无缝透明地工作。最近,我的同事Sara Robinson和我演示了一个基于NGINX如何创建这种服务,我们 开源了代码 。耐心读完这篇深入报道(报道很长,可以直接跳到自己关心的部分)。这个演示依赖于Kubernetes和Google Container Engine运行这个集群。我们开始之前,确保已经创建了一个Google Cloud project,如果需要从Kubernetes开始,可以查看 这篇博客 。
为什么需要Kubernetes
Sara和我用不同语言进行编程,每种语言都有最佳适用范围。例如,Google内部使用C++、Java、Python和Go组合的开发环境。在有Containers和Kubernetes之前,这意味着为不同开发栈设置四种不同服务器,也就是一种Ops很高的架构。如果想整合服务器,就需要在同一台服务器上安装不同开发栈,但是更新一种栈则需要影响其他栈,扩展系统编程很大挑战,一些看起来简单的操作变的比较复杂。基于这种场景,很多人不得不只选择一种开发栈。有了容器,这种两难境地彻底改变。容器可以在机器和代码之间进行了抽象,开发者在任何机器上不做明显配置就可以运行任何开发栈。Kubernetes自动编排这些作业,开发者可以部署管理所有这些容器而不需要ssh登进这些机器。创建一个Kubernetes集群
先创建一个Kubernetes集群运行应用。确保已经安装了Google Cloud SDK,或者使用Cloud Shell(如果对Google Cloud还比较陌生,可以尝试试用版: https://cloud.google.com/free-trial/ )。我会使用标准三节点集群。
$ gcloud container clusters create mycluster
登陆
$ gcloud container clusters get-credentials mycluster
集群就简单生成了。
微服务代码样例
我们要部署的代码非常简单。用每种语言,我们都实现了某种字符操作,我们有四种不同服务:Ruby - Arrayify :
Hello World → [H,e,l,l,o, ,W,o,r,l,d]
Python - Reverse :
Hello World → dlroW olleH
Node.js - To Lower :
Hello World → hello world
Go - To Upper :
Hello World → HELLO WORLD
是不是很简单?
容器化代码
下一步是把如上代码放到容器中。容器会帮助将所有依赖包打包为一起并且以可发布方式发行。我们会使用Docker来实现。确保安装了Docker或者使用Cloud Shell。Docker使得创建容器非常简单,并且确保环境一致。如果从未使用过Docker,可以先阅读以下讨论如何在Docker中运行 MEAN栈 的 博客 。首先是创建叫做Dockerfile的配置文件,如下是我们使用的Dockerfiles。
Ruby:
FROM ruby:2.3.0-onbuild CMD ["ruby", "./arrayify.rb"]
Python:
FROM python:2.7.11-onbuild CMD [ "python", "./app.py" ]
Node.js:
FROM node:5.7.0-onbuild
Go:
FROM golang:1.6-onbuild
这些就是所需的所有配置文件了。你的依赖包可能会复杂些,其实Dockerfile的基本概念就是写下需要在Linux中运行的命令,指出哪些文件需要挂载或者copy到容器中。可以从 这儿 学习更多。
创建应用,需要在包含Dockerfile的目录下运行docker build命令。开发者可以给这些影响打上标签(tag),以便安全地使用 Google Container Registry 保存在云端。
$ docker build -t gcr.io/<PROJECT_ID>/<CONTAINER_NAME>:<CONTAINER_VERSION> .
用容器名(例如reverser)以及版本(例如0.1)替换Google Cloud Project ID,从西面开始,我会将gcr.io//: 用 开代替。为以上四个微服务都执行以上命令,就创建容器完毕。可以用如下命令测试:
$ docker run -ti -p 8080:80
如果使用Linux,可以访问localhost:8080来使用微服务。如果不使用linux,可以使用docker-machine运行本机Docker引擎(很快Mac和Windows会自带Docker功能)。通过docker-machine,能得到实例名:
$ docker-machine list
并且得到机器IP地址:
$ docker-machine ip
此时应当可以看到如下显示:
使用Google Container Engine部署容器
接下来需要部署他们。首先将容器从本机拷贝到云端。$ gcloud docker push
这条命令会将本地映像推送到集群访问的私有代码池(repository),记住要推送四次,然后就可以从Container Registry页面看到并且管理所有推送的容器了。下面需要部署容器到集群,最容易的方式是运行一下命令:
$ kubectl run \ --image= \ --port=80
这条命令将你的容器以Kubernetes部署方式部署为一个实例,如果发生任何事情都将会重启或者重新调度容器。一篇之前的 博客 讨论过复制控制(部署的老版本名词)以及为什么很重要。
你可以在这停止了,但是我想还是继续为我的部署生成几个配置文件因为后面会比较容易记住我做了什么修改。
如下是我用于arraify微服务的YAML文件,给部署一个名字叫arrayify,规定了复制次数(3),一起容器名和开放端口。
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: arrayify spec: replicas: 3 template: metadata: labels: name: arrayify-pods spec: containers: - image: name: arrayify-container imagePullPolicy: Always ports: - containerPort: 80 name: http-server
保存文件叫做“deployment.yaml”,然后部署它。
$ kubectl apply -f deployment.yaml
为四个微服务都重复以上步骤,每个微服务都生成yaml文件,改变容器映像和标签(一般就是将arrayify替换为其它对应名字)。此刻,可以看到集群内所有部署和容器。
$ kubectl get deployments
$ kubectl get pods
发布微服务
如果你读了我之前一篇博客,那么就会知道下一步就是为每个微服务创建一个Kubernetes服务,这会创建一个稳定的服务点和每个微服务的负载均衡。然而,并不会希望为每个微服务都对外界开放独自的服务。每个微服务都是这个API的一部分,如果你分别开放每个微服务,每个微服务将会有各自的IP地址,这绝对不是我们想看到的。相反,可以使用NGINX来代理微服务实现唯一服务点。我会使用NGINX+,是一个付费软件,但是开源版工作也很不错。NGINX可以做很多需要创建可扩展API的事情。可以设置为API网关,可以很好控制API粒度,包括rate limiting、security、access control和其它。我会使用很多基础NGI配置,这样大家可以直接使用它们。
创建内部服务
第一步是可以用NGINX代理。可以参考如下arrayify微服务代码:apiVersion: v1 kind: Service metadata: name: arrayify spec: ports: - port: 80 targetPort: 80 protocol: TCP selector: name: arrayify-pods
服务目标是所有带“arrayify-pods”标签pod上的端口80,保存此文件为"service.yaml",然后用如下命令部署:
$ kubectl create -f service.yaml
为所有四个微服务重复以上过程,创建文件,改变标签(一般就是替代arrayify)。此时,可以看见集群内运行的所有服务:
$ kubectl get svc
配置NGINX
下一步是配置NGINX代理微服务。可以看看如下目录 NGINX folder in GitHub 。我们会详细讲解如何配置nginx.conf。首先看第一行:
resolver 10.11.240.10 valid=5s;
这一行定义NGINX用来搜索微服务的DNS服务,对集群来说可能并不是必须的,但是包含这一行却是很安全的。你可能很奇怪IP地址从哪里来,其实是Kubernetes内置的DNS服务,如果你自己有DNS服务,也可以用如下命令获得IP地址:
$ kubectl get svc kube-dns --namespace=kube-system
下一步,需要设置upstreams(实现同一功能的服务器集合,例如一个微服务),因为可以使用DNS,因此很容易搭建,参见如下arrayify微服务的upstream:
upstream arrayify-backend { zone arrayify-backend 64k; server arrayify.default.svc.cluster.local resolve; }
Arrayify.default.svc.cluster.local是Kubernetes服务的全名;为四个微服务重复以上过程(一般就是讲arrayify替换为其它名字)。注意server字段,这里会告诉NGINX哪个路径可以重新映射到哪个微服务:
server { listen 80; status_zone backend-servers; location /arrayify/ { proxy_pass http://arrayify-backend/; } }
这里,告诉NGINX任何一/arrayify/开始的服务请求应该被转发到arrayify微服务。为其它微服务创建一个字段(一般就是替换arrayify为其它微服务名字),可以参考完整 nginx.conf 。然后跟推送其它微服务一样,推送客制化NGINX映像。也可以从GitHub查看所有内容。
公开NGINX
最后一步就是公开NGINX服务,跟为微服务创建内部服务流程一样,但是需要指出“type:LoadBalancer",会给此服务一个外部IP地址,可以从NGINX目录下svc.yaml文件中看到。一旦部署服务,可以用如下命令看到外部IP地址:
测试
下面使用外部IP,测试这个API看看输出情况,非常酷!回顾
我们创建了如下应用:使用NGINX公开了单一API服务点和代理负载到四个不同微服务,每个都有三个实例。
额外阅读
我们所有的模组都已经运行成功,我们接下来看看如何监控、扩展和更新微服务。扩展
用Kubernetes扩展微服务并不容易。例如要扩展Arrayify容器的数量,可以使用如下命令扩展到五个容器:$ kubectl scale deployment arrayify --replicas=5
缩减也是一样的过程。如果想减少到一个容器,运行如下命令:
$ kubectl scale deployment arrayify --replicas=1
也可以实现自动扩展,根据CPU负载动态调整集群内容器数量。使用如下命令:
$ kubectl autoscale deployment arrayify --min=1 --max=5 --cpu-percent=80
如你所愿,这条命令确保至少一个容器在运行,如果需要最多可以扩展到五个,并且会确保每个容器至少CPU占用80%以上。也可以用此命令扩展NGINX!需要注意的是虚机(节点)自动扩展现在在Kubernetes内并不支持,然而,可以使用Google Cloud Managed Instance Group来实现此需求。
更新
如果能够动态更新微服务而不影响业务就非常理想。应用不同部分依赖不同微服务,因此如果一个微服务下线了,会对其它部分造成负面影响。幸运的是,Kubernetes使得零宕机部署变的更加易于管理。更新一个微服务,首先创建一个运行新代码的容器,打上新标签。例如,如果想更新arrayify微服务,重新运行docker build命令,但是需要更改版本号。$ docker build -t gcr.io//arrayify:0.2 .
然后推送:
$ gcloud docker push gcr.io//arrayify:0.2
打开arrayify微服务的deployment.yaml 文件,改变容器版本(从0.1到0.2),然后部署新版本。
$ kubectl apply -f deployment.yaml
Kubernetes会自动扩展部署新版本,缩减下电旧版本。如果新版本有bug,可以用单一命令回滚:
$ kubectl rollout undo deployment/arrayify
(替换arrayify为其它需要更新或者回滚微服务名字)如果想了解详细信息,可以读其它 文档 。
监控
使用NGINX+,有一个非常酷的面板,可以了解所有微服务状态。可以从中看出流量、错误以及每个不同微服务的健康状态。可以参看详细的 nginx.conf 。
最后,我强烈推荐使用Google Stackdriver来为微服务配置自动告警和触发。Stackdriver是一个监控应用的一站式商店;默认的,每个容器的stdout和stderr都被发往stackdriver日志。stackdriver监控可以查看Kubernetes集群和监控各自pods,stackdriver监控可以帮助模拟代码debug而不需要真的命中bug。
感谢读完了这么长的博客,如果有什么需要分享的,可以跟我在 Twitter @SandeepDinesh互动。
原文链接:Creating a scalable API with microservices(翻译:杨峰)
原文发布时间为:2016-07-11
本文作者:杨峰
本文来自云栖社区合作伙伴Dockerone.io,了解相关信息可以关注Dockerone.io。
原文标题:用微服务创建可扩展API