Primjer aplikacije vođene događajima zasnovane na webhookovima u S3 objektnoj pohrani Mail.ru Cloud Solutions

Primjer aplikacije vođene događajima zasnovane na webhookovima u S3 objektnoj pohrani Mail.ru Cloud Solutions
Rube Goldberg aparat za kafu

Arhitektura vođena događajima povećava isplativost korišćenih resursa jer se koriste samo u trenutku kada su potrebni. Postoji mnogo opcija kako to implementirati i ne kreirati dodatne entitete u oblaku kao radne aplikacije. I danas ću govoriti ne o FaaS-u, već o webhookovima. Pokazat ću tutorial primjer rukovanja događajima koristeći web-hookove za pohranu objekata.

Nekoliko riječi o pohranjivanju objekata i webhookovima. Skladištenje objekata vam omogućava pohranjivanje svih podataka u oblaku u obliku objekata, dostupnih preko S3 ili drugog API-ja (ovisno o implementaciji) putem HTTP/HTTPS-a. Webhookovi su općenito prilagođeni HTTP povratni pozivi. Obično ih pokreće događaj, kao što je kod koji se gura u spremište ili komentar koji se postavlja na blog. Kada se dogodi neki događaj, izvorna stranica šalje HTTP zahtjev na URL naveden za webhook. Kao rezultat toga, možete učiniti da događaji na jednoj lokaciji pokreću radnje na drugoj (Wiki). U slučaju kada je izvorna stranica skladište objekata, događaji se ponašaju kao promjene njegovog sadržaja.

Primjeri jednostavnih slučajeva kada se takva automatizacija može koristiti:

  1. Kreiranje kopija svih objekata u drugom skladištu u oblaku. Kopije se moraju kreirati u hodu kad god se fajlovi dodaju ili mijenjaju.
  2. Automatsko kreiranje serije sličica grafičkih datoteka, dodavanje vodenih žigova fotografijama i druge modifikacije slika.
  3. Obavijest o pristizanju novih dokumenata (na primjer, distribuirani računovodstveni servis prenosi izvještaje u oblak, a finansijski monitoring prima obavijesti o novim izvještajima, provjerava ih i analizira).
  4. Nešto složeniji slučajevi uključuju, na primjer, generiranje zahtjeva za Kubernetes, koji kreira pod s potrebnim kontejnerima, prosljeđuje mu parametre zadatka i nakon obrade sažima kontejner.

Kao primjer, napravit ćemo varijantu zadatka 1, kada se promjene u Mail.ru Cloud Solutions (MCS) spremniku za pohranu objekata sinkroniziraju u AWS pohranu objekata pomoću webhookova. U stvarno učitanom slučaju, asinhroni rad bi trebao biti osiguran registracijom web-hukova u red čekanja, ali za zadatak obuke mi ćemo implementaciju odraditi bez toga.

Shema rada

Protokol interakcije je detaljno opisan u Vodič za S3 webhookove na MCS-u. Šema rada sadrži sljedeće elemente:

  • Izdavačka usluga, koji se nalazi na strani S3 memorije i objavljuje HTTP zahtjeve kada se pokrene webnhook.
  • Webhook server za prijem, koji sluša zahtjeve HTTP servisa za objavljivanje i izvodi odgovarajuće radnje. Server može biti napisan na bilo kom jeziku; u našem primjeru, server ćemo pisati u Go.

Posebna karakteristika implementacije webhookova u S3 API je registracija servera za primanje webhooka na servisu za objavljivanje. Konkretno, server koji prima webhook mora potvrditi pretplatu na poruke iz usluge objavljivanja (u drugim implementacijama webhook-a, potvrda pretplate obično nije potrebna).

U skladu s tim, server koji prima webhook mora podržavati dvije glavne operacije:

  • odgovori na zahtjev izdavačke službe za potvrdu registracije,
  • obraditi dolazeće događaje.

Instaliranje servera za primanje webhook-a

