Esimerkki tapahtumapohjaisesta sovelluksesta, joka perustuu webhookeihin S3-objektitallennustilassa Mail.ru Cloud Solutions

Esimerkki tapahtumapohjaisesta sovelluksesta, joka perustuu webhookeihin S3-objektitallennustilassa Mail.ru Cloud Solutions
Rube Goldberg kahvinkeitin

Tapahtumalähtöinen arkkitehtuuri lisää käytettyjen resurssien kustannustehokkuutta, koska niitä käytetään vain silloin, kun niitä tarvitaan. On monia vaihtoehtoja, kuinka tämä voidaan toteuttaa ja olla luomatta uusia pilvikokonaisuuksia työntekijäsovelluksiksi. Ja tänään en puhu FaaS:stä vaan webhookeista. Näytän opetusesimerkin tapahtumien käsittelystä objektitallennus webhookeja käyttämällä.

Muutama sana objektin varastoinnista ja webhookeista. Objektien tallennus mahdollistaa kaiken datan tallentamisen pilveen objektien muodossa, joihin pääsee S3:n tai muun API:n kautta (toteutuksesta riippuen) HTTP/HTTPS:n kautta. Webhookit ovat yleensä mukautettuja HTTP-puheluita. Ne laukaisevat yleensä tapahtuman, kuten koodin siirtämisen arkistoon tai kommentin lähettämisen blogiin. Kun tapahtuma tapahtuu, alkuperäsivusto lähettää HTTP-pyynnön webhookille määritettyyn URL-osoitteeseen. Tämän seurauksena voit saada tapahtumat yhdellä sivustolla käynnistämään toimintoja toisella (wiki). Jos lähdesivusto on objektitallennus, tapahtumat toimivat muutoksina sen sisältöön.

Esimerkkejä yksinkertaisista tapauksista, joissa tällaista automaatiota voidaan käyttää:

  1. Luodaan kopioita kaikista objekteista toisessa pilvitallennustilassa. Kopiot on luotava lennossa aina, kun tiedostoja lisätään tai muutetaan.
  2. Graafisten tiedostojen pikkukuvien sarjan automaattinen luominen, vesileimojen lisääminen valokuviin ja muut kuvan muokkaukset.
  3. Ilmoitus uusien asiakirjojen saapumisesta (esimerkiksi hajautettu kirjanpitopalvelu lataa raportit pilveen ja talousseuranta vastaanottaa ilmoitukset uusista raporteista, tarkistaa ja analysoi ne).
  4. Hieman monimutkaisempiin tapauksiin kuuluu esimerkiksi pyynnön luominen Kubernetesille, joka luo podin tarvittavilla säilöillä, välittää sille tehtäväparametrit ja käsittelyn jälkeen tiivistää kontin.

Esimerkkinä teemme muunnelman tehtävästä 1, kun muutokset Mail.ru Cloud Solutions (MCS) -objektien tallennussäilössä synkronoidaan AWS-objektien tallennustilassa webhookien avulla. Todellisessa ladatussa tapauksessa asynkroninen työ tulee tarjota rekisteröimällä webhookit jonoon, mutta koulutustehtävässä toteutamme toteutuksen ilman tätä.

Työohjelma

Vuorovaikutusprotokolla on kuvattu yksityiskohtaisesti kohdassa Opas S3-webhookeihin MCS:ssä. Työsuunnitelma sisältää seuraavat elementit:

  • Kustannuspalvelu, joka on S3-tallennuspuolella ja julkaisee HTTP-pyynnöt, kun webnhook laukeaa.
  • Webhook-vastaanottopalvelin, joka kuuntelee HTTP-julkaisupalvelun pyyntöjä ja suorittaa tarvittavat toimenpiteet. Palvelin voidaan kirjoittaa millä tahansa kielellä; esimerkissämme kirjoitamme palvelimen Go-kielellä.

Webhookien toteutuksen erityispiirre S3 API:ssa on webhookin vastaanottavan palvelimen rekisteröinti julkaisupalveluun. Erityisesti webhookin vastaanottavan palvelimen on vahvistettava julkaisupalvelun viestien tilaaminen (muissa webhook-toteutuksissa tilauksen vahvistusta ei yleensä vaadita).

Näin ollen webhookin vastaanottavan palvelimen on tuettava kahta päätoimintoa:

  • vastata julkaisupalvelun pyyntöön vahvistaa rekisteröinti,
  • käsitellä saapuvia tapahtumia.

Webhook-vastaanottopalvelimen asentaminen

