Primer aplikacije, ki temelji na dogodkih in temelji na webhookih v objektnem shranjevanju S3 rešitve Mail.ru Cloud Solutions

Primer aplikacije, ki temelji na dogodkih in temelji na webhookih v objektnem shranjevanju S3 rešitve Mail.ru Cloud Solutions
Kavni aparat Rube Goldberg

Dogodkovno vodena arhitektura povečuje stroškovno učinkovitost uporabljenih virov, saj se le-ti uporabljajo le v trenutku, ko so potrebni. Obstaja veliko možnosti, kako to implementirati in ne ustvariti dodatnih entitet v oblaku kot delovnih aplikacij. In danes ne bom govoril o FaaS, ampak o webhookih. Pokazal bom primer vadnice za obravnavo dogodkov z uporabo spletnih kavljev za shranjevanje objektov.

Nekaj ​​besed o shranjevanju objektov in webhookih. Shramba objektov vam omogoča shranjevanje poljubnih podatkov v oblaku v obliki objektov, dostopnih prek S3 ali drugega API-ja (odvisno od izvedbe) prek HTTP/HTTPS. Webhooki so na splošno prilagojeni povratni klici HTTP. Običajno jih sproži dogodek, na primer potisna koda v repozitorij ali objava komentarja na blogu. Ko pride do dogodka, izvorno mesto pošlje zahtevo HTTP na URL, naveden za webhook. Posledično lahko povzročite, da dogodki na enem mestu sprožijo dejanja na drugem (wiki). V primeru, da je izvorno mesto shramba objektov, dogodki delujejo kot spremembe njegove vsebine.

Primeri preprostih primerov, ko je mogoče uporabiti takšno avtomatizacijo:

  1. Ustvarjanje kopij vseh predmetov v drugi shrambi v oblaku. Kopije je treba ustvariti sproti, ko so datoteke dodane ali spremenjene.
  2. Samodejno ustvarjanje serije sličic grafičnih datotek, dodajanje vodnih žigov fotografijam in druge spremembe slik.
  3. Obveščanje o prispetju novih dokumentov (npr. porazdeljeni računovodski servis nalaga poročila v oblak, finančni nadzor pa prejema obvestila o novih poročilih, jih preverja in analizira).
  4. Nekoliko bolj zapleteni primeri vključujejo na primer generiranje zahteve do Kubernetesa, ki ustvari pod s potrebnimi vsebniki, mu posreduje parametre opravila in po obdelavi vsebnik zruši.

Kot primer bomo naredili različico naloge 1, ko se spremembe v vedru za shranjevanje objektov Mail.ru Cloud Solutions (MCS) sinhronizirajo v shrambi objektov AWS z uporabo spletnih kavljev. V realno naloženem primeru bi moralo biti asinhrono delo zagotovljeno z registracijo webhookov v čakalni vrsti, vendar bomo za nalogo usposabljanja implementacijo izvedli brez tega.

Shema dela

Protokol interakcije je podrobno opisan v Vodnik po S3 webhooks na MCS. Shema dela vsebuje naslednje elemente:

  • Založniška storitev, ki je na strani shrambe S3 in objavlja zahteve HTTP, ko se sproži webnhook.
  • Prejemni strežnik Webhook, ki posluša zahteve storitve objavljanja HTTP in izvaja ustrezna dejanja. Strežnik je lahko napisan v katerem koli jeziku; v našem primeru bomo strežnik napisali v Go.

Posebnost implementacije webhookov v S3 API je registracija sprejemnega strežnika webhookov na storitvi objavljanja. Strežnik za sprejem webhooka mora zlasti potrditi naročnino na sporočila storitve objavljanja (pri drugih izvedbah webhooka potrditev naročnine običajno ni potrebna).

V skladu s tem mora sprejemni strežnik webhook podpirati dve glavni operaciji:

  • odgovorite na zahtevo založniške službe za potrditev registracije,
  • obdelati dohodne dogodke.

Namestitev sprejemnega strežnika webhook