Da biste pokrenuli server za primanje webhook-a, potreban vam je Linux server. U ovom članku, kao primjer, koristimo virtuelnu instancu koju postavljamo na MCS.

Hajde da instaliramo neophodan softver i pokrenemo server za primanje webhooka.

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) ...

Klonirajte folder sa serverom za primanje webhook-a:

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.

Pokrenimo server:

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

Pretplatite se na izdavačku uslugu

Možete registrirati svoj server za primanje webhook-a putem API-ja ili web sučelja. Radi jednostavnosti, registrovaćemo se preko web interfejsa:

  1. Idemo na odjeljak za kante u kontrolnoj sobi.
  2. Idite na kantu za koju ćemo konfigurirati webhookove i kliknite na zupčanik:

Primjer aplikacije vođene događajima zasnovane na webhookovima u S3 objektnoj pohrani Mail.ru Cloud Solutions

Idite na karticu Webhooks i kliknite na Dodaj:

Primjer aplikacije vođene događajima zasnovane na webhookovima u S3 objektnoj pohrani Mail.ru Cloud Solutions
Popunite polja:

Primjer aplikacije vođene događajima zasnovane na webhookovima u S3 objektnoj pohrani Mail.ru Cloud Solutions

ID — naziv webhok-a.

Događaj - koje događaje prenijeti. Postavili smo prijenos svih događaja koji se javljaju pri radu sa fajlovima (dodavanje i brisanje).

URL — webhook adresa servera za prijem.

Prefiks/sufiks filtera je filter koji vam omogućava da generišete webhookove samo za objekte čija imena odgovaraju određenim pravilima. Na primjer, da bi webhook pokrenuo samo datoteke sa ekstenzijom .png, in Sufiks filtera potrebno je da napišete "png".

Trenutno su samo portovi 80 i 443 podržani za pristup serveru za primanje webhook-a.

Hajde da kliknemo Dodajte kuku i videcemo sledece:

Primjer aplikacije vođene događajima zasnovane na webhookovima u S3 objektnoj pohrani Mail.ru Cloud Solutions
Hook je dodao.

Server za primanje webhook-a pokazuje u svojim zapisnicima napredak procesa registracije kuke:

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

Registracija je završena. U sledećem odeljku ćemo detaljnije pogledati algoritam rada servera za primanje webhooka.

Opis servera za primanje webhook-a

U našem primjeru, server je napisan u Go. Pogledajmo osnovne principe njegovog rada.

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))
}

Razmotrite glavne funkcije:

  • Ping() - ruta koja odgovara putem URL-a/pinga, najjednostavnije implementacije sonde za živost.
  • Webhook() - glavna ruta, URL/webhook rukovalac:
    • potvrđuje registraciju na izdavačkoj usluzi (idite na funkciju SubscriptionConfirmation),
    • obrađuje dolazne webhookove (funkcija Gorecords).
  • Funkcije HmacSha256 i HmacSha256hex su implementacije algoritama za šifriranje HMAC-SHA256 i HMAC-SHA256 sa izlazom kao nizom heksadecimalnih brojeva za izračunavanje potpisa.
  • main je glavna funkcija, obrađuje parametre komandne linije i registruje URL rukovaoce.

Parametri komandne linije koje server prihvata:

  • -port je port na kojem će server slušati.
  • -address - IP adresa koju će server slušati.
  • -script je eksterni program koji se poziva za svaku dolaznu zakačicu.

Pogledajmo pobliže neke od funkcija:

//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)
    }

}

Ova funkcija određuje da li je stigao zahtjev za potvrdu registracije ili webhook. Kako slijedi iz dokumentaciju, ako je registracija potvrđena, sljedeća Json struktura se prima u zahtjevu za poštu:

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»
}

Na ovaj upit je potrebno odgovoriti:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Gdje se potpis računa kao:

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

Ako webhook stigne, struktura zahtjeva za objavu izgleda ovako:

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"
            }
        }
    ]
}

