'n Voorbeeld van 'n gebeurtenisgedrewe toepassing gebaseer op webhooks in die S3-objekberging van Mail.ru Cloud Solutions

'n Voorbeeld van 'n gebeurtenisgedrewe toepassing gebaseer op webhooks in die S3-objekberging van Mail.ru Cloud Solutions
Rube Goldberg koffiemasjien

Gebeurtenisgedrewe argitektuur verhoog die kostedoeltreffendheid van die hulpbronne wat gebruik word omdat dit slegs gebruik word op die oomblik wanneer dit nodig is. Daar is baie opsies oor hoe om dit te implementeer en nie bykomende wolkentiteite as werkertoepassings te skep nie. En vandag sal ek nie oor FaaS praat nie, maar oor webhooks. Ek sal 'n tutoriaalvoorbeeld wys van die hantering van gebeure met behulp van webhooks vir voorwerpberging.

'n Paar woorde oor voorwerpberging en webhooks. Met objekberging kan u enige data in die wolk stoor in die vorm van voorwerpe, toeganklik via S3 of 'n ander API (afhangende van implementering) via HTTP/HTTPS. Webhooks is oor die algemeen pasgemaakte HTTP-terugroepe. Hulle word tipies veroorsaak deur 'n gebeurtenis, soos kode wat na 'n bewaarplek gedruk word of 'n opmerking wat op 'n blog geplaas word. Wanneer 'n gebeurtenis plaasvind, stuur die oorsprongwebwerf 'n HTTP-versoek na die URL wat vir die webhook gespesifiseer is. As gevolg hiervan kan jy gebeure op een webwerf laat aksies op 'n ander veroorsaak (wiki). In die geval waar die bronwebwerf 'n objekberging is, tree gebeurtenisse op as veranderinge aan die inhoud daarvan.