Če želite zagnati sprejemni strežnik webhook, potrebujete strežnik Linux. V tem članku kot primer uporabljamo virtualni primerek, ki ga namestimo na MCS.

Namestimo potrebno programsko opremo in zaženimo sprejemni strežnik webhook.

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 mapo s strežnikom za sprejem webhooka:

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.

Zaženemo strežnik:

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

Naročite se na založniško storitev

Prejemni strežnik webhook lahko registrirate prek API-ja ali spletnega vmesnika. Zaradi poenostavitve se bomo prijavili preko spletnega vmesnika:

  1. Pojdimo na razdelek z vedri v kontrolni sobi.
  2. Pojdite do vedra, za katerega bomo konfigurirali webhooke, in kliknite na zobnik:

Primer aplikacije, ki temelji na dogodkih in temelji na webhookih v objektnem shranjevanju S3 rešitve Mail.ru Cloud Solutions

Pojdite na zavihek Webhooks in kliknite Dodaj:

Primer aplikacije, ki temelji na dogodkih in temelji na webhookih v objektnem shranjevanju S3 rešitve Mail.ru Cloud Solutions
Izpolnite polja:

Primer aplikacije, ki temelji na dogodkih in temelji na webhookih v objektnem shranjevanju S3 rešitve Mail.ru Cloud Solutions

ID — ime webhooka.

Dogodek - katere dogodke posredovati. Nastavili smo prenos vseh dogodkov, ki se zgodijo pri delu z datotekami (dodajanje in brisanje).

URL — naslov strežnika za sprejem webhooka.

Predpona/pripona filtra je filter, ki vam omogoča ustvarjanje webhookov samo za predmete, katerih imena ustrezajo določenim pravilom. Na primer, da bi webhook sprožil samo datoteke s pripono .png, v Pripona filtra morate napisati "png".

Trenutno so za dostop do sprejemnega strežnika webhook podprta le vrata 80 in 443.

Kliknimo Dodajte kavelj in videli bomo naslednje:

Primer aplikacije, ki temelji na dogodkih in temelji na webhookih v objektnem shranjevanju S3 rešitve Mail.ru Cloud Solutions
Kavelj dodan.

Prejemni strežnik webhook v svojih dnevnikih prikazuje napredek postopka registracije kljuka:

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 končana. V naslednjem razdelku si bomo podrobneje ogledali algoritem delovanja sprejemnega strežnika webhook.

Opis sprejemnega strežnika webhook

V našem primeru je strežnik napisan v Go. Oglejmo si osnovna načela njegovega delovanja.

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

Razmislite o glavnih funkcijah:

  • Ping() - pot, ki se odziva prek URL-ja/pinga, najpreprostejša izvedba sonde živahnosti.
  • Webhook() - glavna pot, URL/upravljalnik webhooka:
    • potrdi registracijo na založniškem servisu (pojdite na funkcijo SubscriptionConfirmation),
    • obdeluje dohodne webhooke (funkcija Gorecords).
  • Funkciji HmacSha256 in HmacSha256hex sta implementaciji šifrirnih algoritmov HMAC-SHA256 in HMAC-SHA256 z izhodom kot nizom šestnajstiških števil za izračun podpisa.
  • main je glavna funkcija, obdeluje parametre ukazne vrstice in registrira obdelovalce URL-jev.

Parametri ukazne vrstice, ki jih sprejme strežnik:

  • -port so vrata, na katerih bo strežnik poslušal.
  • -address - IP naslov, ki ga bo strežnik poslušal.
  • -script je zunanji program, ki se kliče za vsako dohodno kljuko.

Oglejmo si podrobneje nekatere 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)
    }

}

Ta funkcija določa, ali je prispela zahteva za potrditev registracije ali webhook. Kot sledi iz dokumentacijo, če je registracija potrjena, je v zahtevi za objavo prejeta naslednja struktura Json:

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 to vprašanje je treba odgovoriti:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Pri čemer se podpis izračuna kot:

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

Če prispe webhook, je struktura zahteve za objavo videti takole:

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

