Mail.ru Cloud Solutions S3 нысан қоймасындағы веб-хуктарға негізделген оқиғаға негізделген қолданбаның мысалы.

Mail.ru Cloud Solutions S3 нысан қоймасындағы веб-хуктарға негізделген оқиғаға негізделген қолданбаның мысалы.
Rube Goldberg кофе машинасы

Оқиғаға негізделген архитектура пайдаланылған ресурстардың үнемділігін арттырады, өйткені олар қажет болған сәтте ғана пайдаланылады. Мұны іске асыру және қосымша бұлттық нысандарды жұмысшы қолданбалары ретінде жасамаудың көптеген нұсқалары бар. Ал бүгін мен FaaS туралы емес, вебхуктар туралы сөйлесетін боламын. Мен объектілерді сақтау веб-хуктарын пайдаланып оқиғаларды өңдеудің оқулық мысалын көрсетемін.

Объектілерді сақтау және вебхуктар туралы бірнеше сөз. Нысандарды сақтау кез келген деректерді бұлтта HTTP/HTTPS арқылы S3 немесе басқа API (іске асыруға байланысты) арқылы қол жетімді нысандар түрінде сақтауға мүмкіндік береді. Вебхуктар әдетте реттелетін HTTP кері шақырулары болып табылады. Олар әдетте репозиторийге кодты жіберу немесе блогта жарияланған түсініктеме сияқты оқиға арқылы іске қосылады. Оқиға орын алған кезде, бастапқы сайт вебхук үшін көрсетілген URL мекенжайына HTTP сұрауын жібереді. Нәтижесінде бір сайттағы оқиғаларды басқа сайтта әрекеттерді іске қоса аласыз (уики). Бастапқы торап нысан қоймасы болған жағдайда, оқиғалар оның мазмұнына өзгертулер ретінде әрекет етеді.

Мұндай автоматтандыруды қолдануға болатын қарапайым жағдайлардың мысалдары:

  1. Басқа бұлттық қоймадағы барлық нысандардың көшірмелерін жасау. Файлдар қосылған немесе өзгертілген кезде көшірмелер жылдам жасалуы керек.
  2. Графикалық файлдардың нобайлар сериясын автоматты түрде жасау, фотосуреттерге су белгілерін қосу және кескіннің басқа модификациялары.
  3. Жаңа құжаттардың келуі туралы хабарлама (мысалы, таратылған бухгалтерлік қызмет есептерді бұлтқа жүктейді, ал қаржылық мониторинг жаңа есептер туралы хабарландыруларды алады, оларды тексереді және талдайды).
  4. Сәл күрделірек жағдайлар, мысалы, Kubernetes-ке сұрауды жасауды қамтиды, ол қажетті контейнерлері бар подкаст жасайды, оған тапсырма параметрлерін береді және өңдеуден кейін контейнерді бұзады.

Мысал ретінде, Mail.ru Cloud Solutions (MCS) нысанын сақтау шелегіндегі өзгерістер AWS нысан қоймасында веб-ілмектерді пайдаланып синхрондалған кезде 1-тапсырманың нұсқасын жасаймыз. Нақты жүктелген жағдайда асинхронды жұмыс веб-хуктарды кезекте тіркеу арқылы қамтамасыз етілуі керек, бірақ оқыту тапсырмасы үшін біз онсыз жүзеге асырамыз.

Жұмыс схемасы

Өзара әрекеттесу хаттамасы егжей-тегжейлі сипатталған MCS жүйесіндегі S3 вебхуктарына арналған нұсқаулық. Жұмыс схемасы келесі элементтерді қамтиды:

  • Баспа қызметі, ол S3 сақтау жағында орналасқан және webnhook іске қосылғанда HTTP сұрауларын жариялайды.
  • Webhook қабылдау сервері, ол HTTP жариялау қызметінің сұрауларын тыңдайды және сәйкес әрекеттерді орындайды. Серверді кез келген тілде жазуға болады, біздің мысалда біз серверді Go бағдарламасында жазамыз.

S3 API-де вебхуктарды жүзеге асырудың ерекше ерекшелігі вебхукты қабылдау серверін жариялау қызметінде тіркеу болып табылады. Атап айтқанда, веб-хук қабылдау сервері жариялау қызметінен хабарларға жазылуды растауы керек (басқа веб-хук енгізулерінде жазылуды растау әдетте талап етілмейді).

