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

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

Arhitektura vođena događajima povećava cijenu učinkovitosti korištenih resursa, jer se angažiraju samo u trenutku kada su potrebni. Postoji mnogo načina kako to implementirati bez stvaranja dodatnih cloud entiteta kao što su worker aplikacije. Danas neću govoriti o FaaS-u, već o webhookovima. Prikazat ću obrazovni primjer obrade događaja pomoću webhookova objektne pohrane.

Par riječi o objektnoj pohrani i o webhookovima. Objektne pohrane omogućuju pohranu bilo kakvih podataka u oblak u obliku objekata, dostupnih preko S3 ili drugog API-ja (ovisno o implementaciji) putem HTTP/HTTPS. Webhookovi (webhooks) u općenitom smislu su korisnički povratni pozivi preko HTTP-a. Obično ih pokreće događaj, kao što je slanje koda u repozitorij ili objava komentara na blogu. Kada se dogodi događaj, izvorna web stranica šalje HTTP zahtjev na URL adresu određenu za webhook. Kao rezultat toga, događaji na jednoj web stranici mogu pokrenuti akcije na drugoj. U slučaju kada je izvorna web stranica objektna pohrana, promjene njenog sadržaja djeluju kao događaji.wiki.

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

  1. Izrada kopija svih objekata u drugoj oblaku. Kopije trebaju biti izrađene 'u hodu', pri svakom dodavanju ili izmjeni datoteka.
  2. Automatsko stvaranje serija miniatura grafičkih datoteka, dodavanje vodenih žigova na fotografije, druge modifikacije slika.
  3. Obavijest o dolasku novih dokumenata (primjerice, distribucijska računovodstvena služba postavlja izvještaje u oblak, a financijski nadzor prima obavijesti o novim izvještajima, provjerava i analizira ih).
  4. Malo složeniji slučajevi podrazumijevaju, primjerice, formiranje zahtjeva za Kubernetes, koji stvara pod sa potrebnim kontejnerima, prenosi mu parametre zadatka i nakon obrade zatvara kontejner.

Kao primjer, razmotrit ćemo varijantu zadatka 1, kada se promjene u spremniku objektne pohrane Mail.ru Cloud Solutions (MCS) sinkroniziraju u objektno spremište AWS putem webhookova. U realnom opterećenom slučaju trebalo bi osigurati asinkroni rad registracijom webhookova u red, no za obrazovni zadatak implementirat ćemo rješenje bez toga.

Shema rada

Protokol interakcije detaljno je opisan u vodiču za S3 webhookove na MCS. U shemi rada postoje sljedeći elementi:

  • Servis objavljivanja, koji se nalazi na strani S3 spremišta i objavljuje HTTP zahtjeve kada se aktivira webhook.
  • Poslužitelj za prijem webhookova, koji sluša zahtjeve servisa objavljivanja putem HTTP-a i poduzima odgovarajuće radnje. Poslužitelj može biti napisan na bilo kojem jeziku, u našem primjeru napisat ćemo poslužitelj na Go.

Osobina implementacije webhookova u S3 API-u je registracija poslužitelja za prijem webhookova na servisu objavljivanja. Konkretno, poslužitelj za prijem webhookova mora potvrditi pretplatu na poruke servisa objavljivanja (u drugim implementacijama webhookova obično nije potrebna potvrda pretplate).

Prema tome, poslužitelj za prijem webhookova mora podržavati dvije osnovne operacije:

  • odgovarati na zahtjev servisa objavljivanja o potvrdi registracije,
  • obrađivati dolazne događaje.

Postavljanje poslužitelja za prijem webhookova

Za pokretanje poslužitelja za prijem webhookova potreban je Linux poslužitelj. U ovom članku kao primjer koristimo virtualni instancu koju postavljamo na MCS.

Instalirat ćemo potrebni softver i pokrenuti poslužitelj za prijem webhookova.

ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install git
Čitanje popisa paketa... Gotovo
Izgradnja stabla ovisnosti
Čitanje stanja informacija... Gotovo
Sljedeći paketi su automatski instalirani i više nisu potrebni:
 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
Upotrijebite 'sudo apt autoremove' za njihovo uklanjanje.
Preporučeni paketi:
 git-daemon-run | git-daemon-sysvinit git-doc git-el git-email git-gui 
gitk gitweb git-cvs git-mediawiki git-svn
Sljedeći NOVI paketi bit će instalirani:
 git
0 nadograđeno, 1 novo instalirano, 0 za ukloniti i 46 nije nadograđeno.
Trebate preuzeti 3915 kB arhiva.
Nakon ove operacije, dodatno 32.3 MB prostora na disku bit će zauzeto.
Preuzimanje:1 http://MS1.clouds.archive.ubuntu.com/ubuntu bionic-updates/main 
amd64 git amd64 1:2.17.1-1ubuntu0.7 [3915 kB]
Preuzeto 3915 kB u 1s (5639 kB/s)
Odabir prethodno neizabrane pakete git.
(Čitanje baze podataka ... trenutno je instalirano 53932 datoteka i direktorija.)
Priprema za raspakiranje .../git_12.17.1-1ubuntu0.7_amd64.deb ...
Raspakiravanje git (1:2.17.1-1ubuntu0.7) ...
Postavljanje git (1:2.17.1-1ubuntu0.7) ...