Webhook-vastaanottopalvelimen suorittamiseen tarvitaan Linux-palvelin. Tässä artikkelissa käytämme esimerkkinä virtuaalista ilmentymää, jonka otamme käyttöön MCS:ssä.

Asennetaan tarvittavat ohjelmistot ja käynnistetään webhook-vastaanottopalvelin.

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

Kloonaa kansio webhookin vastaanottavalla palvelimella:

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.

Aloitetaan palvelin:

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

Tilaa julkaisupalvelu

Voit rekisteröidä webhook-vastaanottopalvelimesi API:n tai verkkokäyttöliittymän kautta. Yksinkertaisuuden vuoksi rekisteröidymme verkkokäyttöliittymän kautta:

  1. Siirrytään kauhat-osioon valvomossa.
  2. Siirry ämpäriin, jolle määritämme webhookit, ja napsauta ratasta:

Esimerkki tapahtumapohjaisesta sovelluksesta, joka perustuu webhookeihin S3-objektitallennustilassa Mail.ru Cloud Solutions

Siirry Webhooks-välilehdelle ja napsauta Lisää:

Esimerkki tapahtumapohjaisesta sovelluksesta, joka perustuu webhookeihin S3-objektitallennustilassa Mail.ru Cloud Solutions
Täytä kentät:

Esimerkki tapahtumapohjaisesta sovelluksesta, joka perustuu webhookeihin S3-objektitallennustilassa Mail.ru Cloud Solutions

ID — webhookin nimi.

Tapahtuma - mitkä tapahtumat lähetetään. Olemme asettaneet kaikkien tiedostojen käsittelyssä tapahtuvien tapahtumien lähettämisen (lisääminen ja poistaminen).

URL — Webhook vastaanottavan palvelimen osoite.

Suodattimen etuliite/liite on suodatin, jonka avulla voit luoda webhookeja vain objekteille, joiden nimet vastaavat tiettyjä sääntöjä. Esimerkiksi, jotta webhook käynnistäisi vain .png-tunnisteella varustetut tiedostot Suodattimen pääte sinun on kirjoitettava "png".

Tällä hetkellä vain portteja 80 ja 443 tuetaan webhook-vastaanottopalvelimen käyttämiseen.

Napsautetaan Lisää koukku ja näemme seuraavaa:

Esimerkki tapahtumapohjaisesta sovelluksesta, joka perustuu webhookeihin S3-objektitallennustilassa Mail.ru Cloud Solutions
Koukku lisäsi.

Webhook-vastaanottopalvelin näyttää lokeissaan koukun rekisteröintiprosessin edistymisen:

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

Ilmoittautuminen on valmis. Seuraavassa osiossa tarkastellaan lähemmin webhook-vastaanottopalvelimen toiminta-algoritmia.

Webhook-vastaanottopalvelimen kuvaus

Esimerkissämme palvelin on kirjoitettu Go. Katsotaanpa sen toiminnan perusperiaatteita.

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

Harkitse päätoimintoja:

  • Ping() - reitti, joka vastaa URL-osoitteen/pingin kautta, joka on elävyyden mittauksen yksinkertaisin toteutus.
  • Webhook() - pääreitti, URL-/webhook-käsittelijä:
    • vahvistaa rekisteröitymisen julkaisupalveluun (siirry tilausvahvistustoimintoon),
    • käsittelee saapuvat webhookit (Gorecords-toiminto).
  • Funktiot HmacSha256 ja HmacSha256hex ovat HMAC-SHA256- ja HMAC-SHA256-salausalgoritmien toteutuksia, joiden lähtö on heksadesimaalilukujen merkkijono allekirjoituksen laskemista varten.
  • main on päätoiminto, käsittelee komentoriviparametreja ja rekisteröi URL-käsittelijät.

Palvelimen hyväksymät komentoriviparametrit:

  • -portti on portti, jota palvelin kuuntelee.
  • -osoite - IP-osoite, jota palvelin kuuntelee.
  • -script on ulkoinen ohjelma, jota kutsutaan jokaiselle saapuvalle hookille.

Tarkastellaanpa tarkemmin joitain toimintoja:

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

}

Tämä toiminto määrittää, onko saapunut rekisteröinnin vahvistuspyyntö vai webhook. Kuten seuraa dokumentointi, jos rekisteröinti vahvistetaan, seuraava Json-rakenne vastaanotetaan lähetyspyyntöön:

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

Tähän kyselyyn on vastattava:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Jos allekirjoitus lasketaan seuraavasti:

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

Jos webhook saapuu, lähetyspyynnön rakenne näyttää tältä:

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

