Primjer aplikacije vođene događajima temeljene na web dojavnicima u S3 pohrani objekata Mail.ru Cloud Solutions

Primjer aplikacije vođene događajima temeljene na web dojavnicima u S3 pohrani objekata Mail.ru Cloud Solutions
Rube Goldberg aparat za kavu

Arhitektura vođena događajima povećava troškovnu učinkovitost korištenih resursa jer se koriste samo u trenutku kada su potrebni. Postoji mnogo opcija kako to implementirati i ne stvarati dodatne entitete u oblaku kao radne aplikacije. A danas neću govoriti o FaaS-u, već o webhookovima. Pokazat ću primjer vodiča za rukovanje događajima pomoću web-dojavnika za pohranu objekata.

Nekoliko riječi o pohrani objekata i web-dojavnicima. Pohranjivanje objekata omogućuje pohranjivanje bilo kojih podataka u oblaku u obliku objekata, dostupnih putem S3 ili drugog API-ja (ovisno o implementaciji) putem HTTP/HTTPS. Webdojavnici su općenito prilagođeni HTTP povratni pozivi. Obično ih pokreće događaj, poput guranja koda u repozitorij ili objavljivanja komentara na blogu. Kada se dogodi događaj, izvorno mjesto šalje HTTP zahtjev na URL naveden za web-dojavnik. Kao rezultat toga, možete učiniti da događaji na jednom mjestu pokreću radnje na drugom (wiki). U slučaju kada je izvorno mjesto skladište objekta, događaji djeluju kao promjene njegovog sadržaja.

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

  1. Stvaranje kopija svih objekata u drugoj pohrani u oblaku. Kopije se moraju kreirati u hodu kad god se dodaju ili mijenjaju datoteke.
  2. Automatsko stvaranje niza minijatura grafičkih datoteka, dodavanje vodenih žigova fotografijama i druge izmjene slika.
  3. Obavijest o pristizanju novih dokumenata (npr. distribuirani računovodstveni servis učitava izvješća u oblak, a financijski nadzor prima obavijesti o novim izvješćima, provjerava ih i analizira).
  4. Malo složeniji slučajevi uključuju, primjerice, generiranje zahtjeva prema Kubernetesu, koji kreira pod s potrebnim spremnicima, prosljeđuje mu parametre zadatka i nakon obrade urušava spremnik.

Kao primjer, napravit ćemo varijantu zadatka 1, kada se promjene u kanti za pohranu objekata Mail.ru Cloud Solutions (MCS) sinkroniziraju u pohrani objekata AWS pomoću webhookova. U stvarnom opterećenom slučaju, asinkroni rad trebao bi se osigurati registriranjem web-dojavnika u redu čekanja, ali za zadatak obuke izvršit ćemo implementaciju bez toga.

Shema rada

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

  • Izdavačka usluga, koji je na strani S3 pohrane i objavljuje HTTP zahtjeve kada se aktivira webnhook.
  • Poslužitelj primanja Webhooka, koji sluša zahtjeve HTTP usluge objavljivanja i izvodi odgovarajuće radnje. Poslužitelj može biti napisan na bilo kojem jeziku; u našem primjeru, mi ćemo napisati poslužitelj na Go.

Posebnost implementacije webhookova u S3 API-ju je registracija poslužitelja za primanje webhooka na servis za objavljivanje. Konkretno, poslužitelj za primanje web-dojavnika mora potvrditi pretplatu na poruke usluge objavljivanja (u drugim implementacijama web-dojavnika potvrda pretplate obično nije potrebna).

Sukladno tome, poslužitelj koji prima webhook mora podržavati dvije glavne operacije:

  • odgovoriti na zahtjev izdavačke službe za potvrdu registracije,
  • obraditi dolazne događaje.

Instaliranje poslužitelja za primanje webhooka

Da biste pokrenuli primateljski poslužitelj webhooka, potreban vam je Linux poslužitelj. U ovom članku, kao primjer, koristimo virtualnu instancu koju implementiramo na MCS.

Instalirajmo potreban softver i pokrenimo poslužitelj 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 mapu s poslužiteljem primatelja web-dojavnika:

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 uslugu izdavanja

Poslužitelj primanja webhooka možete registrirati putem API-ja ili web sučelja. Radi jednostavnosti registraciju ćemo izvršiti putem web sučelja:

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

Primjer aplikacije vođene događajima temeljene na web dojavnicima u S3 pohrani objekata Mail.ru Cloud Solutions

Idite na karticu Webhooks i kliknite Dodaj:

Primjer aplikacije vođene događajima temeljene na web dojavnicima u S3 pohrani objekata Mail.ru Cloud Solutions
Ispunite polja:

Primjer aplikacije vođene događajima temeljene na web dojavnicima u S3 pohrani objekata Mail.ru Cloud Solutions

ID — naziv web-dojavnika.

Događaj - koje događaje prenositi. Postavili smo prijenos svih događaja koji se događaju pri radu s datotekama (dodavanje i brisanje).

URL — adresa poslužitelja primanja webhooka.