Kloniramo mapu s poslužiteljem za primanje webhooks:

ubuntu@ubuntu-basic-1-2-10gb:~$ git clone
https://github.com/RomanenkoDenys/s3-webhook.git
Klonira se u 's3-webhook'...
remote: Nabrajanje objekata: 48, gotovo.
remote: Brojanje objekata: 100% (48/48), gotovo.
remote: Komprimiranje objekata: 100% (27/27), gotovo.
remote: Ukupno 114 (delta 20), ponovno korišteno 45 (delta 18), pakirano-ponovno korišteno 66
Preuzima objekte: 100% (114/114), 23.77 MiB | 20.25 MiB/s, gotovi.
Rješavanje delta: 100% (49/49), gotovo.

Pokrenimo poslužitelj:

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

Pretplata na servis objavljivanja

Možete registrirati svoj poslužitelj za primanje webhooks putem API-ja ili web-sučelja. Za jednostavnost ćemo se registrirati putem web-sučelja:

  1. Idemo u odjeljak s bucketima u upravljačkoj konzoli.
  2. Ulazimo u bucket za koji ćemo postaviti webhooks i kliknemo na ikonu zupčastog kotačića:

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

Prelazimo na karticu Webhooks i kliknemo Dodaj:

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

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

ID — naziv webhooka.

Događaj — koja će se događanja prenositi. Postavili smo prijenos svih događanja koja se odvijaju prilikom rada s datotekama (dodavanje i brisanje).

URL — adresa poslužitelja za primanje webhooks.

Filter prefiksa/sufiksa — filter koji omogućava generiranje webhooks samo za objekte čija se imena podudaraju s određenim pravilima. Na primjer, da webhook radi samo za datoteke s ekstenzijom .png, u Filter sufiks treba napisati "png".

Trenutno su podržani samo portovi 80 i 443 za pristup poslužitelju za primanje webhooks.

Kliknemo Dodati hook i vidimo sljedeće:

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

Poslužitelj za primanje webhooks u logovima pokazuje postupak registracije hooka:

ubuntu@ubuntu-basic-1-2-10gb:~/s3-webhook$ sudo ./s3-webhook -port 80
2020/06/15 12:01:14 [POST] dolazni HTTP zahtjev s 
95.163.216.92:42530
2020/06/15 12:01:14 Primljen vremenski pečat: 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 Generirani potpis odgovora: 
3754ce36636f80dfd606c5254d64ecb2fd8d555c27962b70b4f759f32c76b66d

Registracija je završena. U sljedećem dijelu detaljnije ćemo razmotriti algoritam rada poslužitelja za primanje webhokova.

Opis poslužitelja za primanje webhokova

U našem primjeru, poslužitelj je napisan na Go jeziku. Razmotrit ćemo osnovne principe rada.

package main

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

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

// Pošalji potvrdu pretplate
func SubscriptionConfirmation(w http.ResponseWriter, req *http.Request, body []byte) {
}

// Pošalji potvrdu pretplate
func GotRecords(w http.ResponseWriter, req *http.Request, body []byte) {
}

// Provjera livosti
func Ping(w http.ResponseWriter, req *http.Request) {
    // logiraj zahtjev
    log.Printf("[%s] dolazni HTTP Ping zahtjev s %sn", req.Method, req.RemoteAddr)
    fmt.Fprintf(w, "Pongn")
}

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

func main() {

    // dohvati argumente iz naredbenog retka
    bindPort := flag.Int("port", 80, "broj između 1-65535")
    bindAddr := flag.String("address", "", "ip adresa u formatu točka")
    flag.StringVar(&actionScript, "script", "", "izvana skripta za izvršavanje")
    flag.Parse()

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

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

Razmotrit ćemo osnovne funkcije:

  • Ping() — ruta koja odgovara na URL /ping, najjednostavnija realizacija provjere livosti.
  • Webhook() — osnovna ruta, obrada za URL /webhook:
    • potvrđuje registraciju na usluzi objavljivanja (prelazi u funkciju SubscriptionConfirmation),
    • obrađuje dolazne webhokove (funkcija GotRecords).
  • Funkcije HmacSha256 i HmacSha256hex — realizacije algoritama šifriranja HMAC-SHA256 i HMAC-SHA256 s ishodom u obliku stringa 16-ih brojeva za izračun potpisa.
  • main — glavna funkcija, obrađuje parametre iz naredbenog retka i registrira obrađivače URL-a.

Parametri naredbenog retka koje poslužitelj prihvaća:

  • -port — port na kojem će poslužitelj slušati.
  • -address — IP adresa koju će poslužitelj slušati.
  • -script — vanjski program koji se poziva za svaki primljeni huk.

Pogledajmo bliže neke funkcije:

//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 što je primljeno — zahtjev za potvrdu registracije ili webhuk. Kao što proizlazi iz dokumentaciju, u slučaju potvrde registracije dolazi sljedeća struktura Json u Post zahtjevu:

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":"Odabrali ste da se pretplatite na temu $topic. Da biste potvrdili pretplatu, potrebno je da odgovorite sa izračunatim potpisom",
    "TopicArn":"mcs2883541269|bucketA|s3:ObjectCreated:Put",
    "SignatureVersion":1,
    "Token":"RPE5UuG94rGgBH6kHXN9FUPugFxj1hs2aUQc99btJp3E49tA"
}

