Пример апликације вођене догађајима засноване на веб-хуковима у С3 објектном складишту Маил.ру Цлоуд Солутионс

Пример апликације вођене догађајима засноване на веб-хуковима у С3 објектном складишту Маил.ру Цлоуд Солутионс
Рубе Голдберг апарат за кафу

Архитектура вођена догађајима повећава исплативост коришћених ресурса јер се користе само у тренутку када су потребни. Постоји много опција како то имплементирати и не креирати додатне ентитете у облаку као радне апликације. И данас ћу говорити не о ФааС-у, већ о веб-хуковима. Показаћу пример водича за руковање догађајима помоћу веб-хукова за складиштење објеката.

Неколико речи о складишту објеката и веб-хуковима. Складиштење објеката вам омогућава да складиштите све податке у облаку у облику објеката, доступних преко С3 или другог АПИ-ја (у зависности од имплементације) преко ХТТП/ХТТПС-а. Веб-хукови су генерално прилагођени ХТТП повратни позиви. Обично их покреће догађај, као што је прослеђивање кода у спремиште или објављивање коментара на блогу. Када дође до догађаја, изворна локација шаље ХТТП захтев на УРЛ наведен за веб-хук. Као резултат тога, можете учинити да догађаји на једном сајту покрећу радње на другом (вики). У случају када је изворни сајт складиште објеката, догађаји делују као промене његовог садржаја.

Примери једноставних случајева када се таква аутоматизација може користити:

  1. Прављење копија свих објеката у другом складишту у облаку. Копије се морају креирати у ходу кад год се датотеке додају или мењају.
  2. Аутоматско креирање серије сличица графичких датотека, додавање водених жигова фотографијама и друге модификације слика.
  3. Обавештење о пристизању нових докумената (на пример, дистрибуирани рачуноводствени сервис учитава извештаје у облак, а финансијски мониторинг прима обавештења о новим извештајима, проверава их и анализира).
  4. Нешто сложенији случајеви укључују, на пример, генерисање захтева за Кубернетес, који креира под са потребним контејнерима, прослеђује му параметре задатка, а након обраде сажима контејнер.

Као пример, направићемо варијанту задатка 1, када се измене у кофи за складиштење објеката Маил.ру Цлоуд Солутионс (МЦС) синхронизују у АВС складиште објеката помоћу веб-хукова. У стварно учитаном случају, асинхрони рад би требало да се обезбеди регистрацијом веб-хуквица у реду, али за задатак обуке ми ћемо имплементацију урадити без овога.

Схема рада

Протокол интеракције је детаљно описан у Водич за С3 веб-хукове на МЦС-у. Шема рада садржи следеће елементе:

  • Издавачка служба, који се налази на страни С3 складишта и објављује ХТТП захтеве када се покрене вебнхоок.
  • Вебхоок сервер за пријем, који слуша захтеве ХТТП сервиса за објављивање и извршава одговарајуће радње. Сервер може бити написан на било ком језику; у нашем примеру ћемо написати сервер у Го.

Посебна карактеристика имплементације вебхук-а у С3 АПИ-ју је регистрација сервера за пријем веб-хука на сервису за објављивање. Конкретно, сервер који прима веб-хук мора да потврди претплату на поруке из сервиса за објављивање (у другим имплементацијама веб-хука, потврда претплате обично није потребна).

Сходно томе, сервер који прима веб-хук мора да подржава две главне операције:

  • одговори на захтев издавачке службе за потврду регистрације,
  • обрадити долазеће догађаје.

Инсталирање сервера за пријем веб-хука

Да бисте покренули сервер за пријем веб-хука, потребан вам је Линук сервер. У овом чланку, као пример, користимо виртуелну инстанцу коју примењујемо на МЦС-у.

Хајде да инсталирамо потребан софтвер и покренемо сервер за пријем веб-хука.

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

Клонирајте фасциклу са сервером за пријем веб-хука:

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.

Покренимо сервер:

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

Претплатите се на издавачку услугу

Можете да региструјете свој сервер за пријем веб-хука преко АПИ-ја или веб интерфејса. Ради једноставности, регистроваћемо се преко веб интерфејса:

  1. Идемо у одељак за канте у контролној соби.
  2. Идите на корпу за коју ћемо конфигурисати веб-хукове и кликните на зупчаник:

Пример апликације вођене догађајима засноване на веб-хуковима у С3 објектном складишту Маил.ру Цлоуд Солутионс

Идите на картицу Вебхоокс и кликните на Додај:

Пример апликације вођене догађајима засноване на веб-хуковима у С3 објектном складишту Маил.ру Цлоуд Солутионс
Попуните поља:

Пример апликације вођене догађајима засноване на веб-хуковима у С3 објектном складишту Маил.ру Цлоуд Солутионс

ИД — назив вебхок-а.

Догађај - које догађаје пренети. Поставили смо пренос свих догађаја који се јављају при раду са датотекама (додавање и брисање).

УРЛ — адреса сервера за пријем веб-хук-а.

Префикс/суфикс филтера је филтер који вам омогућава да генеришете веб-хукове само за објекте чија имена одговарају одређеним правилима. На пример, да би веб-хук покренуо само датотеке са екстензијом .пнг, ин Суфикс филтера потребно је да напишете „пнг“.

Тренутно су подржани само портови 80 и 443 за приступ серверу за пријем веб-хука.

Хајде да кликнемо Додајте куку и видећемо следеће:

Пример апликације вођене догађајима засноване на веб-хуковима у С3 објектном складишту Маил.ру Цлоуд Солутионс
Хук је додао.

Сервер за пријем веб-хук-а показује у својим евиденцијама напредак процеса регистрације куке:

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

