Un esempiu di una applicazione guidata da l'avvenimentu basatu annantu à i webhooks in S3 object storage Mail.ru Cloud Solutions

Un esempiu di una applicazione guidata da l'avvenimentu basatu annantu à i webhooks in S3 object storage Mail.ru Cloud Solutions
Macchina da caffè Rube Goldberg

L'architettura guidata da l'avvenimentu aumenta l'efficienza di u costu di i risorse utilizati perchè sò usati solu in u mumentu quandu sò necessarii. Ci hè parechje opzioni nantu à cumu implementà questu è micca creà entità cloud supplementari cum'è applicazioni di travagliu. È oghje ùn parleraghju micca di FaaS, ma di webhooks. Mostreraghju un esempiu tutoriale di gestione di l'avvenimenti utilizendu webhooks di almacenamentu di oggetti.

Uni pochi parolle nantu à u almacenamentu di l'ughjettu è i webhooks. L'almacenamiento d'ughjettu permette di almacenà qualsiasi dati in u nuvulu in forma di oggetti, accessibile via S3 o un altru API (secondu l'implementazione) via HTTP / HTTPS. Webhooks sò generalmente callbacks HTTP persunalizati. Sò tipicamenti attivati ​​da un avvenimentu, cum'è u codice chì hè imbuttatu à un repository o un cumentu chì hè publicatu in un blog. Quandu si verifica un avvenimentu, u situ d'origine manda una dumanda HTTP à l'URL specificata per u webhook. In u risultatu, pudete fà avvenimenti in un situ attivate l'azzioni nantu à l'altru (lontana). In u casu induve u situ fonte hè un almacenamentu d'ughjettu, l'avvenimenti agisce cum'è cambiamenti à u so cuntenutu.

Esempii di casi simplici quandu una tale automatizazione pò esse usata:

  1. Crea copie di tutti l'uggetti in un altru almacenamentu in nuvola. Copie deve esse creatu nantu à a mosca ogni volta chì i schedari sò aghjuntu o cambiatu.
  2. Creazione automatica di una seria di miniature di fugliali grafici, aghjunghje filigrane à e fotografie, è altre mudificazioni di l'imaghjini.
  3. Notificazione annantu à l'arrivu di novi documenti (per esempiu, un serviziu di cuntabilità distribuitu carica rapporti à u nuvulu, è u monitoraghju finanziariu riceve notificazioni nantu à novi rapporti, cuntrolla è analizà).
  4. I casi un pocu più cumplessi implicanu, per esempiu, a generazione di una dumanda à Kubernetes, chì crea un pod cù i cuntenituri necessarii, passa i paràmetri di u compitu à ellu, è dopu a trasfurmazioni collapses the container.

Per esempiu, faremu una variante di u compitu 1, quandu i cambiamenti in u bucket d'almacenamiento d'oggetti di Mail.ru Cloud Solutions (MCS) sò sincronizati in l'almacenamiento d'oggetti AWS cù webhooks. In un veru casu caricatu, u travagliu asincronu deve esse furnitu registrendu webhooks in una fila, ma per u travagliu di furmazione faremu l'implementazione senza questu.

Schema di travagliu

U protocolu d'interazzione hè descrittu in dettagliu in Guida à i webhooks S3 nantu à MCS. U schema di travagliu cuntene i seguenti elementi:

  • serviziu di publicazione, chì si trova nantu à u latu di almacenamiento S3 è publica e dumande HTTP quandu u webnhook hè attivatu.
  • Servitore di ricezione Webhook, chì ascolta e dumande da u serviziu di publicazione HTTP è eseguisce l'azzioni appropritate. U servitore pò esse scrittu in ogni lingua; in u nostru esempiu, scriveremu u servitore in Go.

Una funziunalità particulari di l'implementazione di webhooks in l'API S3 hè a registrazione di u servitore di ricezione webhook in u serviziu di publicazione. In particulare, u servitore chì riceve u webhook deve cunfirmà l'abbonamentu à i missaghji da u serviziu di publicazione (in altre implementazioni di webhook, a cunferma di l'abbonamentu ùn hè generalmente micca necessariu).

Per quessa, u servitore di ricezione webhook deve sustene duie operazioni principali:

  • risponde à a dumanda di u serviziu di pubblicazione per cunfirmà a registrazione,
  • processà l'avvenimenti in entrata.

Installazione di un servitore di ricezione webhook

Per eseguisce u servitore di ricezione webhook, avete bisognu di un servitore Linux. In questu articulu, per esempiu, usemu una istanza virtuale chì implementemu in MCS.

Stallà u software necessariu è lanciamu u servitore di ricezione 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) ...

Clone u cartulare cù u servitore di ricezione webhook:

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.

Cuminciamu u servitore:

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

Abbonate à u serviziu di publicazione

Pudete registrà u vostru servitore di ricezione webhook via l'API o l'interfaccia web. Per simplicità, registremu via l'interfaccia web:

  1. Andemu à a sezione di buckets in a sala di cuntrollu.
  2. Andate à u bucket per quale cunfiguremu webhooks è cliccate nantu à l'ingranaggio:

Un esempiu di una applicazione guidata da l'avvenimentu basatu annantu à i webhooks in S3 object storage Mail.ru Cloud Solutions

Andate à a tabulazione Webhooks è cliccate Aggiungi:

Un esempiu di una applicazione guidata da l'avvenimentu basatu annantu à i webhooks in S3 object storage Mail.ru Cloud Solutions
Riempite i campi:

Un esempiu di una applicazione guidata da l'avvenimentu basatu annantu à i webhooks in S3 object storage Mail.ru Cloud Solutions

ID - u nome di u webhook.

Avvenimentu - quale avvenimenti trasmette. Avemu stabilitu a trasmissione di tutti l'avvenimenti chì si verificanu quandu u travagliu cù i schedari (aghjunghje è sguassà).

URL - webhook chì riceve l'indirizzu di u servitore.

Prefissu / suffissu di filtru hè un filtru chì vi permette di generà webhooks solu per l'uggetti chì i nomi currispondenu à certe regule. Per esempiu, per u webhook per attivà solu i schedari cù l'estensione .png, in Filtru suffissu avete bisognu di scrive "png".

Attualmente, solu i porti 80 è 443 sò supportati per accede à u servitore di ricezione webhook.

Facemu cliccà Aghjunghjite un ganciu è videremu i seguenti:

Un esempiu di una applicazione guidata da l'avvenimentu basatu annantu à i webhooks in S3 object storage Mail.ru Cloud Solutions
Hook aghjustatu.

U servitore di ricezione di u webhook mostra in i so logs u prugressu di u prucessu di registrazione di u ganciu:

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

A registrazione hè cumpleta. In a sezione dopu, avemu da piglià un ochju più vicinu à l'algoritmu di u funziunamentu di u servitore di ricezione webhook.

Descrizzione di u servitore di ricezione webhook

In u nostru esempiu, u servitore hè scrittu in Go. Fighjemu i principii basi di u so funziunamentu.

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

Cunsiderate e funzioni principali:

  • Ping () - una strada chì risponde via URL / ping, l'implementazione più simplice di una sonda di vivacità.
  • Webhook () - rotta principale, URL / gestore webhook:
    • cunfirma a registrazione nantu à u serviziu di pubblicazione (andà à a funzione SubscriptionConfirmation),
    • processa i webhooks in entrata (funzione Gorecords).
  • E funzioni HmacSha256 è HmacSha256hex sò implementazioni di l'algoritmi di crittografia HMAC-SHA256 è HMAC-SHA256 cù output cum'è una stringa di numeri esadecimali per calculà a firma.
  • main hè a funzione principale, processa i paràmetri di a linea di cumanda è registra i gestori di URL.

Paràmetri di linea di cumanda accettati da u servitore:

  • -port hè u portu nantu à quale u servitore stà à sente.
  • -address - indirizzu IP chì u servitore ascolterà.
  • -script hè un prugramma esternu chì hè chjamatu per ogni hook entrante.

Fighjemu un ochju più vicinu à alcune di e funzioni:

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

}

Questa funzione determina se una dumanda per cunfirmà a registrazione o un webhook hè ghjuntu. Comu seguita da ducumentazione, Se a registrazione hè cunfirmata, a seguente struttura Json hè ricevutu in a dumanda Post:

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

Sta dumanda deve esse risposta:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Induve a firma hè calculata cum'è:

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

Se un webhook arriva, a struttura di a dumanda di Post hè cusì:

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

In cunsiquenza, secondu a dumanda, avete bisognu di capiscenu cumu processà i dati. Aghju sceltu l'entrata cum'è indicatore "Type":"SubscriptionConfirmation", postu chì hè presente in a dumanda di cunferma di abbunamentu è ùn hè micca presente in u webhook. Basatu nantu à a presenza / assenza di sta entrata in a dumanda POST, l'esekzione ulteriore di u prugramma passa sia à a funzione SubscriptionConfirmation, o in a funzione GotRecords.

Ùn cunsideremu micca a funzione SubscriptionConfirmation in dettagliu; hè implementata secondu i principii stabiliti in ducumentazione. Pudete vede u codice fonte per sta funzione à repository git di prughjettu.

A funzione GotRecords analizza una dumanda in entrata è per ogni ughjettu Record chjama un script esternu (chì u nome hè statu passatu in u paràmetru -script) cù i paràmetri:

  • nome di bucket
  • chjave di l'ughjettu
  • azzione:
    • copia - se in a dumanda originale EventName = ObjectCreated | PutObject | PutObjectCopy
    • sguassà - se in a dumanda originale EventName = ObjectRemoved | Elimina l'Oggettu

Cusì, se un ganciu arriva cù una dumanda Post, cum'è descritta Lingua, è u paràmetru -script=script.sh allora u script serà chjamatu cusì:

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

Hè da esse capitu chì stu servitore di ricezione webhook ùn hè micca una suluzione cumpleta di produzzione, ma un esempiu simplificatu di una implementazione pussibule.

Esempiu di travagliu

Sincronizemu i fugliali da u bucket principale in MCS à u bucket di salvezza in AWS. U bucket principale hè chjamatu myfiles-ash, a copia di salvezza hè chjamata myfiles-backup (a cunfigurazione di bucket in AWS hè fora di u scopu di questu articulu). In cunsiquenza, quandu un schedariu hè piazzatu in u bucket principale, a so copia deve cumparisce in a copia di salvezza, è quandu hè sguassata da u principale, deve esse sguassata in quella di salvezza.

Travaglieremu cù buckets usendu l'utilità awscli, chì hè cumpatibile cù u almacenamentu in nuvola MCS è u almacenamentu in nuvola 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) ...

Configuremu l'accessu à l'API 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]:

Configuremu l'accessu à l'API 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]:

Cuntrollamu l'accessi:

À AWS:

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

Per MCS, quandu eseguite u cumandimu avete bisognu di aghjunghje -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

Accessu.

Avà scrivemu un script per processà u ganciu in entrata, chjamemu 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

Cuminciamu u servitore:

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

Videmu cumu si travaglia. À traversu Interfaccia web MCS aghjunghje u schedariu test.txt à u bucket myfiles-ash. I logs di a cunsola mostranu chì una dumanda hè stata fatta à u servitore 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

Cuntrollamu u cuntenutu di u bucket myfiles-backup 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

Avà, attraversu l 'interfaccia web, avemu da sguassà u schedariu da u bucket myfiles-ash.

Logs di u servitore:

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

Cuntenutu di u bucket:

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

U schedariu hè sguassatu, u prublema hè risolta.

Conclusioni è ToDo

Tuttu u codice utilizatu in questu articulu hè in u mo repository. Ci sò ancu esempi di scripts è esempi di cuntà signatures per registrà webhooks.

Stu codice hè nunda di più chè un esempiu di cumu si pò aduprà S3 webhooks in u vostru attività. Cumu l'aghju dettu à u principiu, se pensa à aduprà un tali servitore in a produzzione, avete bisognu di almenu riscrive u servitore per u travagliu asincronu: registrà i webhooks entranti in una fila (RabbitMQ o NATS), è da quì analizà è processanu. cù l'applicazioni di u travagliu. Altrimenti, quandu i webhooks arrivanu massivamente, pudete scontru una mancanza di risorse di u servitore per cumpiendu i travaglii. A prisenza di fila permette di distribuisce u servitore è i travagliadori, è ancu di risolve i prublemi cù a ripetizione di i travaglii in casu di fallimenti. Hè ancu cunsigliatu di cambià u logging à un più detallatu è standardizatu.

Bona furtuna!

Più leghje nantu à u tema:

Source: www.habr.com

Add a comment