V skladu s tem morate glede na zahtevo razumeti, kako obdelati podatke. Za indikator sem izbral vnos "Type":"SubscriptionConfirmation", ker je prisoten v zahtevi za potrditev naročnine in ni prisoten v webhooku. Glede na prisotnost/odsotnost tega vnosa v zahtevi POST gre nadaljnje izvajanje programa bodisi na funkcijo SubscriptionConfirmation, ali v funkcijo GotRecords.

Funkcije SubscriptionConfirmation ne bomo podrobno obravnavali; implementirana je v skladu z načeli, določenimi v dokumentacijo. Izvorno kodo za to funkcijo si lahko ogledate na repozitoriji projekta git.

Funkcija GotRecords razčleni dohodno zahtevo in za vsak objekt Record pokliče zunanji skript (katerega ime je bilo posredovano v parametru -script) s parametri:

  • ime vedra
  • predmetni ključ
  • dejanje:
    • kopija - če je v izvirni zahtevi ImeDogodka = ObjectCreated | PutObject | PutObjectCopy
    • izbriši - če je v prvotni zahtevi ImeDogodka = Odstranjen Objekt | DeleteObject

Torej, če kljuka prispe z zahtevo za objavo, kot je opisano zgorajin parameter -script=script.sh, potem bo skript poklican na naslednji način:

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

Razumeti je treba, da ta sprejemni strežnik webhook ni popolna proizvodna rešitev, ampak poenostavljen primer možne izvedbe.

Primer dela

Sinhronizirajmo datoteke iz glavnega vedra v MCS v rezervno vedro v AWS. Glavno vedro se imenuje myfiles-ash, rezervno pa se imenuje myfiles-backup (konfiguracija vedra v AWS je izven obsega tega članka). V skladu s tem, ko je datoteka postavljena v glavno vedro, mora biti njena kopija prikazana v rezervnem, in ko je izbrisana iz glavnega, mora biti izbrisana v varnostnem.

Z vedri bomo delali s pomočjo pripomočka awscli, ki je združljiv s shrambo v oblaku MCS in shrambo v oblaku AWS.

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

Konfigurirajmo dostop do API-ja AWS S3:

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

Preverimo dostope:

Za AWS:

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

Za MCS morate pri zagonu ukaza 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

Dostopano.

Zdaj pa napišimo skript za obdelavo dohodne kljuke, poimenujmo 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

Zaženemo strežnik:

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

Poglejmo, kako deluje. Skozi MCS spletni vmesnik dodajte datoteko test.txt v vedro myfiles-ash. Dnevniki konzole kažejo, da je bila zahteva poslana strežniku webhook:

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

Preverimo vsebino vedra myfiles-backup v 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

Zdaj bomo prek spletnega vmesnika izbrisali datoteko iz vedra myfiles-ash.

Dnevniki strežnika:

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

Vsebina vedra:

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

Datoteka je izbrisana, težava je rešena.

Zaključek in ToDo

Vsa koda, uporabljena v tem članku, je v mojem skladišču. Obstajajo tudi primeri skript in primeri štetja podpisov za registracijo webhookov.

Ta koda ni nič drugega kot primer, kako lahko uporabite S3 webhooks v svojih dejavnostih. Kot sem rekel na začetku, če nameravate tak strežnik uporabljati v produkciji, morate strežnik vsaj prepisati za asinhrono delo: registrirati dohodne webhooke v čakalno vrsto (RabbitMQ ali NATS) in jih od tam razčleniti in obdelati z delavskimi prijavami. V nasprotnem primeru, ko webhooki prihajajo množično, lahko naletite na pomanjkanje strežniških virov za dokončanje nalog. Prisotnost čakalnih vrst vam omogoča porazdelitev strežnika in delavcev ter reševanje težav s ponavljajočimi se nalogami v primeru napak. Priporočljivo je tudi spremeniti beleženje v podrobnejše in bolj standardizirano.

Srečno!

Več branja na temo:

Vir: www.habr.com

Dodaj komentar