Тиісінше, вебхук қабылдау сервері екі негізгі операцияны қолдауы керек:

  • тіркеуді растау туралы баспа қызметінің сұрауына жауап беру,
  • кіріс оқиғаларын өңдеу.

Webhook қабылдау серверін орнату

Webhook қабылдау серверін іске қосу үшін сізге Linux сервері қажет. Бұл мақалада мысал ретінде біз MCS жүйесінде қолданатын виртуалды дананы қолданамыз.

Қажетті бағдарламалық құралды орнатып, вебхук қабылдау серверін іске қосамыз.

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

Баспа қызметіне жазылыңыз

Webhook қабылдау серверін API немесе веб-интерфейс арқылы тіркеуге болады. Қарапайымдылық үшін біз веб-интерфейс арқылы тіркелеміз:

  1. Шелектер бөліміне барайық басқару бөлмесінде.
  2. Біз веб-хуктарды конфигурациялайтын шелекке өтіп, беріліс түймесін басыңыз:

Mail.ru Cloud Solutions S3 нысан қоймасындағы веб-хуктарға негізделген оқиғаға негізделген қолданбаның мысалы.

Webhooks қойындысына өтіп, Қосу түймесін басыңыз:

Mail.ru Cloud Solutions S3 нысан қоймасындағы веб-хуктарға негізделген оқиғаға негізделген қолданбаның мысалы.
Өрістерді толтырыңыз:

Mail.ru Cloud Solutions S3 нысан қоймасындағы веб-хуктарға негізделген оқиғаға негізделген қолданбаның мысалы.

ID — вебхуктың аты.

Оқиға – қандай оқиғаларды жіберу керек. Біз файлдармен жұмыс істегенде (қосу және жою) орын алатын барлық оқиғаларды жіберуді орнаттық.

URL — сервер адресін қабылдайтын вебхук.

Сүзгі префиксі/суффикс – атаулары белгілі ережелерге сәйкес келетін нысандар үшін ғана веб-хуктарды жасауға мүмкіндік беретін сүзгі. Мысалы, веб-хук тек .png кеңейтімі бар файлдарды іске қосуы үшін Сүзгі жұрнағы «png» жазу керек.

Қазіргі уақытта вебхук қабылдау серверіне кіру үшін тек 80 және 443 порттарына қолдау көрсетіледі.

Басайық Ілмек қосыңыз және біз мынаны көреміз:

Mail.ru Cloud Solutions S3 нысан қоймасындағы веб-хуктарға негізделген оқиғаға негізделген қолданбаның мысалы.
Ілмек қосылды.

Вебхукты қабылдау сервері журналдарында ілмектерді тіркеу процесінің барысын көрсетеді:

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

Тіркеу аяқталды. Келесі бөлімде біз вебхук қабылдау серверінің жұмыс істеу алгоритмін егжей-тегжейлі қарастырамыз.

Webhook қабылдау серверінің сипаттамасы

Біздің мысалда сервер Go тілінде жазылған. Оның жұмыс істеуінің негізгі принциптерін қарастырайық.

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

Негізгі функцияларды қарастырыңыз:

  • Ping() – URL/ping арқылы жауап беретін маршрут, жандылық зондының ең қарапайым орындалуы.
  • Webhook() - негізгі маршрут, URL/webhook өңдеушісі:
    • баспа қызметіне тіркеуді растайды (Жазылымды растау функциясына өтіңіз),
    • кіріс веб-хуктарды өңдейді (Gorecords функциясы).
  • HmacSha256 және HmacSha256hex функциялары қолтаңбаны есептеуге арналған он алтылық сандар жолы ретінде шығысы бар HMAC-SHA256 және HMAC-SHA256 шифрлау алгоритмдерін іске асыру болып табылады.
  • main — негізгі функция, пәрмен жолы параметрлерін өңдейді және URL өңдеушілерін тіркейді.

Сервер қабылдаған пәрмен жолы параметрлері:

  • -порт - сервер тыңдайтын порт.
  • -адрес - сервер тыңдайтын IP мекенжайы.
  • -скрипт - бұл әрбір кіріс ілгегі үшін шақырылатын сыртқы бағдарлама.

Кейбір функцияларды толығырақ қарастырайық:

//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 сұрауында келесі 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»
}

