前言

埋点数据个别取决于服务提供商想从用户身上获取什么信息。通常来讲,次要分为用户的根本属性信息和行为信息。用户的根本属性信息次要包含:年龄、性别、设施等。行为信息即用户的点击行为和浏览行为,在什么工夫,哪个用户点击了哪个按钮,浏览了哪个页面,浏览时长等等的数据。 根本属性信息和行为信息又能够称之为一个简略的报文。报文是网络中替换与传输的数据单元,即站点一次性要发送的数据块。报文蕴含了将要发送的残缺的数据信息,其长短很不统一,长度不限且可变。简略来说就是用户在 App 内有一个操作行为,就会上报一组带有数据的字段。

亚马逊云科技开发者社区为开发者们提供寰球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、流动与比赛等。帮忙中国开发者对接世界最前沿技术,观点,和我的项目,并将中国优良开发者或技术举荐给寰球云社区。如果你还没有关注/珍藏,看到这里请肯定不要匆匆划过,点这里让它成为你的技术宝库!

本文会演示如何利用开源软件和 Amazon 服务来构建服务端埋点零碎,客户端局部不在本文的探讨范畴内。

软件架构

Lua 是一种轻量级、可嵌入式的脚本语言,能够非常容易的嵌入到其余语言中应用。另外 Lua 提供了协程并发,即以同步调用的形式进行异步执行,从而实现并发,比起回调机制的并发来说代码更容易编写和了解,排查问题也会容易。Lua 还提供了闭包机制,函数能够作为 First Class Value 进行参数传递,另外其实现了标记革除垃圾收集。

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其外部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于不便地搭建可能解决超高并发、扩展性极高的动静 Web 利用、Web 服务和动静网关。OpenResty® 通过汇聚各种设计精良的 Nginx 模块,从而将 Nginx 无效地变成一个弱小的通用 Web 利用平台。这样,Web 开发人员和零碎工程师能够应用 Lua 脚本语言调动 Nginx 反对的各种 C 以及 Lua 模块,疾速结构出足以胜任 10K 乃至 1000K 以上单机并发连贯的高性能 Web 利用零碎。

通常会利用 Nginx&Lua 实现服务端日志的对立收集,笔者会利用这项技术实现埋点数据的收集,思路如下:

  • 以 Http 申请的形式将埋点数据发送至 Nginx 端;
  • 通过 Lua 模块解析申请体,再将埋点数据以异步的形式发送至后端 Kafka。这个过程中数据不必落盘,大大节约了存储空间和进步了效率;
  • 最终后端会有一组消费者(例如 Spark)从 Kafka 中将数据落盘(例如 S3);

下图是本文软件层面的架构图。

整体架构

架构分为四大块:

  • Amazon EKS,本文会将 Nginx 和 Lua 以容器的模式部署在 Amazon EKS 中,充分利用 EKS 的弹性;
  • Amazon MSK,本文会应用托管的 Kafka,也就是 Amazon MSK,升高部署和前期运维的压力;
  • Amazon EFS,思考到整体架构的可用性和持久性,如果在 MSK 端产生了故障,尽管概率极低,本文会应用 Amazon EFS 来存储 Nginx 的谬误日志,尽量保障音讯的完整性;
  • Amazon NLB,本文会应用 NLB 来裸露服务;
  • Amazon ECR,本文会应用 ECR 来存储容器镜像;

步骤

在开始之前,请先确保您具备登录 Amazon 寰球区控制台的账号,并具备管理员权限。

前提条件

  • 一台 Linux 终端
  • 足够的 Amazon 账号权限
  • 装置 Amazon CLI
  • 装置 Docker
  • 装置 eksctl
  • 装置 kubectl

    一、创立 Amazon VPC和Security Group

    参考此链接,创立 1个 VPC、3个私有子网、3个公有子网和其余 VPC 资源。接下来笔者会应用 vpcid, publicsubnetid01, publicsubnetid02, publicsubnetid03, privatesubnetid01, privatesubnetid02, privatesubnetid03来代替相干 VPC 和子网资源。