Na ovaj zahtev treba odgovoriti:

content-type: application/json

{"signature":"ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af"}

Gde se potpis izračunava kao:

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

Ako dođe webhook, struktura Post-zahtjeva izgleda ovako:

POST  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, u zavisnosti od zahteva, potrebno je razumeti kako obraditi podatke. Kao indikator sam izabrao zapis "Type":"SubscriptionConfirmation", koji se pojavljuje u zahtevu za potvrdu pretplate i ne pojavljuje se u webhook-u. Na osnovu prisustva/odsustva ovog zapisa u POST-zahtjevu, dalja izvršenja programa prelazi ili u funkciju SubscriptionConfirmation, ili u funkciju GotRecords.

Funkciju SubscriptionConfirmation nećemo detaljno razmatrati, realizovana je prema principima izloženim u dokumentaciju. Izvorni kod ove funkcije možete proučiti u git-repozitorijumu projekta.

Funkcija GotRecords analizira dolazni zahtev i za svaki objekat Record poziva spoljašnju skriptu (čije je ime prosledjeno u parametru -script) sa parametrima:

  • ime kontejnera
  • ključ objekta
  • akcija:
    • copy — ako je u originalnom zahtevu EventName = ObjectCreated | PutObject | PutObjectCopy
    • delete — ako je u originalnom zahtevu EventName = ObjectRemoved | DeleteObject

Tako da, ako je stigao huc c Post-zahtjevom, kao što je opisano gore, i parametar -script=script.sh, skripta će se pozvati na sledeći način:

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

Treba razumjeti da je ovaj server za primanje webhookova samo pojednostavljeni primjer moguće implementacije, a ne završeno proizvodno rješenje.

Primjer rada

Sinhronizirati ćemo datoteke iz glavnog bucket-a u MCS u rezervni bucket u AWS-u. Glavni bucket se zove myfiles-ash, a rezervni — myfiles-backup (konfiguracija bucketa u AWS-u izlazi iz okvira ovog članka). Kada datoteka bude stavljena u glavni bucket, njezina kopija će se pojaviti u rezervnom, a kada se ukloni iz glavnog — bit će uklonjena i iz rezervnog.

Radit ćemo s bucketima koristeći alat awscli, koji je kompatibilan s obje, MCS i AWS cloud storage.

ubuntu@ubuntu-basic-1-2-10gb:~$ sudo apt-get install awscli
Čitanje popisa paketa... Gotovo
Izgradnja stabla zavisnosti
Čitanje stanja informacija... Gotovo
Nakon ove operacije, dodatnih 34.4 MB prostora na disku će biti korišteno.
Raspakiravanje awscli (1.14.44-1ubuntu1) ...
Postavljanje awscli (1.14.44-1ubuntu1) ...

Konfigurirajmo pristup API-ju S3 MCS:

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 API-ju S3 AWS:

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

K AWS-u:

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

Za MCS, prilikom korištenja komande treba 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

Pristup je dobiven.

Sada ćemo napisati skriptu za obradu dolaznog hooka, nazivaćemo je 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="${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

Pokrećemo server:

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

Provjerimo kako će to funkcionirati. Kroz web sučelje MCS dodajemo datoteku test.txt u bucket myfiles-ash. U logovima na konzoli vidimo da je poslan zahtjev na server webhookova:

2020/07/06 09:43:08 [POST] dolazni HTTP zahtjev s
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 bucketa myfiles-backup 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 kroz web sučelje ukloniti datoteku iz bucketa myfiles-ash.

Logovi servera:

2020/07/06 09:44:46 [POST] dolazni HTTP zahtjev s
95.163.216.92:58224
delete: s3://myfiles-backup/test.txt

Sadržaj bucketa:

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

Datoteka je uklonjena, zadatak je riješen.

Zaključak i ToDo

Sav kod korišten u ovom članku leži u mom repozitoriju. Ovdje su i primjeri skripti i primjeri izračuna potpisivanja za registraciju webhooka.

Ovaj kod je samo primjer kako možete koristiti S3 webhooks u svom radu. Kao što sam rekao na početku, ako planirate koristiti takav server u produkciji, potrebno je barem preraditi server za asinhroni rad: dolazne webhooks registrirati u redu (RabbitMQ ili NATS), a odatle ih obraditi worker aplikacijama. Inače, pri masovnom dolasku webhooks možete se suočiti s nedostatkom resursa servera za izvršavanje zadataka. Postojanje redova omogućava raspodjelu servera i workera, kao i rješavanje problema ponavljanja zadataka u slučaju grešaka. Također je poželjno promijeniti logiranje u detaljnije i standardiziranije.

Sretno!

Dodatno čitanje na temu:

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster