什么是Envoy
Envoy 是Lyft公司贡献给CNCF的高性能L4/L7代理和通信总线开源项目, 目前流行的service mesh 产品/服务如istio, AWS App Mesh 均使用envoy作为数据平面。在最新发布版本Envoy 1.14 中官方内置了AWS Lambda filter, 通过该filter可以将AWS Lambda 直接暴露成kubernetes的服务供其他服务调用。 本文将演示在AWS EKS 中如何使用envoy 1.14集成AWS Lambda。
场景
越来越多的客户选择Amazon EKS作为首选的微服务平台,通过kubernetes配置文件将自己的应用部署到Amazon EKS上。在微服务架构设计中客户往往会考虑使用serverless架构即AWS Lambda来扩展自己的服务. 在Amazon EKS集群中我们可以通过2种方式来使用AWS Lambda服务:
- 通过集群外的Amazon API Gateway,或者Application Load Balancer来访问AWS Lambda,对于这些API Gateway或者ALB客户需要进行额外的管理、维护。
- 在docker image 中编写代码通过SDK来访问AWS Lambda,这种属于代码级别的调用,往往缺少可配置的灵活性。
架构图(未使用envoy):
为了降低上述方案的集成难度、管理复杂度,作为service mesh首选的数据平面Envoy在1.14中推出了AWS Lambda filter, 为此新的架构图如下, 可以看到我们不再对Amazon EKS集群外的Amazon API Gateway/ALB等资源进行管理,也不需要在代码中直接调用AWS Lambda SDK, 仅仅通过标准的Envoy的配置文件就可以将任意AWS Lambda暴露成Amazon EKS集群中的服务,与其他服务在使用上并无差异。
架构图:
前提条件
AWS 账号, aws cli 命令行工具, 使用eksctl已经创建好的EKS集群。
部署示例lambda
创建lambda_function.py
cat <<EOF >>lambda_function.py
import json
import base64
def lambda_handler(event, context):
# TODO implement
body=""
if "body" in event:
body=event["body"]
if "is_base64_encoded" in event and event["is_base64_encoded"]:
body=str(base64.decodebytes(bytes(body,"utf-8")))
body="body is {}".format(body)
return {
'statusCode': 200,
'body': json.dumps("hello envoy with lambda. {}".format(body))
}
EOF
创建IAM role
cat <<EOF >>trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
#设置默认region为us-east-1
export AWS_DEFAULT_REGION=us-east-1
#创建lambda所需要的role和权限
aws iam create-role --role-name hello-envoy-lambda --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --role-name hello-envoy-lambda --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
部署lambda
#打包python文件
zip function.zip lambda_function.py
#部署lambda
LAMBDA_ROLE=$(aws iam get-role --role-name hello-envoy-lambda --query "Role.Arn" --out text)
aws lambda create-function --function-name hello-envoy-lambda \
--zip-file fileb://function.zip \
--runtime python3.7 \
--handler lambda_function.lambda_handler \
--role ${LAMBDA_ROLE}
部署hello-lambda-envoy应用到EKS集群
使用eksctl创建kubernetes service account
需要为hello-lambda-envoy 应用创建有lambda 执行权限的kubernetes service account,我们使用eksctl 工具来完成service account的创建。
#export CLUSTER_NAME=<你的eks集群名字>
export CLUSTER_NAME=eksworkshop
eksctl create iamserviceaccount --name hello-envoy-lambda --namespace default \
--cluster ${CLUSTER_NAME} --attach-policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole \
--approve --override-existing-serviceaccounts
#确认hello-envoy-lambda是否有lambda执行role
kubectl get serviceaccount hello-envoy-lambda -o yaml
参考输出
配置config map
envoy.yaml是envoy的配置文件,我们将文件发布成kubernets 的config map ,以供deployment/pod 使用,在文件中已经配置好了官方的aws lambda filter , 只需要替换lambda 对应的arn即可。
创建配置模版envoy.yaml.template
cat <<EOF >> envoy.yaml.template
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
protocol: TCP
address: 127.0.0.1
port_value: 9901
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
stat_prefix: ingress_http
access_log:
- name: envoy.file_access_log
config:
path: "/dev/stdout"
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: service_bing
request_headers_to_add:
- header:
key: X-IWant-Host
value: "lambda.us-east-1.amazonaws.com"
http_filters:
- name: envoy.filters.http.aws_lambda
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.aws_lambda.v3.Config
arn: "<LAMBDA_ARN>"
payload_passthrough: false
- name: envoy.filters.http.router
typed_config: {}
clusters:
- name: service_bing
connect_timeout: 10s
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
metadata:
filter_metadata:
com.amazonaws.lambda:
egress_gateway: true
load_assignment:
cluster_name: service_bing
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: lambda.us-east-1.amazonaws.com
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext
sni: "*.amazonaws.com"
EOF
使用envoy.yaml.template 生成envoy配置文件
#生成Envoy配置文件envoy.yaml
LAMBDA_ARN=$(aws lambda get-function --function-name hello-envoy-lambda --query "Configuration.FunctionArn" --output text)
#使用sed替换模版中<LAMBDA_ARN>为正确的AWS Lambda ARN
sed -e "s/<LAMBDA_ARN>/${LAMBDA_ARN}/g" envoy.yaml.template > envoy.yaml
#使用envoy.yaml创建config map
kubectl create configmap config-envoy-lambda --from-file=envoy.yaml
部署hello-lambda-envoy应用
创建kubernetes 部署文件 envoy-lambda-eks.yaml
cat <<EOF >>envoy-lambda-eks.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: envoy-lambda
labels:
app: envoy-lambda
spec:
replicas: 1
selector:
matchLabels:
app: envoy-lambda
template:
metadata:
labels:
app: envoy-lambda
spec:
serviceAccountName: hello-envoy-lambda
containers:
- name: envoy-lambda
image: envoyproxy/envoy:v1.14.1
ports:
- containerPort: 10000
volumeMounts:
- name: config-envoy-lambda
mountPath: /etc/envoy
volumes:
- name: config-envoy-lambda
configMap:
name: config-envoy-lambda
---
apiVersion: v1
kind: Service
metadata:
name: "service-envoy-lambda"
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
selector:
app: envoy-lambda
type: LoadBalancer
ports:
- protocol: TCP
port: 10000
targetPort: 10000
EOF
#部署
kubectl apply -f envoy-lambda-eks.yaml
#验证
kubectl get deploy envoy-lambda
kubectl get svc service-envoy-lambda
参考输出
使用curl调用部署在EKS里面的lambda服务.
#创建测试data.json
cat <<EOF >>data.json
{"data":"this is test!"}
EOF
#获取NLB的地址,端口固定为10000
ENVOY_ENDPOINT=$(kubectl get svc service-envoy-lambda --output jsonpath='{.status.loadBalancer.ingress[0].hostname}'):10000
curl -v -X POST -H "Content-Type: application/json" -H "Host:lambda" -d @data.json ${ENVOY_ENDPOINT}
参考输出
清除资源
#清除kubernetes 部署
kubectl delete -f envoy-lambda-eks.yaml
kubectl delete configmap config-envoy-lambda
#删除iamserviceaccount
eksctl delete iamserviceaccount --name hello-envoy-lambda --namespace default --cluster ${CLUSTER_NAME}
#删除lambda以及role
aws lambda delete-function --function-name hello-envoy-lambda
aws iam detach-role-policy --role-name hello-envoy-lambda --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name hello-envoy-lambda
总结
Envoy 官方最新版本1.14已经提供了的AWS Lambda filter,可以帮助用户更快得在kubernetes集群环境下集成AWS Lambda,通过这个版本我们可以更方便的在EKS,AWS App Mesh环境下使用AWS Lambda。
参考:
Envoy 1.14版本说明:https://www.envoyproxy.io/docs/envoy/latest/version_history/v1.14.0
本篇作者