创立一个平安组供后续其余服务应用,为了简便配置,笔者会将本文应用到的资源放入同一个平安组中。读者能够在本人环境中将平安组进行拆分。

###$ aws ec2 create-security-group --group-name my-poc-sg --description " my-poc-sg " --vpc-id vpcid###

记录 Security Group ID, 笔者会应用 securitygroupid 来代替它。

###$ aws ec2 authorize-security-group-ingress \     --group-id securitygroupid\     --protocol all\     --port -1 \     --source-group securitygroupid###

二、创立 Amazon MSK

创立 Amazon MSK 集群来接管音讯,并记录 Broker 地址,笔者会应用 broker01, broker02, broker03 来代替。

###$ aws kafka create-cluster \     --cluster-name "my-poc-msk-cluster" \     --broker-node-group-info file://brokernodegroupinfo.json \     --kafka-version "2.6.2" \     --number-of-broker-nodes 3 \     --encryption-info EncryptionInTransit={ClientBroker=TLS_PLAINTEXT}###brokernodegroupinfo.json~~~{    "InstanceType": "kafka.m5.large",    "BrokerAZDistribution": "DEFAULT",    "ClientSubnets": [      "privatesubnetid01",      "privatesubnetid02",      "privatesubnetid03"    ],    "SecurityGroups": [      "securitygroupid "    ],    "StorageInfo": {      "EbsStorageInfo": {        "VolumeSize": 100      }    }}~~~

三、构建镜像

应用到的文件蕴含:

  • Dockerfile
  • sh
  • conf
  • conf
  • my-poc-send2kafka.lua
###$ mkdir workdir$ cd workdir###

顺次依照如下内容创立文件。

Dockerfile~~~FROM amazonlinuxCOPY install.sh /RUN chmod +x /install.shRUN /install.shCOPY nginx.conf /opt/openresty/nginx/conf/nginx.confCOPY common.conf /opt/openresty/nginx/conf/conf.d/common.confCOPY my-poc-send2kafka.lua /opt/openresty/nginx/lua/my-poc-send2kafka.luaEXPOSE 80CMD sed -i "s/\$mypodip/$(hostname -i)/g" /opt/openresty/nginx/conf/conf.d/common.conf && /opt/openresty/nginx/sbin/nginx -c /opt/openresty/nginx/conf/nginx.conf~~~install.sh~~~#!/bin/shyum -y install readline-devel pcre-devel openssl-devel gcc wget tar gzip perl make unzip hostnamemkdir /opt/softwaremkdir /opt/modulecd /opt/software/wget https://openresty.org/download/openresty-1.9.7.4.tar.gztar -xzf openresty-1.9.7.4.tar.gz -C /opt/module/cd /opt/module/openresty-1.9.7.4./configure --prefix=/opt/openresty \--with-luajit \--without-http_redis2_module \--with-http_iconv_modulemakemake installcd /opt/software/wget https://github.com/doujiang24/lua-resty-kafka/archive/master.zipunzip master.zip -d /opt/module/cp -rf /opt/module/lua-resty-kafka-master/lib/resty/kafka/ /opt/openresty/lualib/resty/mkdir /opt/openresty/nginx/lua/mkdir /var/log/nginx/~~~nginx.conf~~~worker_processes auto;worker_rlimit_nofile 100000;daemon off;events {    worker_connections 102400;    multi_accept on;    use epoll;}http {    include mime.types;    default_type application/octet-stream;    log_format main '$remote_addr - $remote_user [$time_local] "$request" '    '$status $body_bytes_sent "$http_referer" '    '"$http_user_agent" "$http_x_forwarded_for"';    access_log /var/log/nginx/access.log main;    resolver 8.8.8.8;    #resolver 127.0.0.1 valid=3600s;    sendfile on;    keepalive_timeout 65;    underscores_in_headers on;    gzip on;    include /opt/openresty/nginx/conf/conf.d/common.conf;}~~~common.conf~~~lua_package_path "/opt/openresty/lualib/resty/kafka/?.lua;;";lua_package_cpath "/opt/openresty/lualib/?.so;;";lua_shared_dict ngx_cache 128m;lua_shared_dict cache_lock 100k;server {    listen 80;    server_name 127.0.0.1;    root html;    lua_need_request_body on;    access_log /var/log/nginx/access.log main;    error_log /var/log/nginx/error-$mypodip.log notice;    location = /putmessage {        lua_code_cache on;        charset utf-8;        default_type 'application/json';        content_by_lua_file "/opt/openresty/nginx/lua/my-poc-send2kafka.lua";    }}~~~my-poc-send2kafka.lua~~~local producer = require("resty.kafka.producer")local json = require("cjson")local broker_list = {  {host = "broker01", port = 9092},  {host = "broker02", port = 9092},  {host = "broker03", port = 9092}}local log_json = {}log_json["body"] = ngx.req.read_body()log_json["body_data"] = ngx.req.get_body_data()local topic = ngx.req.get_headers()["topic"]local producer_error_handle = function(topic, partition_id, queue, index, err, retryable)  ngx.log(ngx.ERR, "Error handle: index ", index, ' partition_id ', partition_id, ' retryable ', retryable, ' json ', json.encode(queue))endlocal bp = producer:new(broker_list, { producer_type = "async", batch_num = 200, error_handle = producer_error_handle})local sendMsg = json.encode(log_json)local ok, err = bp:send(topic, nil, sendMsg)~~~###$ docker build -t  my-poc-image .###

四、创立 Amazon ECR 并上传镜像

aws ecr create-repository \    --repository-name my-poc-ecr/nginx-lua###$ aws ecr get-login-password --region regioncode | docker login --username AWS --password-stdin youraccountid.dkr.ecr.regioncode.amazonaws.com$ docker tag my-poc-image:latest youraccountid.dkr.ecr.regioncode.amazonaws.com/my-poc-ecr/nginx-lua:latest$ docker push youraccountid.dkr.ecr.regioncode.amazonaws.com/my-poc-ecr/nginx-lua:latest###

五、创立 Amazon EFS

###aws efs create-file-system \    --performance-mode generalPurpose \    --throughput-mode bursting \    --encrypted \    --region ap-northeast-1 \    --tags Key=Name,Value=my-poc-efs###

记录 FileSystemId,笔者会应用 fsid 来代替它。

###aws efs create-mount-target \    --file-system-id fsid  \    --subnet-id privatesubnetid01 \    --security-groups securitygroupidaws efs create-mount-target \    --file-system-id fsid \    --subnet-id privatesubnetid02 \    --security-groups securitygroupidaws efs create-mount-target \    --file-system-id fsid \    --subnet-id privatesubnetid03 \    --security-groups securitygroupid###

六、创立Amazon EKS集群并装置组件

cluster.yaml###apiVersion: eksctl.io/v1alpha5kind: ClusterConfigmetadata:  name: my-poc-eks-cluster  region: ap-northeast-1  version: "1.21"iam:  withOIDC: trueaddons:- name: vpc-cni  version: v1.11.2-eksbuild.1- name: coredns  version: v1.8.4-eksbuild.1- name: kube-proxy  version: v1.21.2-eksbuild.2vpc:  subnets:    private:      ap-northeast-1a: { id: "privatesubnetid01" }      ap-northeast-1c: { id: "privatesubnetid02" }      ap-northeast-1d: { id: "privatesubnetid03" }managedNodeGroups:  - name: my-poc-ng-1    instanceType: m5.large    desiredCapacity: 2    volumeSize: 100    privateNetworking: true######$ eksctl create cluster -f cluster.yaml###

参考此链接装置 Amazon Load Balancer Controller。

参考此链接装置 Amazon EFS CSI driver。

七、在 Amazon EKS 中部署