Voorbeelde van eenvoudige gevalle waar sulke outomatisering gebruik kan word:

  1. Skep kopieë van alle voorwerpe in 'n ander wolkberging. Afskrifte moet dadelik geskep word wanneer lêers bygevoeg of verander word.
  2. Outomatiese skepping van 'n reeks duimnaels van grafiese lêers, voeg watermerke by foto's en ander beeldwysigings.
  3. Kennisgewing oor die aankoms van nuwe dokumente (byvoorbeeld, 'n verspreide rekeningkundige diens laai verslae na die wolk op, en finansiële monitering ontvang kennisgewings oor nuwe verslae, kontroleer en ontleed dit).
  4. Effens meer komplekse gevalle behels byvoorbeeld die generering van 'n versoek aan Kubernetes, wat 'n peul met die nodige houers skep, taakparameters daarheen deurgee, en na verwerking die houer ineenstort.

As 'n voorbeeld sal ons 'n variant van taak 1 maak, wanneer veranderinge in die Mail.ru Cloud Solutions (MCS) objekberging-emmer gesinchroniseer word in AWS-objekberging met behulp van webhooks. In 'n werklike gelaaide geval moet asynchrone werk verskaf word deur webhooks in 'n tou te registreer, maar vir die opleidingstaak sal ons die implementering daarsonder doen.

Werksskema

Die interaksieprotokol word in detail beskryf in Gids tot S3 webhooks op MCS. Die werkskema bevat die volgende elemente:

  • Uitgewersdiens, wat aan die S3-bergingkant is en HTTP-versoeke publiseer wanneer die webnhook geaktiveer word.
  • Webhook ontvang bediener, wat na versoeke van die HTTP-publiseringsdiens luister en toepaslike aksies uitvoer. Die bediener kan in enige taal geskryf word; in ons voorbeeld sal ons die bediener in Go skryf.

'n Spesiale kenmerk van die implementering van webhooks in die S3 API is die registrasie van die webhook-ontvangsbediener op die publikasiediens. In die besonder moet die webhook-ontvangsbediener die intekening op boodskappe van die publiseringsdiens bevestig (in ander webhook-implementerings word bevestiging van intekening gewoonlik nie vereis nie).

Gevolglik moet die webhook-ontvangsbediener twee hoofbewerkings ondersteun:

  • reageer op die uitgewersdiens se versoek om registrasie te bevestig,
  • inkomende gebeure verwerk.

Installering van 'n webhook-ontvangsbediener

Om die webhook-ontvangsbediener te laat loop, benodig jy 'n Linux-bediener. In hierdie artikel, as 'n voorbeeld, gebruik ons ​​'n virtuele instansie wat ons op MCS ontplooi.

Kom ons installeer die nodige sagteware en begin die webhook-ontvangsbediener.

ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install git
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  bc dns-root-data dnsmasq-base ebtables landscape-common liblxc-common 
liblxc1 libuv1 lxcfs lxd lxd-client python3-attr python3-automat 
python3-click python3-constantly python3-hyperlink
  python3-incremental python3-pam python3-pyasn1-modules 
python3-service-identity python3-twisted python3-twisted-bin 
python3-zope.interface uidmap xdelta3
Use 'sudo apt autoremove' to remove them.
Suggested packages:
  git-daemon-run | git-daemon-sysvinit git-doc git-el git-email git-gui 
gitk gitweb git-cvs git-mediawiki git-svn
The following NEW packages will be installed:
  git
0 upgraded, 1 newly installed, 0 to remove and 46 not upgraded.
Need to get 3915 kB of archives.
After this operation, 32.3 MB of additional disk space will be used.
Get:1 http://MS1.clouds.archive.ubuntu.com/ubuntu bionic-updates/main 
amd64 git amd64 1:2.17.1-1ubuntu0.7 [3915 kB]
Fetched 3915 kB in 1s (5639 kB/s)
Selecting previously unselected package git.
(Reading database ... 53932 files and directories currently installed.)
Preparing to unpack .../git_1%3a2.17.1-1ubuntu0.7_amd64.deb ...
Unpacking git (1:2.17.1-1ubuntu0.7) ...
Setting up git (1:2.17.1-1ubuntu0.7) ...

Kloon die gids met die webhook-ontvangsbediener:

ubuntu@ubuntu-basic-1-2-10gb:~$ git clone
https://github.com/RomanenkoDenys/s3-webhook.git
Cloning into 's3-webhook'...
remote: Enumerating objects: 48, done.
remote: Counting objects: 100% (48/48), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 114 (delta 20), reused 45 (delta 18), pack-reused 66
Receiving objects: 100% (114/114), 23.77 MiB | 20.25 MiB/s, done.
Resolving deltas: 100% (49/49), done.

Kom ons begin die bediener:

ubuntu@ubuntu-basic-1-2-10gb:~$ cd s3-webhook/
ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80

Teken in op uitgewersdiens

Jy kan jou webhook-ontvangsbediener registreer via die API of webkoppelvlak. Vir eenvoud sal ons via die webkoppelvlak registreer:

  1. Kom ons gaan na die emmers-afdeling in die beheerkamer.
  2. Gaan na die emmer waarvoor ons webhooks sal konfigureer en klik op die rat:

'n Voorbeeld van 'n gebeurtenisgedrewe toepassing gebaseer op webhooks in die S3-objekberging van Mail.ru Cloud Solutions

Gaan na die Webhooks-oortjie en klik Voeg by:

'n Voorbeeld van 'n gebeurtenisgedrewe toepassing gebaseer op webhooks in die S3-objekberging van Mail.ru Cloud Solutions
Vul die velde in:

'n Voorbeeld van 'n gebeurtenisgedrewe toepassing gebaseer op webhooks in die S3-objekberging van Mail.ru Cloud Solutions

ID — die naam van die webhook.

Gebeurtenis - watter gebeure om te versend. Ons het die oordrag gestel van alle gebeure wat plaasvind wanneer daar met lêers gewerk word (byvoeging en uitvee).

URL — webhook ontvang bedieneradres.

Filter voorvoegsel/agtervoegsel is 'n filter wat jou toelaat om webhooks te genereer slegs vir voorwerpe wie se name ooreenstem met sekere reëls. Byvoorbeeld, sodat die webhook slegs lêers met die .png-uitbreiding kan aktiveer, in Filter agtervoegsel jy moet "png" skryf.

Tans word slegs poorte 80 en 443 ondersteun vir toegang tot die webhook-ontvangsbediener.

Kom ons klik Voeg haak by en ons sal die volgende sien:

'n Voorbeeld van 'n gebeurtenisgedrewe toepassing gebaseer op webhooks in die S3-objekberging van Mail.ru Cloud Solutions
Haak bygevoeg.

Die webhook-ontvangsbediener wys in sy logboeke die vordering van die haakregistrasieproses:

ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80
2020/06/15 12:01:14 [POST] incoming HTTP request from 
95.163.216.92:42530
2020/06/15 12:01:14 Got timestamp: 2020-06-15T15:01:13+03:00 TopicArn: 
mcs5259999770|myfiles-ash|s3:ObjectCreated:*,s3:ObjectRemoved:* Token: 
E2itMqAMUVVZc51pUhFWSp13DoxezvRxkUh5P7LEuk1dEe9y URL: 
http://89.208.199.220/webhook
2020/06/15 12:01:14 Generate responce signature: 
3754ce36636f80dfd606c5254d64ecb2fd8d555c27962b70b4f759f32c76b66d

Registrasie is voltooi. In die volgende afdeling sal ons die algoritme van die werking van die webhook-ontvangsbediener van nader bekyk.

Beskrywing van die webhook-ontvangsbediener

In ons voorbeeld is die bediener in Go geskryf. Kom ons kyk na die basiese beginsels van die werking daarvan.

package main

// Generate hmac_sha256_hex
func HmacSha256hex(message string, secret string) string {
}

// Generate hmac_sha256
func HmacSha256(message string, secret string) string {
}

// Send subscription confirmation
func SubscriptionConfirmation(w http.ResponseWriter, req *http.Request, body []byte) {
}

// Send subscription confirmation
func GotRecords(w http.ResponseWriter, req *http.Request, body []byte) {
}

// Liveness probe
func Ping(w http.ResponseWriter, req *http.Request) {
    // log request
    log.Printf("[%s] incoming HTTP Ping request from %sn", req.Method, req.RemoteAddr)
    fmt.Fprintf(w, "Pongn")
}

//Webhook
func Webhook(w http.ResponseWriter, req *http.Request) {
}

func main() {

    // get command line args
    bindPort := flag.Int("port", 80, "number between 1-65535")
    bindAddr := flag.String("address", "", "ip address in dot format")
    flag.StringVar(&actionScript, "script", "", "external script to execute")
    flag.Parse()

    http.HandleFunc("/ping", Ping)
    http.HandleFunc("/webhook", Webhook)

log.Fatal(http.ListenAndServe(*bindAddr+":"+strconv.Itoa(*bindPort), nil))
}

Oorweeg die hooffunksies:

  • Ping() - 'n roete wat reageer via URL/ping, die eenvoudigste implementering van 'n lewendige sonde.
  • Webhook() - hoofroete, URL/webhook-hanteerder:
    • bevestig registrasie op die publikasiediens (gaan na die SubscriptionConfirmation-funksie),
    • verwerk inkomende webhooks (Gorecords-funksie).
  • Funksies HmacSha256 en HmacSha256hex is implementerings van die HMAC-SHA256 en HMAC-SHA256 enkripsiealgoritmes met uitvoer as 'n string heksadesimale getalle vir die berekening van die handtekening.
  • main is die hooffunksie, verwerk opdragreëlparameters en registreer URL-hanteerders.

Opdragreëlparameters wat deur die bediener aanvaar word:

  • -poort is die poort waarop die bediener sal luister.
  • -adres - IP-adres waarna die bediener sal luister.
  • -script is 'n eksterne program wat vir elke inkomende haak geroep word.

Kom ons kyk van naderby na sommige van die funksies:

//Webhook
func Webhook(w http.ResponseWriter, req *http.Request) {

    // Read body
    body, err := ioutil.ReadAll(req.Body)
    defer req.Body.Close()
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }

    // log request
    log.Printf("[%s] incoming HTTP request from %sn", req.Method, req.RemoteAddr)
    // check if we got subscription confirmation request
    if strings.Contains(string(body), 
""Type":"SubscriptionConfirmation"") {
        SubscriptionConfirmation(w, req, body)
    } else {
        GotRecords(w, req, body)
    }

}

Hierdie funksie bepaal of 'n versoek om registrasie te bevestig of 'n webhook aangekom het. Soos volg uit dokumentasie, as registrasie bevestig word, word die volgende Json-struktuur in die Pos-versoek ontvang:

POST http://test.com HTTP/1.1
x-amz-sns-messages-type: SubscriptionConfirmation
content-type: application/json

{
    "Timestamp":"2019-12-26T19:29:12+03:00",
    "Type":"SubscriptionConfirmation",
    "Message":"You have chosen to subscribe to the topic $topic. To confirm the subscription you need to response with calculated signature",
    "TopicArn":"mcs2883541269|bucketA|s3:ObjectCreated:Put",
    "SignatureVersion":1,
    "Token":«RPE5UuG94rGgBH6kHXN9FUPugFxj1hs2aUQc99btJp3E49tA»
}

Hierdie navraag moet beantwoord word:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Waar die handtekening bereken word as:

signature = hmac_sha256(url, hmac_sha256(TopicArn, 
hmac_sha256(Timestamp, Token)))

As 'n webhook arriveer, lyk die struktuur van die Post-versoek soos volg:

POST <url> HTTP/1.1
x-amz-sns-messages-type: SubscriptionConfirmation

{ "Records":
    [
        {
            "s3": {
                "object": {
                    "eTag":"aed563ecafb4bcc5654c597a421547b2",
                    "sequencer":1577453615,
                    "key":"some-file-to-bucket",
                    "size":100
                },
            "configurationId":"1",
            "bucket": {
                "name": "bucketA",
                "ownerIdentity": {
                    "principalId":"mcs2883541269"}
                },
                "s3SchemaVersion":"1.0"
            },
            "eventVersion":"1.0",
            "requestParameters":{
                "sourceIPAddress":"185.6.245.156"
            },
            "userIdentity": {
                "principalId":"2407013e-cbc1-415f-9102-16fb9bd6946b"
            },
            "eventName":"s3:ObjectCreated:Put",
            "awsRegion":"ru-msk",
            "eventSource":"aws:s3",
            "responseElements": {
                "x-amz-request-id":"VGJR5rtJ"
            }
        }
    ]
}

Gevolglik, afhangende van die versoek, moet jy verstaan ​​hoe om die data te verwerk. Ek het die inskrywing as 'n aanwyser gekies "Type":"SubscriptionConfirmation", aangesien dit teenwoordig is in die intekeningbevestigingsversoek en nie in die webhook teenwoordig is nie. Op grond van die teenwoordigheid/afwesigheid van hierdie inskrywing in die POST-versoek, gaan die verdere uitvoering van die program óf na die funksie SubscriptionConfirmation, of in 'n funksie GotRecords.

Ons sal nie die Subscription Confirmation-funksie in detail oorweeg nie; dit word geïmplementeer volgens die beginsels uiteengesit in dokumentasie. Jy kan die bronkode vir hierdie funksie sien by projek git-bewaarplekke.

Die GotRecords-funksie ontleed 'n inkomende versoek en roep vir elke Rekord-objek 'n eksterne skrif (wie se naam in die -script-parameter deurgegee is) met die parameters:

  • emmer naam
  • voorwerp sleutel
  • aksie:
    • kopie - indien in die oorspronklike versoek Gebeurtenisnaam = ObjectCreated | PutObject | PutObjectCopy
    • delete - if in the original request EventName = ObjectRemoved | Vee Voorwerp uit

Dus, as 'n haak opdaag met 'n Pos-versoek, soos beskryf bo, en die parameter -script=script.sh dan sal die script soos volg genoem word:

script.sh  bucketA some-file-to-bucket copy

Dit moet verstaan ​​word dat hierdie webhook-ontvangsbediener nie 'n volledige produksie-oplossing is nie, maar 'n vereenvoudigde voorbeeld van 'n moontlike implementering.

Werk voorbeeld

Kom ons sinchroniseer die lêers vanaf die hoofemmer in MCS na die rugsteunemmer in AWS. Die hoofemmer word myfiles-ash genoem, die rugsteun een word myfiles-backup genoem (emmerkonfigurasie in AWS is buite die bestek van hierdie artikel). Gevolglik, wanneer 'n lêer in die hoofemmer geplaas word, moet sy kopie in die rugsteun een verskyn, en wanneer dit van die hoof een uitgevee word, moet dit in die rugsteun een uitgevee word.

Ons sal met emmers werk deur die awscli-nutsding te gebruik, wat versoenbaar is met beide MCS-wolkberging en AWS-wolkberging.

ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install awscli
Reading package lists... Done
Building dependency tree
Reading state information... Done
After this operation, 34.4 MB of additional disk space will be used.
Unpacking awscli (1.14.44-1ubuntu1) ...
Setting up awscli (1.14.44-1ubuntu1) ...

Kom ons konfigureer toegang tot die S3 MCS API:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile mcs
AWS Access Key ID [None]: hdywEPtuuJTExxxxxxxxxxxxxx
AWS Secret Access Key [None]: hDz3SgxKwXoxxxxxxxxxxxxxxxxxx
Default region name [None]:
Default output format [None]:

Kom ons konfigureer toegang tot die AWS S3 API:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws configure --profile aws
AWS Access Key ID [None]: AKIAJXXXXXXXXXXXX
AWS Secret Access Key [None]: dfuerphOLQwu0CreP5Z8l5fuXXXXXXXXXXXXXXXX
Default region name [None]:
Default output format [None]:

Kom ons kyk na die toegang:

Aan AWS:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws s3 ls --profile aws
2020-07-06 08:44:11 myfiles-backup

Vir MCS, wanneer jy die opdrag uitvoer, moet jy —endpoint-url byvoeg:

ubuntu@ubuntu-basic-1-2-10gb:~$ aws s3 ls --profile mcs --endpoint-url 
https://hb.bizmrg.com
2020-02-04 06:38:05 databasebackups-0cdaaa6402d4424e9676c75a720afa85
2020-05-27 10:08:33 myfiles-ash

Toegang verkry.

Kom ons skryf nou 'n skrif vir die verwerking van die inkomende haak, kom ons noem dit s3_backup_mcs_aws.sh

#!/bin/bash
# Require aws cli
# if file added — copy it to backup bucket
# if file removed — remove it from backup bucket
# Variables
ENDPOINT_MCS="https://hb.bizmrg.com"
AWSCLI_MCS=`which aws`" --endpoint-url ${ENDPOINT_MCS} --profile mcs s3"
AWSCLI_AWS=`which aws`" --profile aws s3"
BACKUP_BUCKET="myfiles-backup"

SOURCE_BUCKET=""
SOURCE_FILE=""
ACTION=""

SOURCE="s3://${SOURCE_BUCKET}/${SOURCE_FILE}"
TARGET="s3://${BACKUP_BUCKET}/${SOURCE_FILE}"
TEMP="/tmp/${SOURCE_BUCKET}/${SOURCE_FILE}"

case ${ACTION} in
    "copy")
    ${AWSCLI_MCS} cp "${SOURCE}" "${TEMP}"
    ${AWSCLI_AWS} cp "${TEMP}" "${TARGET}"
    rm ${TEMP}
    ;;

    "delete")
    ${AWSCLI_AWS} rm ${TARGET}
    ;;

    *)
    echo "Usage: 
#!/bin/bash
# Require aws cli
# if file added — copy it to backup bucket
# if file removed — remove it from backup bucket
# Variables
ENDPOINT_MCS="https://hb.bizmrg.com"
AWSCLI_MCS=`which aws`" --endpoint-url ${ENDPOINT_MCS} --profile mcs s3"
AWSCLI_AWS=`which aws`" --profile aws s3"
BACKUP_BUCKET="myfiles-backup"
SOURCE_BUCKET="${1}"
SOURCE_FILE="${2}"
ACTION="${3}"
SOURCE="s3://${SOURCE_BUCKET}/${SOURCE_FILE}"
TARGET="s3://${BACKUP_BUCKET}/${SOURCE_FILE}"
TEMP="/tmp/${SOURCE_BUCKET}/${SOURCE_FILE}"
case ${ACTION} in
"copy")
${AWSCLI_MCS} cp "${SOURCE}" "${TEMP}"
${AWSCLI_AWS} cp "${TEMP}" "${TARGET}"
rm ${TEMP}
;;
"delete")
${AWSCLI_AWS} rm ${TARGET}
;;
*)
echo "Usage: ${0} sourcebucket sourcefile copy/delete"
exit 1
;;
esac
sourcebucket sourcefile copy/delete" exit 1 ;; esac

Kom ons begin die bediener:

ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80 -
script scripts/s3_backup_mcs_aws.sh

Kom ons kyk hoe dit werk. Deur MCS-webkoppelvlak voeg die test.txt-lêer by die myfiles-ash-emmer. Die konsolelogboeke wys dat 'n versoek aan die webhook-bediener gerig is:

2020/07/06 09:43:08 [POST] incoming HTTP request from 
95.163.216.92:56612
download: s3://myfiles-ash/test.txt to ../../../tmp/myfiles-ash/test.txt
upload: ../../../tmp/myfiles-ash/test.txt to 
s3://myfiles-backup/test.txt

Kom ons kyk na die inhoud van die myfiles-rugsteun-emmer in AWS:

ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ aws s3 --profile aws ls 
myfiles-backup
2020-07-06 09:43:10       1104 test.txt

Nou, deur die webkoppelvlak, sal ons die lêer uit die myfiles-ash-emmer uitvee.

Bediener logs:

2020/07/06 09:44:46 [POST] incoming HTTP request from 
95.163.216.92:58224
delete: s3://myfiles-backup/test.txt

Emmer inhoud:

ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ aws s3 --profile aws ls 
myfiles-backup
ubuntu@ubuntu-basic-1-2-10gb:~$

Die lêer is uitgevee, die probleem is opgelos.

Gevolgtrekking en ToDo

Alle kode wat in hierdie artikel gebruik word, is in my bewaarplek. Daar is ook voorbeelde van skrifte en voorbeelde van die tel van handtekeninge vir die registrasie van webhooks.

Hierdie kode is niks meer as 'n voorbeeld van hoe jy S3 webhooks in jou aktiwiteite kan gebruik nie. Soos ek aan die begin gesê het, as jy van plan is om so 'n bediener in produksie te gebruik, moet jy ten minste die bediener herskryf vir asynchrone werk: registreer inkomende webhooks in 'n tou (RabbitMQ of NATS), en van daar af ontleed en verwerk hulle met werkersaansoeke. Andersins, wanneer webhooks massief opdaag, kan u 'n gebrek aan bedienerhulpbronne ondervind om take te voltooi. Die teenwoordigheid van toue laat jou toe om die bediener en werkers te versprei, asook om probleme op te los met herhalende take in geval van mislukkings. Dit is ook raadsaam om die aantekening na 'n meer gedetailleerde en meer gestandaardiseerde een te verander.

Good Luck!

Meer leesstof oor die onderwerp:

Bron: will.com

Voeg 'n opmerking