Бұл сұраққа жауап беру керек:

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", өйткені ол жазылымды растау сұрауында бар және вебхукта жоқ. POST сұрауында осы жазбаның болуы/болмауы негізінде бағдарламаның әрі қарай орындалуы не функцияға өтеді SubscriptionConfirmation, немесе функцияға GotRecords.

Біз Жазылымды растау функциясын егжей-тегжейлі қарастырмаймыз, ол мына жерде көрсетілген принциптерге сәйкес жүзеге асырылады. құжаттама. Бұл функцияның бастапқы кодын мына жерден көре аласыз жоба git репозиторийлері.

GotRecords функциясы кіріс сұрауды талдайды және әрбір Record нысаны үшін параметрлері бар сыртқы сценарийді (оның аты -script параметрінде жіберілген) шақырады:

  • шелек атауы
  • нысан кілті
  • әрекет:
    • көшіру - егер бастапқы сұрауда EventName = ObjectCreated | PutObject | PutObjectCopy
    • жою - егер бастапқы сұрауда EventName = ObjectRemoved | DeleteObject

Осылайша, егер ілмек сипатталғандай Post сұрауымен келсе жоғары, және -script=script.sh параметрі болса, сценарий келесідей шақырылады:

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

Бұл вебхукты қабылдау сервері толық өндірістік шешім емес, мүмкін іске асырудың жеңілдетілген мысалы екенін түсіну керек.

Жұмыстың мысалы

Файлдарды MCS жүйесіндегі негізгі шелектен AWS жүйесіндегі сақтық көшірме шелегіне синхрондаймыз. Негізгі шелек myfiles-ash деп аталады, сақтық көшірме myfiles-backup деп аталады (AWS ішіндегі шелек конфигурациясы осы мақаланың ауқымынан тыс). Тиісінше, файл негізгі шелекке орналастырылған кезде оның көшірмесі сақтық көшірмеде пайда болуы керек, ал негізгіден жойылған кезде ол сақтық көшірмеде жойылуы керек.

Біз MCS бұлттық қоймасымен және AWS бұлттық қоймасымен үйлесімді awscli қызметтік бағдарламасын пайдаланып шелектермен жұмыс істейміз.

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

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

AWS S3 API қолжетімділігін конфигурациялайық:

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

Кірулерді тексерейік:

AWS үшін:

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

MCS үшін пәрменді іске қосу кезінде —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

Қолжетімді.

Енді кіріс ілгекті өңдеуге арналған сценарий жазайық, оны 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

Серверді бастайық:

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

Оның қалай жұмыс істейтінін көрейік. арқылы MCS веб-интерфейсі test.txt файлын myfiles-kül шелегіне қосыңыз. Консоль журналдары вебхук серверіне сұрау жасалғанын көрсетеді:

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

AWS жүйесіндегі myfiles-сақтық көшірме шелегінің мазмұнын тексерейік:

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

Енді веб-интерфейс арқылы біз файлды myfiles-ash шелекінен жоямыз.

Сервер журналдары:

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

Файл жойылды, мәселе шешілді.

Қорытынды және тапсырмалар

Осы мақалада қолданылатын барлық код менің репозиторийімде. Сондай-ақ сценарийлердің мысалдары және веб-хуктарды тіркеу үшін қолдарды санау мысалдары бар.

Бұл код сіздің әрекеттеріңізде S3 веб-хуктарын қалай пайдалануға болатынының мысалынан басқа ештеңе емес. Басында айтқанымдай, мұндай серверді өндірісте пайдалануды жоспарласаңыз, кем дегенде асинхронды жұмыс үшін серверді қайта жазуыңыз керек: кіріс веб-хуктарды кезекте (RabbitMQ немесе NATS) тіркеңіз және сол жерден оларды талдап, өңдеңіз. жұмысшы қолданбаларымен. Әйтпесе, веб-хуктар жаппай келген кезде тапсырмаларды орындау үшін сервер ресурстарының жетіспеушілігіне тап болуыңыз мүмкін. Кезектердің болуы сервер мен жұмысшыларды таратуға, сондай-ақ сәтсіздіктер жағдайында қайталанатын тапсырмалармен проблемаларды шешуге мүмкіндік береді. Сондай-ақ журналды неғұрлым егжей-тегжейлі және стандартталғанға ауыстырған жөн.

Сәттілік тілейміз!

Тақырып бойынша көбірек оқу:

Ақпарат көзі: www.habr.com

пікір қалдыру