Pyynnöstä riippuen sinun on siis ymmärrettävä, kuinka tietoja käsitellään. Valitsin merkinnän indikaattoriksi "Type":"SubscriptionConfirmation", koska se on mukana tilauksen vahvistuspyynnössä eikä webhookissa. Tämän merkinnän läsnäolon/puuttumisen perusteella POST-pyynnössä ohjelman jatkosuoritus menee joko funktiolle SubscriptionConfirmation, tai toimintoon GotRecords.

Emme käsittele Tilausvahvistus-toimintoa yksityiskohtaisesti, vaan se toteutetaan kohdassa esitettyjen periaatteiden mukaisesti dokumentointi. Voit tarkastella tämän toiminnon lähdekoodia osoitteessa projekti git arkistot.

GotRecords-funktio jäsentää saapuvan pyynnön ja jokaiselle Record-objektille kutsuu ulkoista komentosarjaa (jonka nimi välitettiin -script-parametrissa) parametrein:

  • kauhan nimi
  • objektiavain
  • toiminta:
    • kopioi - jos alkuperäisessä pyynnössä EventName = ObjectCreated | PutObject | PutObjectCopy
    • poista - jos alkuperäisessä pyynnössä EventName = ObjectRemoved | PoistaObject

Näin ollen, jos koukku saapuu lähetyspyynnön kanssa, kuten on kuvattu edellä, ja parametri -script=script.sh, komentosarjaa kutsutaan seuraavasti:

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

On ymmärrettävä, että tämä webhook-vastaanottopalvelin ei ole täydellinen tuotantoratkaisu, vaan yksinkertaistettu esimerkki mahdollisesta toteutuksesta.

Esimerkki työstä

Synkronoidaan tiedostot MCS:n pääsäilystä AWS:n varmuuskopiosäilöön. Pääsäilön nimi on myfiles-ash, varmuuskopio on myfiles-backup (AWS-säihön määritys ei kuulu tämän artikkelin piiriin). Vastaavasti kun tiedosto asetetaan pääsäilöön, sen kopion pitäisi näkyä varmuuskopiossa ja kun se poistetaan pääsäilystä, se tulee poistaa varmuuskopiosta.

Työskentelemme kauhojen kanssa käyttämällä awscli-apuohjelmaa, joka on yhteensopiva sekä MCS-pilvitallennustilan että AWS-pilvitallennustilan kanssa.

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

Määritetään pääsy S3 MCS -sovellusliittymään:

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

Määritetään pääsy AWS S3 -sovellusliittymään:

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

Tarkistamme pääsyt:

AWS:lle:

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

MCS:ssä komentoa suoritettaessa sinun on lisättävä -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

Käytetty.

Nyt kirjoitetaan komentosarja saapuvan koukun käsittelemiseksi, kutsutaan sitä nimellä 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

Aloitetaan palvelin:

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

Katsotaan kuinka se toimii. Kautta MCS-verkkokäyttöliittymä lisää test.txt-tiedosto myfiles-ash -säilöyn. Konsolin lokit osoittavat, että webhook-palvelimelle on tehty pyyntö:

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

Tarkastetaan AWS:n myfiles-backup-säilön sisältö:

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

Nyt poistamme tiedoston verkkokäyttöliittymän kautta myfiles-ash-ämpäristä.

Palvelimen lokit:

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

Kauhan sisältö:

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

Tiedosto on poistettu, ongelma on ratkaistu.

Johtopäätös ja tehtävät

Kaikki tässä artikkelissa käytetty koodi on arkistossani. Siellä on myös esimerkkejä komentosarjoista ja esimerkkejä allekirjoitusten laskemisesta webhookien rekisteröintiä varten.

Tämä koodi on vain esimerkki siitä, kuinka voit käyttää S3-webhookeja toiminnassasi. Kuten alussa sanoin, jos aiot käyttää tällaista palvelinta tuotannossa, sinun on ainakin kirjoitettava palvelin uudelleen asynkronista työtä varten: rekisteröidä saapuvat webhookit jonoon (RabbitMQ tai NATS), ja sieltä tulee jäsentää ja käsitellä ne. työntekijäsovellusten kanssa. Muuten, kun webhookeja saapuu massiivisesti, saatat kohdata palvelinresurssien puutteen tehtävien suorittamiseen. Jonojen läsnäolon avulla voit jakaa palvelimen ja työntekijät sekä ratkaista toistuvien tehtävien ongelmat vikatilanteissa. On myös suositeltavaa muuttaa kirjaus yksityiskohtaisempaan ja standardoidumpaan.

Good Luck!

Lisää luettavaa aiheesta:

Lähde: will.com

Lisää kommentti