Регистрација је завршена. У следећем одељку ћемо детаљније погледати алгоритам рада сервера за пријем веб-хука.

Опис сервера за пријем веб-хука

У нашем примеру, сервер је написан у Го. Хајде да погледамо основне принципе његовог рада.

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

Размотрите главне функције:

  • Пинг() - рута која одговара преко УРЛ/пинга, најједноставнија имплементација сонде за живост.
  • Вебхоок() - главна рута, УРЛ/вебхоок руковалац:
    • потврђује регистрацију на сервису за издаваштво (идите на функцију СубсцриптионЦонфирматион),
    • обрађује долазне веб-хукове (функција Горецордс).
  • Функције ХмацСха256 и ХмацСха256хек су имплементације алгоритама за шифровање ХМАЦ-СХА256 и ХМАЦ-СХА256 са излазом као низом хексадецималних бројева за израчунавање потписа.
  • маин је главна функција, обрађује параметре командне линије и региструје УРЛ руковаоце.

Параметри командне линије које сервер прихвата:

  • -порт је порт на којем ће сервер слушати.
  • -аддресс - ИП адреса коју ће сервер слушати.
  • -сцрипт је екстерни програм који се позива за сваку долазну куку.

Хајде да детаљније погледамо неке од функција:

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

}

Ова функција одређује да ли је стигао захтев за потврду регистрације или веб-хук. Како следи из документација, ако је регистрација потврђена, следећа Јсон структура се прима у захтеву за поштавање:

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

На овај упит треба одговорити:

content-type: application/json

{"signature":«ea3fce4bb15c6de4fec365d36bcebbc34ccddf54616d5ca12e1972f82b6d37af»}

Где се потпис рачуна као:

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

Ако стигне веб-хук, структура захтева за објаву изгледа овако:

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

Сходно томе, у зависности од захтева, морате разумети како да обрађујете податке. Изабрао сам унос као индикатор "Type":"SubscriptionConfirmation", пошто је присутан у захтеву за потврду претплате и није присутан у веб-хуку. На основу присуства/одсуства овог уноса у ПОСТ захтеву, даље извршавање програма иде или на функцију SubscriptionConfirmation, или у функцију GotRecords.

Функцију СубсцриптионЦонфирматион нећемо разматрати детаљно; она се имплементира у складу са принципима наведеним у документација. Можете погледати изворни код за ову функцију на пројектна гит спремишта.

Функција ГотРецордс анализира долазни захтев и за сваки објекат Рецорд позива екстерну скрипту (чије је име прослеђено у параметру -сцрипт) са параметрима:

  • назив канте
  • кључ објекта
  • поступак:
    • копија - ако је у оригиналном захтеву ЕвентНаме = ОбјецтЦреатед | ПутОбјецт | ПутОбјецтЦопи
    • делете - ако је у оригиналном захтеву ЕвентНаме = ОбјецтРемовед | ДелетеОбјецт

Дакле, ако кука стигне са Пост захтевом, као што је описано изнад, и параметар -сцрипт=сцрипт.сх онда ће скрипта бити позвана на следећи начин:

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

Требало би разумети да овај сервер за пријем веб-хука није комплетно производно решење, већ упрошћени пример могуће имплементације.

Пример рада

Хајде да синхронизујемо датотеке из главног сегмента у МЦС-у у резервни део у АВС-у. Главни бацкет се зове мифилес-асх, резервни се зове мифилес-бацкуп (конфигурација буцкета у АВС-у је ван оквира овог чланка). Сходно томе, када се датотека стави у главну канту, њена копија треба да се појави у резервној, а када се избрише из главне, треба да се обрише у резервној.

Радићемо са буцкетс користећи услужни програм авсцли, који је компатибилан и са МЦС складиштем у облаку и са АВС складиштем у облаку.

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

Хајде да конфигуришемо приступ С3 МЦС АПИ-ју:

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

Хајде да конфигуришемо приступ АВС С3 АПИ-ју:

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

Хајде да проверимо приступе:

За АВС:

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

За МЦС, када покрећете команду, морате да додате —ендпоинт-урл:

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

Приступљено.

Хајде сада да напишемо скрипту за обраду долазног кука, назовимо га с3_бацкуп_мцс_авс.сх

#!/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

Покренимо сервер:

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

Хајде да видимо како то функционише. Кроз МЦС веб интерфејс додајте датотеку тест.ткт у корпу мифилес-асх. Евиденција конзоле показује да је захтев упућен вебхоок серверу:

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

Хајде да проверимо садржај мифилес-бацкуп корпе у АВС-у:

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

Сада ћемо преко веб интерфејса избрисати датотеку из мифилес-асх корпе.

Евиденције сервера:

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

Садржај корпе:

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

Датотека је избрисана, проблем је решен.

Закључак и задатак

Сав код који се користи у овом чланку је у мом спремишту. Постоје и примери скрипти и примери бројања потписа за регистрацију веб-хукова.

Овај код није ништа друго до пример како можете да користите С3 веб-хоокове у својим активностима. Као што сам рекао на почетку, ако планирате да користите такав сервер у производњи, потребно је да бар препишете сервер за асинхрони рад: да региструјете долазне веб-хукове у реду (РаббитМК или НАТС), а одатле их анализирате и обрађујете са радничким апликацијама. У супротном, када веб-хукови стигну масовно, можда ћете наићи на недостатак ресурса сервера за довршавање задатака. Присуство редова вам омогућава да дистрибуирате сервер и раднике, као и да решите проблеме са понављањем задатака у случају кварова. Такође је препоручљиво променити евиденцију на детаљније и стандардизованије.

Срећно!

Више читања на тему:

Извор: ввв.хабр.цом

Додај коментар