Prefiks/sufiks filtra je filtar koji vam omogućuje generiranje web dojavljivača samo za objekte čija imena odgovaraju određenim pravilima. Na primjer, kako bi webhook pokrenuo samo datoteke s nastavkom .png, u Sufiks filtra morate napisati "png".

Trenutačno su samo priključci 80 i 443 podržani za pristup poslužitelju za primanje web-dojavnika.

Kliknimo Dodajte kuku i vidjet ćemo sljedeće:

Primjer aplikacije vođene događajima temeljene na web dojavnicima u S3 pohrani objekata Mail.ru Cloud Solutions
Udica dodana.

Poslužitelj koji prima webhook prikazuje u svojim zapisima napredak procesa registracije hooka:

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 sljedećem odjeljku detaljnije ćemo pogledati algoritam rada poslužitelja za primanje webhooka.

Opis poslužitelja za primanje web-dojavnika

U našem primjeru, poslužitelj 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, najjednostavnija implementacija sonde živosti.
  • Webhook() - glavna ruta, URL/webhook rukovatelj:
    • potvrđuje registraciju na izdavačkom servisu (idite na funkciju SubscriptionConfirmation),
    • obrađuje dolazne webhookove (funkcija Gorecords).
  • Funkcije HmacSha256 i HmacSha256hex implementacije su algoritama za šifriranje HMAC-SHA256 i HMAC-SHA256 s izlazom kao nizom heksadecimalnih brojeva za izračun potpisa.
  • main je glavna funkcija, obrađuje parametre naredbenog retka i registrira URL rukovatelje.

Parametri naredbenog retka koje prihvaća poslužitelj:

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

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 utvrđuje je li stigao zahtjev za potvrdu registracije ili webhook. Kako slijedi iz dokumentacija, ako je registracija potvrđena, sljedeća Json struktura se prima 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":"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 potrebno je odgovoriti:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Gdje se potpis izračunava kao:

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

Ako stigne webhook, 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"
            }
        }
    ]
}

Sukladno tome, ovisno o zahtjevu, trebate razumjeti kako obrađivati ​​podatke. Odabrao sam unos kao indikator "Type":"SubscriptionConfirmation", jer je prisutan u zahtjevu za potvrdu pretplate, a nije prisutan u web-dojavniku. Na temelju prisutnosti/odsutnosti ovog unosa u POST zahtjevu, daljnje izvršavanje programa ide ili na funkciju SubscriptionConfirmation, ili u funkciju GotRecords.

Nećemo detaljno razmatrati funkciju SubscriptionConfirmation; implementirana je u skladu s načelima navedenim u dokumentacija. Možete vidjeti izvorni kod za ovu funkciju na projekt git spremišta.

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

  • naziv kante
  • ključ objekta
  • akcijski:
    • kopija - ako je u izvornom zahtjevu EventName = ObjectCreated | PutObject | PutObjectCopy
    • brisanje - ako je u izvornom zahtjevu EventName = ObjectRemoved | DeleteObject

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

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

Treba imati na umu da ovaj poslužitelj za primanje webhooka nije potpuno proizvodno rješenje, već pojednostavljeni primjer moguće implementacije.

Primjer rada

Sinkronizirajmo datoteke iz glavnog spremnika u MCS-u sa rezervnim spremnikom u AWS-u. Glavni spremnik zove se myfiles-ash, rezervni se zove myfiles-backup (konfiguracija spremnika u AWS-u je izvan opsega ovog članka). U skladu s tim, kada se datoteka stavi u glavnu kantu, njezina bi se kopija trebala pojaviti u sigurnosnoj kopiji, a kada se izbriše iz glavne, trebala bi se izbrisati u sigurnosnoj kopiji.

Radit ćemo sa spremnicima pomoću uslužnog programa awscli, koji je kompatibilan s MCS pohranom u oblaku i AWS pohranom u oblaku.

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, prilikom pokretanja naredbe trebate 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 dolazne kuke, nazovimo 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=""
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

Pogledajmo kako radi. Kroz MCS web sučelje dodajte datoteku test.txt u spremnik myfiles-ash. Dnevnici konzole pokazuju da je upućen zahtjev webhook poslužitelju:

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 spremnika 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 putem web sučelja izbrisati datoteku iz myfiles-ash spremnika.

Dnevnici poslužitelja:

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:~$

Datoteka je izbrisana, problem je riješen.

Zaključak i zadatak

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

Ovaj kod nije ništa drugo nego primjer kako možete koristiti S3 webhookove u svojim aktivnostima. Kao što sam rekao na početku, ako planirate koristiti takav poslužitelj u produkciji, trebate barem prepisati poslužitelj za asinkroni rad: registrirati dolazne webhookove u red čekanja (RabbitMQ ili NATS), a zatim ih analizirati i obraditi s prijavama radnika. U suprotnom, kada web-dojavnici stignu masovno, možete naići na nedostatak resursa poslužitelja za dovršavanje zadataka. Prisutnost redova omogućuje vam distribuciju poslužitelja i radnika, kao i rješavanje problema s ponavljanjem zadataka u slučaju kvarova. Također je preporučljivo promijeniti zapis u detaljniji i standardiziraniji.

Sretno!

Još čitanja na temu:

Izvor: www.habr.com

Dodajte komentar