Shodno tome, ovisno o zahtjevu, morate razumjeti kako obrađivati ​​podatke. Odabrao sam unos kao indikator "Type":"SubscriptionConfirmation", budući da je prisutan u zahtjevu za potvrdu pretplate i nije prisutan u webhooku. Na osnovu prisustva/odsustva ovog unosa u POST zahtevu, dalje izvršavanje programa ide ili na funkciju SubscriptionConfirmation, ili u funkciju GotRecords.

Funkciju SubscriptionConfirmation nećemo razmatrati detaljno; ona se implementira prema principima navedenim u dokumentaciju. Izvorni kod za ovu funkciju možete pogledati na projektna git spremišta.

Funkcija GotRecords analizira dolazni zahtjev i za svaki objekt Record poziva eksternu skriptu (čije je ime proslijeđeno u parametru -script) s parametrima:

  • naziv kante
  • objektni ključ
  • akcija:
    • kopija - ako je u originalnom zahtjevu EventName = ObjectCreated | PutObject | PutObjectCopy
    • delete - ako je u originalnom zahtjevu EventName = ObjectRemoved | DeleteObject

Dakle, ako kuka stigne sa Post zahtjevom, kao što je opisano viši, i parametar -script=script.sh tada će skripta biti pozvana na sljedeći način:

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

Treba shvatiti da ovaj webhook server za primanje nije kompletno proizvodno rješenje, već pojednostavljeni primjer moguće implementacije.

Primjer rada

Hajde da sinhronizujemo fajlove iz glavnog segmenta u MCS-u u rezervni segment u AWS-u. Glavni bucket se zove myfiles-ash, rezervni se zove myfiles-backup (konfiguracija bucketa u AWS-u je izvan okvira ovog članka). Shodno tome, kada se datoteka stavi u glavnu kantu, njena kopija treba da se pojavi u rezervnoj, a kada se izbriše iz glavne, treba je izbrisati u rezervnoj.

Radit ćemo s buckets koristeći awscli uslužni program, koji je kompatibilan i sa MCS cloud storage i AWS cloud storage.

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) ...

Konfigurirajmo pristup S3 MCS API-ju:

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]:

Konfigurirajmo pristup AWS S3 API-ju:

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]:

Provjerimo pristupe:

Za AWS:

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

Za MCS, kada izvodite naredbu morate dodati —endpoint-url:

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

Pristupljeno.

Sada napišimo skriptu za obradu dolaznog hooka, nazovimo ga 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

Pokrenimo server:

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

Hajde da vidimo kako to radi. Kroz MCS web interfejs dodajte datoteku test.txt u korpu myfiles-ash. Dnevnici konzole pokazuju da je učinjen zahtjev webhook serveru:

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

Provjerimo sadržaj myfiles-backup bucketa u AWS-u:

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

Sada ćemo preko web interfejsa izbrisati datoteku iz myfiles-ash bucketa.

Dnevnici servera:

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

Sadržaj korpe:

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

Fajl je obrisan, problem je riješen.

Zaključak i ToDo

Sav kod korišten u ovom članku je u mom spremištu. Tu su i primjeri skripti i primjeri brojanja potpisa za registraciju webhok-ova.

Ovaj kod nije ništa drugo do primjer kako možete koristiti S3 webhookove u svojim aktivnostima. Kao što sam rekao na početku, ako planirate da koristite takav server u produkciji, morate barem prepisati server za asinhroni rad: registrirati dolazne web hookove u red čekanja (RabbitMQ ili NATS), a odatle ih analizirati i obraditi sa radničkim aplikacijama. U suprotnom, kada webhookovi stignu masovno, možete naići na nedostatak serverskih resursa za dovršavanje zadataka. Prisustvo redova vam omogućava distribuciju servera i radnika, kao i rješavanje problema s ponavljanjem zadataka u slučaju kvarova. Također je preporučljivo promijeniti evidenciju na detaljnije i standardiziranije.

Sretno!

Više čitanja na temu:

izvor: www.habr.com

Dodajte komentar