###$ kubectl apply -f deploy.yaml###deploy.yaml~~~---kind: StorageClassapiVersion: storage.k8s.io/v1metadata:  name: efs-scprovisioner: efs.csi.aws.comparameters:  provisioningMode: efs-ap  fileSystemId: fsid  directoryPerms: "700"  gidRangeStart: "1000"  gidRangeEnd: "2000"  basePath: "/dynamic_provisioning"---apiVersion: v1kind: PersistentVolumeClaimmetadata:  name: my-poc-pvcspec:  accessModes:    - ReadWriteMany  storageClassName: efs-sc  resources:    requests:      storage: 5Gi---apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx-lua  labels:    app: nginx-luaspec:  replicas: 2  selector:    matchLabels:      app: nginx-lua  template:    metadata:      labels:        app: nginx-lua    spec:      containers:      - name: nginx-lua        image: youraccountid.dkr.ecr.regioncode.amazonaws.com/my-poc-ecr/nginx-lua:latest        ports:        - containerPort: 80        resources:          limits:            cpu: 500m          requests:            cpu: 200m        volumeMounts:        - name: my-poc-volume          mountPath: /var/log/nginx      volumes:      - name: my-poc-volume        persistentVolumeClaim:          claimName: my-poc-pvc---apiVersion: v1kind: Servicemetadata:  name: nginx-lua-svc  annotations:    service.beta.kubernetes.io/aws-load-balancer-type: external    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing    service.beta.kubernetes.io/aws-load-balancer-subnets: publicsubnetid01, publicsubnetid02, publicsubnetid03    service.beta.kubernetes.io/aws-load-balancer-name: my-poc-nginx-lua    service.beta.kubernetes.io/aws-load-balancer-attributes: load_balancing.cross_zone.enabled=truespec:  selector:    app: nginx-lua  type: LoadBalancer  ports:    - protocol: TCP      port: 80      targetPort: 80~~~

应用以下命令获取 EXTERNAL-IP 地址,笔者会应用 nlbdns 来代替。

###$ kubectl get svc nginx-lua-svc###

八、功能测试

参考此链接装置 kafka 客户端。

创立测试用 Topic。

###$ ./kafka-topics.sh --topic my-poc-topic-01 --create --bootstrap-server broker01:9092, broker02:9092, broker03:9092 --partitions 3 --replication-factor 2###

装置 ApacheBench 测试工具,并进行测试。

###$ sudo yum -y install httpd-tools$ ab -T 'application/json' -H "topic: my-poc-topic-01" -n 1000 -p postdata.json http://nlbdns/putmessage###postdata.json~~~{    "uuid": "2b8c376e-bd20-11e6-9ebf-525499b45be6",    "event_time": "2016-12-08T18:08:12",    "page": "www.example.com/poster.html",    "element": "register",    "attrs":     {        "title": "test",        "user_id": 1234    }}~~~

查看音讯是否能够胜利生产。

###./kafka-console-consumer.sh --bootstrap-server broker01:9092, broker02:9092, broker03:9092 --topic my-poc-topic-01 --from-beginning###

音讯曾经胜利生产。

接下来笔者会给一个不存在的 topic 发送音讯,用来模仿生产环境中后端 MSK 不可用的状况。

###$ ab -T 'application/json' -H "topic: my-poc-topic-noexist" -n 1000 -p postdata.json http://nlbdns/putmessage###

依照料想情况,这部分音讯会以谬误日志的模式保留在 Amazon EFS 中。

###$ ab -T 'application/json' -H "topic: my-poc-topic-noexist" -n 1000 -p postdata.json http://nlbdns/putmessage###

进入 EFS 中,关上带有 pod ip 的谬误日志,能够看到错误信息被记录了下来。

本篇作者

杨探
亚马逊云科技解决方案架构师,负责互联网行业云端架构征询和设计。

文章起源:https://dev.amazoncloud.cn/column/article/6309d47976658473a32...