{"id":307527,"date":"2020-07-27T09:00:28","date_gmt":"2020-07-27T09:00:28","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=307527"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=307527","title":{"rendered":"Apache Airflow: \u0434\u0435\u043b\u0430\u0435\u043c ETL \u043f\u0440\u043e\u0449\u0435"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/512386\/\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u044f \u0414\u043c\u0438\u0442\u0440\u0438\u0439 \u041b\u043e\u0433\u0432\u0438\u043d\u0435\u043d\u043a\u043e \u2014 Data Engineer \u043e\u0442\u0434\u0435\u043b\u0430 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438 \u0433\u0440\u0443\u043f\u043f\u044b \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439 \u00ab\u0412\u0435\u0437\u0451\u0442\u00bb.<\/p>\n<p>  <\/p>\n<p>\u042f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c \u043e \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u2014 Apache Airflow. \u041d\u043e Airflow \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u0435\u043d \u0438 \u043c\u043d\u043e\u0433\u043e\u0433\u0440\u0430\u043d\u0435\u043d, \u0447\u0442\u043e \u0432\u0430\u043c \u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u0438\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c\u0441\u044f \u043a \u043d\u0435\u043c\u0443 \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0435\u0441\u044c \u043f\u043e\u0442\u043e\u043a\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0438\u043c\u0435\u0435\u0442\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0438 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0438\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0418 \u0434\u0430, \u044f \u0431\u0443\u0434\u0443 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c, \u043d\u043e \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c: \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435 \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u0432 \u0438 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0439.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/lf\/59\/ve\/lf59vefopsudfwc2ynlpomu_ntq.png\"><br \/>  <em>\u0427\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u0438\u0434\u0438\u0448\u044c, \u043a\u043e\u0433\u0434\u0430 \u0433\u0443\u0433\u043b\u0438\u0448\u044c \u0441\u043b\u043e\u0432\u043e Airflow \/ Wikimedia Commons<\/em><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"vvedenie\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>  <\/p>\n<p>Apache Airflow \u2014 \u043e\u043d \u043f\u0440\u044f\u043c\u043e \u043a\u0430\u043a Django:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 Python,<\/li>\n<li>\u0435\u0441\u0442\u044c \u043e\u0442\u043b\u0438\u0447\u043d\u0430\u044f \u0430\u0434\u043c\u0438\u043d\u043a\u0430,<\/li>\n<li>\u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c,<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u043b\u0443\u0447\u0448\u0435, \u0434\u0430 \u0438 \u0441\u0434\u0435\u043b\u0430\u043d \u0441\u043e\u0432\u0441\u0435\u043c \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u0438\u0445 \u0446\u0435\u043b\u0435\u0439, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e (\u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u0434\u043e \u043a\u0430\u0442\u0430): <\/p>\n<p>  <\/p>\n<ul>\n<li>\u0437\u0430\u043f\u0443\u0441\u043a \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0437\u0430\u0434\u0430\u0447 \u043d\u0430 \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u043c\u0430\u0448\u0438\u043d (\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0430\u043c \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 Celery\/Kubernetes \u0438 \u0432\u0430\u0448\u0430 \u0441\u043e\u0432\u0435\u0441\u0442\u044c) <\/li>\n<li>\u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 workflow \u0438\u0437 \u043e\u0447\u0435\u043d\u044c \u043b\u0435\u0433\u043a\u043e\u0433\u043e \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438 \u0432\u043e\u0441\u043f\u0440\u0438\u044f\u0442\u0438\u044f Python-\u043a\u043e\u0434\u0430<\/li>\n<li>\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433 \u0441 \u0434\u0440\u0443\u0433 \u043b\u044e\u0431\u044b\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 API \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u0430\u043a \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u0442\u0430\u043a \u0438 \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 (\u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0447\u0440\u0435\u0437\u0432\u044b\u0447\u0430\u0439\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e).<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Apache Airflow \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432 (\u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432 SQL Server \u0438 PostgreSQL, \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 API \u0441 \u043c\u0435\u0442\u0440\u0438\u043a\u0430\u043c\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0434\u0430\u0436\u0435 1\u0421) \u0432 DWH \u0438 ODS (\u0443 \u043d\u0430\u0441 \u044d\u0442\u043e Vertica \u0438 Clickhouse).<\/li>\n<li>\u043a\u0430\u043a \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439 <code>cron<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u043a\u043e\u043d\u0441\u043e\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 ODS, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u043b\u0435\u0434\u0438\u0442 \u0437\u0430 \u0438\u0445 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u0435\u043c.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430\u0448\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u043b \u043e\u0434\u0438\u043d \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 32 \u044f\u0434\u0440\u0430\u0445 \u0438 50 GB \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043a\u0438. \u0412 Airflow \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0431\u043e\u043b\u0435\u0435 <strong>200 \u0434\u0430\u0433\u043e\u0432<\/strong> (\u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e workflows, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043d\u0430\u0431\u0438\u043b\u0438 \u0437\u0430\u0434\u0430\u0447\u043a\u0438), <\/li>\n<li>\u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0432 \u0441\u0440\u0435\u0434\u043d\u0435\u043c \u043f\u043e <strong>70 \u0442\u0430\u0441\u043a\u043e\u0432<\/strong>,<\/li>\n<li>\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u044d\u0442\u043e \u0434\u043e\u0431\u0440\u043e (\u0442\u043e\u0436\u0435 \u0432 \u0441\u0440\u0435\u0434\u043d\u0435\u043c) <strong>\u0440\u0430\u0437 \u0432 \u0447\u0430\u0441<\/strong>.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0410 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043c\u044b \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u043b\u0438\u0441\u044c, \u044f \u043d\u0430\u043f\u0438\u0448\u0443 \u043d\u0438\u0436\u0435, \u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u00fcber-\u0437\u0430\u0434\u0430\u0447\u0443, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u0448\u0430\u0442\u044c:<\/p>\n<p>  <\/p>\n<blockquote><p>\u0415\u0441\u0442\u044c \u0442\u0440\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 SQL Server\u2019\u0430, \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u043f\u043e 50 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0443 \u043d\u0438\u0445 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u0430\u044f (\u043f\u043e\u0447\u0442\u0438 \u0432\u0435\u0437\u0434\u0435, \u043c\u0443\u0430-\u0445\u0430-\u0445\u0430), \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u0435\u0441\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0430 Orders (\u0431\u043b\u0430\u0433\u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441 \u0442\u0430\u043a\u0438\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0442\u043e\u043b\u043a\u0430\u0442\u044c \u0432 \u043b\u044e\u0431\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441). \u041c\u044b \u0437\u0430\u0431\u0438\u0440\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0435 \u043f\u043e\u043b\u044f (\u0441\u0435\u0440\u0432\u0435\u0440-\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a, \u0431\u0430\u0437\u0430-\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a, \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 ETL-\u0437\u0430\u0434\u0430\u0447\u0438) \u0438 \u043d\u0430\u0438\u0432\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0431\u0440\u043e\u0441\u0438\u043c \u0438\u0445 \u0432, \u0441\u043a\u0430\u0436\u0435\u043c, Vertica.<\/p><\/blockquote>\n<p>\u041f\u043e\u0435\u0445\u0430\u043b\u0438!<\/p>\n<p>  <\/p>\n<h2 id=\"chast-osnovnaya-prakticheskaya-i-nemnogo-teoreticheskaya\">\u0427\u0430\u0441\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f (\u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f)<\/h2>\n<p>  <\/p>\n<h3 id=\"zachem-ono-nam-i-vam\">\u0417\u0430\u0447\u0435\u043c \u043e\u043d\u043e \u043d\u0430\u043c (\u0438 \u0432\u0430\u043c)<\/h3>\n<p>  <\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0434\u0435\u0440\u0435\u0432\u044c\u044f \u0431\u044b\u043b\u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438, \u0430 \u044f \u0431\u044b\u043b \u043f\u0440\u043e\u0441\u0442\u044b\u043c <code>SQL<\/code>-\u0449\u0438\u043a\u043e\u043c \u0432 \u043e\u0434\u043d\u043e\u043c \u0440\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u043e\u043c \u0440\u0438\u0442\u0435\u0439\u043b\u0435, \u043c\u044b \u0448\u043f\u0430\u0440\u0438\u043b\u0438 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b aka \u043f\u043e\u0442\u043e\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0432\u0443\u0445 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043d\u0430\u043c \u0441\u0440\u0435\u0434\u0441\u0442\u0432:<\/p>\n<p>  <\/p>\n<ul>\n<li>\n<p><strong>Informatica Power Center<\/strong> \u2014 \u043a\u0440\u0430\u0439\u043d\u0435 \u0440\u0430\u0437\u0432\u0435\u0441\u0438\u0441\u0442\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u0447\u0440\u0435\u0437\u0432\u044b\u0447\u0430\u0439\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f, \u0441\u043e \u0441\u0432\u043e\u0438\u043c\u0438 \u0436\u0435\u043b\u0435\u0437\u043a\u0430\u043c\u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u044f \u0434\u0430\u0439 \u0431\u043e\u0433 1% \u0435\u0451 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439. \u041f\u043e\u0447\u0435\u043c\u0443? \u041d\u0443, \u0432\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u044d\u0442\u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0433\u0434\u0435-\u0442\u043e \u0438\u0437 \u043d\u0443\u043b\u0435\u0432\u044b\u0445 \u043f\u0441\u0438\u0445\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u0430\u0432\u0438\u043b \u043d\u0430 \u043d\u0430\u0441. \u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u044d\u0442\u0430 \u0448\u0442\u0443\u043a\u043e\u0432\u0438\u043d\u0430 \u0437\u0430\u0442\u043e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434 \u0447\u0440\u0435\u0437\u0432\u044b\u0447\u0430\u0439\u043d\u043e \u043d\u0430\u0432\u043e\u0440\u043e\u0447\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b, \u044f\u0440\u043e\u0441\u0442\u043d\u043e\u0435 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043e\u0447\u0435\u043d\u044c-\u0432\u0430\u0436\u043d\u044b\u0435-\u044d\u043d\u0442\u0435\u0440\u043f\u0440\u0430\u0439\u0437-\u0444\u0438\u0448\u0435\u0447\u043a\u0438. \u041f\u0440\u043e \u0442\u043e \u0447\u0442\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u043d\u0430, \u043a\u0430\u043a \u043a\u0440\u044b\u043b\u043e Airbus A380\/\u0433\u043e\u0434, \u043c\u044b \u043f\u0440\u043e\u043c\u043e\u043b\u0447\u0438\u043c.<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u041e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u043e, \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u043c\u043e\u0436\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043b\u044e\u0434\u044f\u043c \u043c\u043b\u0430\u0434\u0448\u0435 30 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u043b\u044c\u043d\u043e<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/c4\/re\/eu\/c4reeuap1b-lpzvdvdglkgoa0iq.jpeg\"><\/p>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\n<p><strong>SQL Server Integration Server<\/strong> \u2014 \u044d\u0442\u0438\u043c \u0442\u043e\u0432\u0430\u0440\u0438\u0449\u0435\u043c \u043c\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u0432 \u0441\u0432\u043e\u0438\u0445 \u0432\u043d\u0443\u0442\u0440\u0438\u043f\u0440\u043e\u0435\u043a\u0442\u043d\u044b\u0445 \u043f\u043e\u0442\u043e\u043a\u0430\u0445. \u041d\u0443 \u0430 \u0432 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435: SQL Server \u043c\u044b \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c, \u0438 \u043d\u0435 \u044e\u0437\u0430\u0442\u044c \u0435\u0433\u043e ETL-\u0442\u0443\u043b\u0437\u044b \u0431\u044b\u043b\u043e \u0431\u044b \u043a\u0430\u043a-\u0442\u043e \u043d\u0435\u0440\u0430\u0437\u0443\u043c\u043d\u043e. \u0412\u0441\u0451 \u0432 \u043d\u0451\u043c \u0432 \u0445\u043e\u0440\u043e\u0448\u043e: \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439, \u0438 \u043e\u0442\u0447\u0451\u0442\u0438\u043a\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f\u2026 \u041d\u043e \u043d\u0435 \u0437\u0430 \u044d\u0442\u043e \u043c\u044b \u043b\u044e\u0431\u0438\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0435 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u044b, \u043e\u0445 \u043d\u0435 \u0437\u0430 \u044d\u0442\u043e. \u0412\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e <code>dtsx<\/code> (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 XML \u0441 \u043f\u0435\u0440\u0435\u043c\u0435\u0448\u0438\u0432\u0430\u044e\u0449\u0438\u043c\u0438\u0441\u044f \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u043d\u043e\u0434\u0430\u043c\u0438) \u043c\u044b \u043c\u043e\u0436\u0435\u043c, \u0430 \u0442\u043e\u043b\u043a\u0443? \u0410 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442 \u0442\u0430\u0441\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442 \u0441\u043e\u0442\u043d\u044e \u0442\u0430\u0431\u043b\u0438\u0446 \u0441 \u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439? \u0414\u0430 \u0447\u0442\u043e \u0441\u043e\u0442\u043d\u044e, \u0443 \u0432\u0430\u0441 \u043e\u0442 \u0434\u0432\u0430\u0434\u0446\u0430\u0442\u0438 \u0448\u0442\u0443\u043a \u043e\u0442\u0432\u0430\u043b\u0438\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u043b\u0435\u0446, \u0449\u0451\u043b\u043a\u0430\u044e\u0449\u0438\u0439 \u043f\u043e \u043c\u044b\u0448\u0438\u043d\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u0435. \u041d\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043e\u043d, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e, \u0431\u043e\u043b\u0435\u0435 \u043c\u043e\u0434\u043d\u043e:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/tb\/b3\/pw\/tbb3pwqohkzfi9zvhhxjpcjyui0.jpeg\"><\/p>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041c\u044b \u0431\u0435\u0437\u0443\u0441\u043b\u043e\u0432\u043d\u043e \u0438\u0441\u043a\u0430\u043b\u0438 \u0432\u044b\u0445\u043e\u0434\u044b. \u0414\u0435\u043b\u043e \u0434\u0430\u0436\u0435 <em>\u043f\u043e\u0447\u0442\u0438<\/em> \u0434\u043e\u0448\u043b\u043e \u0434\u043e \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u043e\u0433\u043e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u0430 SSIS-\u043f\u0430\u043a\u0435\u0442\u043e\u0432&#8230;<\/p>\n<p>  <\/p>\n<p>\u2026 \u0430 \u043f\u043e\u0442\u043e\u043c \u043c\u0435\u043d\u044f \u043d\u0430\u0448\u043b\u0430 \u043d\u043e\u0432\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430. \u0410 \u043d\u0430 \u043d\u0435\u0439 \u043c\u0435\u043d\u044f \u043d\u0430\u0441\u0442\u0438\u0433 Apache Airflow.<\/p>\n<p>  <\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u044f \u0443\u0437\u043d\u0430\u043b, \u0447\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439 Python-\u043a\u043e\u0434, \u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u043d\u0435 \u043f\u043b\u044f\u0441\u0430\u043b \u043e\u0442 \u0440\u0430\u0434\u043e\u0441\u0442\u0438. \u0412\u043e\u0442 \u0442\u0430\u043a \u043f\u043e\u0442\u043e\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0434\u0432\u0435\u0440\u0433\u043b\u0438\u0441\u044c \u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0438 \u0434\u0438\u0444\u0444\u0443, \u0430 \u0441\u0441\u044b\u043f\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0441 \u0435\u0434\u0438\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u0438\u0437 \u0441\u043e\u0442\u043d\u0438 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043e\u0434\u0438\u043d \u0442\u0430\u0440\u0433\u0435\u0442 \u0441\u0442\u0430\u043b\u043e \u0434\u0435\u043b\u043e\u043c Python-\u043a\u043e\u0434\u0430 \u0432 \u043f\u043e\u043b\u0442\u043e\u0440\u0430-\u0434\u0432\u0430 13\u201d \u044d\u043a\u0440\u0430\u043d\u0430.<\/p>\n<p>  <\/p>\n<h3 id=\"sobiraem-klaster\">\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0442\u0435\u0440<\/h3>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0441\u043e\u0432\u0441\u0435\u043c \u0443\u0436 \u0434\u0435\u0442\u0441\u043a\u0438\u0439 \u0441\u0430\u0434, \u0438 \u043d\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u0442\u0443\u0442 \u043e \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0445 \u0432\u0435\u0449\u0430\u0445, \u0432\u0440\u043e\u0434\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 Airflow, \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u0432\u0430\u043c\u0438 \u0411\u0414, Celery \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u0434\u0435\u043b, \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0445 \u0432 \u0434\u043e\u043a\u0430\u0445.<\/p>\n<p>  <\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043c, \u044f \u043d\u0430\u0431\u0440\u043e\u0441\u0430\u043b <code>docker-compose.yml<\/code> \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041f\u043e\u0434\u043d\u0438\u043c\u0435\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e <strong>Airflow<\/strong>: Scheduler, Webserver. \u0422\u0430\u043c \u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043a\u0440\u0443\u0442\u0438\u0442\u0441\u044f Flower \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 Celery-\u0437\u0430\u0434\u0430\u0447 (\u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0433\u043e \u0443\u0436\u0435 \u0437\u0430\u0442\u043e\u043b\u043a\u0430\u043b\u0438 \u0432 <code>apache\/airflow:1.10.10-python3.7<\/code>, \u0430 \u043c\u044b \u0438 \u043d\u0435 \u043f\u0440\u043e\u0442\u0438\u0432);<\/li>\n<li><strong>PostgreSQL<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 Airflow \u0431\u0443\u0434\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e (\u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430, \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0438 \u0442. \u0434.), \u0430 Celery \u2014 \u043e\u0442\u043c\u0435\u0447\u0430\u0442\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0442\u0430\u0441\u043a\u0438;<\/li>\n<li><strong>Redis<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u0431\u0440\u043e\u043a\u0435\u0440\u043e\u043c \u0437\u0430\u0434\u0430\u0447 \u0434\u043b\u044f Celery;<\/li>\n<li><strong>Celery worker<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438 \u0437\u0430\u0439\u043c\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0435\u043a.<\/li>\n<li>\u0412 \u043f\u0430\u043f\u043a\u0443 <code>.\/dags<\/code> \u043c\u044b \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u0444\u0430\u0439\u043b\u044b \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0434\u0430\u0433\u043e\u0432. \u041e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043b\u0435\u0442\u0443, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0435\u0434\u0451\u0440\u0433\u0438\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u0441\u0442\u0435\u043a \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0447\u0438\u0445\u0430 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e.<\/li>\n<\/ul>\n<p>  <\/p>\n<blockquote><p>\u041a\u043e\u0435-\u0433\u0434\u0435 \u043a\u043e\u0434 \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u0445 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043d\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e (\u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0433\u0440\u043e\u043c\u043e\u0436\u0434\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442), \u0430 \u0433\u0434\u0435-\u0442\u043e \u043e\u043d \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435. \u0426\u0435\u043b\u044c\u043d\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043a\u043e\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 <a href=\"https:\/\/github.com\/dm-logv\/airflow-tutorial\" rel=\"nofollow\">https:\/\/github.com\/dm-logv\/airflow-tutorial<\/a>.<\/p><\/blockquote>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">docker-compose.yml<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"plaintext\">version: '3.4'  x-airflow-config: &amp;airflow-config   AIRFLOW__CORE__DAGS_FOLDER: \/dags   AIRFLOW__CORE__EXECUTOR: CeleryExecutor   AIRFLOW__CORE__FERNET_KEY: MJNz36Q8222VOQhBOmBROFrmeSxNOgTCMaVp2_HOtE0=   AIRFLOW__CORE__HOSTNAME_CALLABLE: airflow.utils.net:get_host_ip_address   AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgres+psycopg2:\/\/airflow:airflow@airflow-db:5432\/airflow    AIRFLOW__CORE__PARALLELISM: 128   AIRFLOW__CORE__DAG_CONCURRENCY: 16   AIRFLOW__CORE__MAX_ACTIVE_RUNS_PER_DAG: 4   AIRFLOW__CORE__LOAD_EXAMPLES: 'False'   AIRFLOW__CORE__LOAD_DEFAULT_CONNECTIONS: 'False'    AIRFLOW__EMAIL__DEFAULT_EMAIL_ON_RETRY: 'False'   AIRFLOW__EMAIL__DEFAULT_EMAIL_ON_FAILURE: 'False'    AIRFLOW__CELERY__BROKER_URL: redis:\/\/broker:6379\/0   AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql:\/\/airflow:airflow@airflow-db\/airflow  x-airflow-base: &amp;airflow-base   image: apache\/airflow:1.10.10-python3.7   entrypoint: \/bin\/bash   restart: always   volumes:     - .\/dags:\/dags     - .\/requirements.txt:\/requirements.txt  services:   # Redis as a Celery broker   broker:     image: redis:6.0.5-alpine    # DB for the Airflow metadata   airflow-db:     image: postgres:10.13-alpine      environment:       - POSTGRES_USER=airflow       - POSTGRES_PASSWORD=airflow       - POSTGRES_DB=airflow      volumes:       - .\/db:\/var\/lib\/postgresql\/data    # Main container with Airflow Webserver, Scheduler, Celery Flower   airflow:     &lt;&lt;: *airflow-base      environment:       &lt;&lt;: *airflow-config        AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL: 30       AIRFLOW__SCHEDULER__CATCHUP_BY_DEFAULT: 'False'       AIRFLOW__SCHEDULER__MAX_THREADS: 8        AIRFLOW__WEBSERVER__LOG_FETCH_TIMEOUT_SEC: 10      depends_on:       - airflow-db       - broker      command: &gt;       -c &quot; sleep 10 &amp;&amp;            pip install --user -r \/requirements.txt &amp;&amp;            \/entrypoint initdb &amp;&amp;           (\/entrypoint webserver &amp;) &amp;&amp;           (\/entrypoint flower &amp;) &amp;&amp;            \/entrypoint scheduler&quot;      ports:       # Celery Flower       - 5555:5555       # Airflow Webserver       - 8080:8080    # Celery worker, will be scaled using `--scale=n`   worker:     &lt;&lt;: *airflow-base      environment:       &lt;&lt;: *airflow-config      command: &gt;       -c &quot; sleep 10 &amp;&amp;            pip install --user -r \/requirements.txt &amp;&amp;            \/entrypoint worker&quot;      depends_on:       - airflow       - airflow-db       - broker<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0412 \u0441\u0431\u043e\u0440\u043a\u0435 \u043a\u043e\u043c\u043f\u043e\u0437\u0430 \u044f \u0432\u043e \u043c\u043d\u043e\u0433\u043e\u043c \u043e\u043f\u0438\u0440\u0430\u043b\u0441\u044f \u043d\u0430 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0437 <a href=\"https:\/\/github.com\/puckel\/docker-airflow\" rel=\"nofollow\">puckel\/docker-airflow<\/a> \u2013 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435. \u041c\u043e\u0436\u0435\u0442, \u0432\u0430\u043c \u0432 \u0436\u0438\u0437\u043d\u0438 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u0438 \u043d\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f.<\/li>\n<li>\u0412\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Airflow \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 <code>airflow.cfg<\/code>, \u043d\u043e \u0438 \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b (\u0441\u043b\u0430\u0432\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c), \u0447\u0435\u043c \u044f \u0437\u043b\u043e\u0441\u0442\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f.<\/li>\n<li>\u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043e\u043d \u043d\u0435 production-ready: \u044f \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e \u043d\u0435 \u0441\u0442\u0430\u0432\u0438\u043b heartbeats \u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b, \u043d\u0435 \u0437\u0430\u043c\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043b\u0441\u044f \u0441 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c\u044e. \u041d\u043e \u043c\u0438\u043d\u0438\u043c\u0443\u043c, \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u043a\u043e\u0432 \u044f \u0441\u0434\u0435\u043b\u0430\u043b.<\/li>\n<li>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e:<br \/> \n<ul>\n<li>\u041f\u0430\u043f\u043a\u0430 \u0441 \u0434\u0430\u0433\u0430\u043c\u0438 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u043a\u0430\u043a \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0443, \u0442\u0430\u043a \u0438 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c.<\/li>\n<li>\u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f \u0438 \u0432\u0441\u0435\u0445 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u2014 \u043e\u043d\u0438 \u0432\u0441\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u044b \u0441 \u0448\u0435\u0434\u0443\u043b\u0435\u0440\u043e\u043c \u0438 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041d\u0443 \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0441\u0442\u043e:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">$ docker-compose up --scale worker=3<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0432\u0441\u0451 \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u0442\u0441\u044f, \u043c\u043e\u0436\u043d\u043e \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b:<\/p>\n<p>  <\/p>\n<ul>\n<li>Airflow: <a href=\"http:\/\/127.0.0.1:8080\/admin\/\" rel=\"nofollow\">http:\/\/127.0.0.1:8080\/admin\/<\/a><\/li>\n<li>Flower: <a href=\"http:\/\/127.0.0.1:5555\/dashboard\" rel=\"nofollow\">http:\/\/127.0.0.1:5555\/dashboard<\/a><\/li>\n<\/ul>\n<p>  <\/p>\n<h3 id=\"osnovnye-ponyatiya\">\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u043e\u043d\u044f\u0442\u0438\u044f<\/h3>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u043e\u043d\u044f\u043b\u0438 \u0432\u043e \u0432\u0441\u0435\u0445 \u044d\u0442\u0438\u0445 \u00ab\u0434\u0430\u0433\u0430\u0445\u00bb, \u0442\u043e \u0432\u043e\u0442 \u043a\u0440\u0430\u0442\u043a\u0438\u0439 \u0441\u043b\u043e\u0432\u0430\u0440\u0438\u043a:<\/p>\n<p>  <\/p>\n<ul>\n<li>\n<p><strong>Scheduler<\/strong> \u2014 \u0441\u0430\u043c\u044b\u0439 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0434\u044f\u0434\u044c\u043a\u0430 \u0432 Airflow, \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u044e\u0449\u0438\u0439, \u0447\u0442\u043e\u0431\u044b \u0432\u043a\u0430\u043b\u044b\u0432\u0430\u043b\u0438 \u0440\u043e\u0431\u043e\u0442\u044b, \u0430 \u043d\u0435 \u0447\u0435\u043b\u043e\u0432\u0435\u043a: \u0441\u043b\u0435\u0434\u0438\u0442 \u0437\u0430 \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0434\u0430\u0433\u0438, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0442\u0430\u0441\u043a\u0438. <\/p>\n<p>  <\/p>\n<p>\u0412\u043e\u043e\u0431\u0449\u0435, \u0432 \u0441\u0442\u0430\u0440\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445, \u0443 \u043d\u0435\u0433\u043e \u0431\u044b\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043f\u0430\u043c\u044f\u0442\u044c\u044e (\u043d\u0435\u0442, \u043d\u0435 \u0430\u043c\u043d\u0435\u0437\u0438\u044f, \u0430 \u0443\u0442\u0435\u0447\u043a\u0438) \u0438 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0430\u0445 \u0434\u0430\u0436\u0435 \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u043b\u0435\u0433\u0430\u0441\u0438-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>run_duration<\/code> \u2014 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430. \u041d\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u0441\u0451 \u0445\u043e\u0440\u043e\u0448\u043e.<\/p>\n<p>  <\/li>\n<li>\n<p><strong>DAG<\/strong> (\u043e\u043d \u0436\u0435 \u00ab\u0434\u0430\u0433\u00bb) \u2014 \u00ab\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0430\u0446\u0438\u043a\u043b\u0438\u0447\u043d\u044b\u0439 \u0433\u0440\u0430\u0444\u00bb, \u043d\u043e \u0442\u0430\u043a\u043e\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u043b\u043e \u043a\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u043a\u0430\u0436\u0435\u0442, \u0430 \u043f\u043e \u0441\u0443\u0442\u0438 \u044d\u0442\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0434\u0440\u0443\u0433 \u0441 \u0434\u0440\u0443\u0433\u043e\u043c \u0442\u0430\u0441\u043a\u043e\u0432 (\u0441\u043c. \u043d\u0438\u0436\u0435) \u0438\u043b\u0438 \u0430\u043d\u0430\u043b\u043e\u0433 Package \u0432 SSIS \u0438 Workflow \u0432 Informatica. <\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043c\u0438\u043c\u043e \u0434\u0430\u0433\u043e\u0432 \u0435\u0449\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u0430\u0431\u0434\u0430\u0433\u0438, \u043d\u043e \u043c\u044b \u0434\u043e \u043d\u0438\u0445 \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u043d\u0435 \u0434\u043e\u0431\u0435\u0440\u0451\u043c\u0441\u044f.<\/p>\n<p>  <\/li>\n<li>\n<p><strong>DAG Run<\/strong> \u2014 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u0430\u0433, \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043f\u0440\u0438\u0441\u0432\u043e\u0435\u043d \u0441\u0432\u043e\u0439 <code>execution_date<\/code>. \u0414\u0430\u0433\u0440\u0430\u043d\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u0434\u0430\u0433\u0430 \u043c\u043e\u0433\u0443\u0442 \u0432\u043f\u043e\u043b\u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e (\u0435\u0441\u043b\u0438 \u0432\u044b, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0441\u0432\u043e\u0438 \u0442\u0430\u0441\u043a\u0438 \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b\u043c\u0438).<\/p>\n<p>  <\/li>\n<li>\n<p><strong>Operator<\/strong> \u2014 \u044d\u0442\u043e \u043a\u0443\u0441\u043e\u0447\u043a\u0438 \u043a\u043e\u0434\u0430, \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0437\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u0430\u043a\u043e\u0433\u043e-\u043b\u0438\u0431\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f. \u0415\u0441\u0442\u044c \u0442\u0440\u0438 \u0442\u0438\u043f\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432:<\/p>\n<p>  <\/p>\n<ul>\n<li><strong>action<\/strong>, \u043a\u0430\u043a \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430\u0448 \u043b\u044e\u0431\u0438\u043c\u044b\u0439 <code>PythonOperator<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0441\u0438\u043b\u0430\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043b\u044e\u0431\u043e\u0439 (\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439) Python-\u043a\u043e\u0434;<\/li>\n<li><strong>transfer<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0437\u044f\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043c\u0435\u0441\u0442\u0430 \u043d\u0430 \u043c\u0435\u0441\u0442\u043e, \u0441\u043a\u0430\u0436\u0435\u043c, <code>MsSqlToHiveTransfer<\/code>;<\/li>\n<li><strong>sensor<\/strong> \u0436\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043b\u0438 \u043f\u0440\u0438\u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u0433\u0430 \u0434\u043e \u043d\u0430\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u043e\u0433\u043e-\u043b\u0438\u0431\u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f. <code>HttpSensor<\/code> \u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0440\u0433\u0430\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u044d\u043d\u0434\u043f\u043e\u0439\u043d\u0442, \u0438 \u043a\u043e\u0433\u0434\u0430 \u0434\u043e\u0436\u0434\u0435\u0442\u0441\u044f \u043d\u0443\u0436\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u0444\u0435\u0440 <code>GoogleCloudStorageToS3Operator<\/code>. \u041f\u044b\u0442\u043b\u0438\u0432\u044b\u0439 \u0443\u043c \u0441\u043f\u0440\u043e\u0441\u0438\u0442: \u00ab\u0437\u0430\u0447\u0435\u043c? \u0412\u0435\u0434\u044c \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u044b \u043f\u0440\u044f\u043c\u043e \u0432 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0435!\u00bb \u0410 \u0437\u0430\u0442\u0435\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0431\u0438\u0432\u0430\u0442\u044c \u043f\u0443\u043b \u0442\u0430\u0441\u043a\u043e\u0432 \u043f\u043e\u0434\u0432\u0438\u0441\u0448\u0438\u043c\u0438 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0430\u043c\u0438. \u0421\u0435\u043d\u0441\u043e\u0440 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0438 \u0443\u043c\u0438\u0440\u0430\u0435\u0442 \u0434\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043f\u043e\u043f\u044b\u0442\u043a\u0438.<\/li>\n<\/ul>\n<p>  <\/li>\n<li>\n<p><strong>Task<\/strong> \u2014 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u0438\u043f\u0430 \u0438 \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u0435\u043d\u043d\u044b\u0435 \u043a \u0434\u0430\u0433\u0443 \u043f\u043e\u0432\u044b\u0448\u0430\u044e\u0442\u0441\u044f \u0434\u043e \u0447\u0438\u043d\u0430 \u0442\u0430\u0441\u043a\u0430.<\/p>\n<p>  <\/li>\n<li>\n<p><strong>Task instance<\/strong> \u2014 \u043a\u043e\u0433\u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0430\u043b-\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0440\u0435\u0448\u0438\u043b, \u0447\u0442\u043e \u0442\u0430\u0441\u043a\u0438 \u043f\u043e\u0440\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u0431\u043e\u0439 \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438-\u0432\u043e\u0440\u043a\u0435\u0440\u044b (\u043f\u0440\u044f\u043c\u043e \u043d\u0430 \u043c\u0435\u0441\u0442\u0435, \u0435\u0441\u043b\u0438 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>LocalExecutor<\/code> \u0438\u043b\u0438 \u043d\u0430 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u0443\u044e \u043d\u043e\u0434\u0443 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 <code>CeleryExecutor<\/code>), \u043e\u043d \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0438\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 (\u0442. \u0435. \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u2014 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f), \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u043a\u043e\u043c\u0430\u043d\u0434 \u0438\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u0438\u0445 \u0432 \u043f\u0443\u043b.<\/p>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<h3 id=\"generiruem-taski\">\u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0430\u0441\u043a\u0438<\/h3>\n<p>  <\/p>\n<p>\u0421\u043f\u0435\u0440\u0432\u0430 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0438\u043c \u043e\u0431\u0449\u0443\u044e \u0441\u0445\u0435\u043c\u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0434\u0430\u0433\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u0431\u0443\u0434\u0435\u043c \u0432\u0441\u0451 \u0431\u043e\u043b\u044c\u0448\u0435 \u0438 \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0432 \u0434\u0435\u0442\u0430\u043b\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f.<\/p>\n<p>  <\/p>\n<p>\u0418\u0442\u0430\u043a, \u0432 \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0435\u043c \u0432\u0438\u0434\u0435 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u0434\u0430\u0433 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">from datetime import timedelta, datetime  from airflow import DAG from airflow.operators.python_operator import PythonOperator  from commons.datasources import sql_server_ds  dag = DAG('orders',           schedule_interval=timedelta(hours=6),           start_date=datetime(2020, 7, 8, 0))  def workflow(**context):     print(context)  for conn_id, schema in sql_server_ds:     PythonOperator(         task_id=schema,         python_callable=workflow,         provide_context=True,         dag=dag)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0421\u043f\u0435\u0440\u0432\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043d\u0443\u0436\u043d\u044b\u0435 \u043b\u0438\u0431\u044b \u0438 <em>\u043a\u043e\u0435 \u0447\u0442\u043e \u0435\u0449\u0451<\/em>;<\/li>\n<li><code>sql_server_ds<\/code> \u2014 \u044d\u0442\u043e <code>List[namedtuple[str, str]]<\/code> \u0441 \u0438\u043c\u0435\u043d\u0430\u043c\u0438 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0432 \u0438\u0437 Airflow Connections \u0438 \u0431\u0430\u0437\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u0431\u0438\u0440\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0442\u0430\u0431\u043b\u0438\u0447\u043a\u0443;<\/li>\n<li><code>dag<\/code> \u2014 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0434\u0430\u0433\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u043b\u0435\u0436\u0430\u0442\u044c \u0432 <code>globals()<\/code>, \u0438\u043d\u0430\u0447\u0435 Airflow \u0435\u0433\u043e \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u0442. \u0414\u0430\u0433\u0443 \u0442\u0430\u043a\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c: <br \/> \n<ul>\n<li>\u0447\u0442\u043e \u0435\u0433\u043e \u0437\u043e\u0432\u0443\u0442 <code>orders<\/code> \u2014 \u044d\u0442\u043e \u0438\u043c\u044f \u043f\u043e\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043c\u0430\u044f\u0447\u0438\u0442\u044c \u0432 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435,<\/li>\n<li>\u0447\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043e\u043d \u0431\u0443\u0434\u0435\u0442, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u043f\u043e\u043b\u0443\u043d\u043e\u0447\u0438 \u0432\u043e\u0441\u044c\u043c\u043e\u0433\u043e \u0438\u044e\u043b\u044f,<\/li>\n<li>\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d, \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0435 6 \u0447\u0430\u0441\u043e\u0432 (\u0434\u043b\u044f \u043a\u0440\u0443\u0442\u044b\u0445 \u043f\u0430\u0440\u043d\u0435\u0439 \u0437\u0434\u0435\u0441\u044c \u0432\u043c\u0435\u0441\u0442\u043e <code>timedelta()<\/code> \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430 <code>cron<\/code>-\u0441\u0442\u0440\u043e\u043a\u0430 <code>0 0 0\/6 ? * * *<\/code>, \u0434\u043b\u044f \u043c\u0435\u043d\u0435\u0435 \u043a\u0440\u0443\u0442\u044b\u0445 \u2014 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432\u0440\u043e\u0434\u0435 <code>@daily<\/code>);<\/li>\n<\/ul>\n<\/li>\n<li><code>workflow()<\/code> \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u043d\u043e \u043d\u0435 \u0441\u0435\u0439\u0447\u0430\u0441. \u0421\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0441\u044b\u043f\u0435\u043c \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432 \u043b\u043e\u0433.<\/li>\n<li>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u043c\u0430\u0433\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0441\u043a\u043e\u0432:<br \/> \n<ul>\n<li>\u043f\u0440\u043e\u0431\u0435\u0433\u0430\u0435\u043c \u043f\u043e \u043d\u0430\u0448\u0438\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c;<\/li>\n<li>\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c <code>PythonOperator<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0430\u0448\u0443 \u043f\u0443\u0441\u0442\u044b\u0448\u043a\u0443 <code>workflow()<\/code>. \u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0439\u0442\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435 (\u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0434\u0430\u0433\u0430) \u0438\u043c\u044f \u0442\u0430\u0441\u043a\u0430 \u0438 \u043f\u043e\u0434\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0430\u043c \u0434\u0430\u0433. \u0424\u043b\u0430\u0433 <code>provide_context<\/code> \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430\u0441\u044b\u043f\u0435\u0442 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0435\u0440\u0435\u0436\u043d\u043e \u0441\u043e\u0431\u0435\u0440\u0451\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>**context<\/code>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041f\u043e\u043a\u0430 \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451. \u0427\u0442\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043d\u043e\u0432\u044b\u0439 \u0434\u0430\u0433 \u0432 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435,<\/li>\n<li>\u043f\u043e\u043b\u0442\u043e\u0440\u044b \u0441\u043e\u0442\u043d\u0438 \u0442\u0430\u0441\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e (\u0435\u0441\u043b\u0438 \u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Airflow, Celery \u0438 \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432).<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041d\u0443, \u043f\u043e\u0447\u0442\u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/qh\/dt\/-p\/qhdt-ppluu0oblx7xhpyqvy26ee.png\"><br \/>  <em>\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043a\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0442\u0430\u0432\u0438\u0442\u044c?<\/em><\/p>\n<p>  <\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u044d\u0442\u043e \u0434\u0435\u043b\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u044f \u0432\u043a\u043e\u0440\u044f\u0447\u0438\u043b \u0432 <code>docker-compose.yml<\/code> \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 <code>requirements.txt<\/code> \u043d\u0430 \u0432\u0441\u0435\u0445 \u043d\u043e\u0434\u0430\u0445.<\/p>\n<p>  <\/p>\n<p>\u0412\u043e\u0442 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043d\u0435\u0441\u043b\u0430\u0441\u044c:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/7u\/qo\/x4\/7uqox4k7kzpnjt6ivpts8pdffmk.png\"><\/p>\n<p>  <\/p>\n<p><em>\u0421\u0435\u0440\u044b\u0435 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0438\u043a\u0438 \u2014 task instances, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u043e\u043c.<\/em> <\/p>\n<p>  <\/p>\n<p>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0436\u0434\u0435\u043c, \u0437\u0430\u0434\u0430\u0447\u0438 \u0440\u0430\u0441\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u044e\u0442 \u0432\u043e\u0440\u043a\u0435\u0440\u044b:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/lx\/z2\/ss\/lxz2ssv_fcltskoxhhc757q6ebc.png\"><\/p>\n<p>  <\/p>\n<p><em>\u0417\u0435\u043b\u0435\u043d\u044b\u0435, \u043f\u043e\u043d\u044f\u0442\u043d\u043e\u0435 \u0434\u0435\u043b\u043e, \u2014 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u0432\u0448\u0438\u0435. \u041a\u0440\u0430\u0441\u043d\u044b\u0435 \u2014 \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0441\u043f\u0435\u0448\u043d\u043e.<\/em><\/p>\n<p>  <\/p>\n<blockquote><p>\u041a\u0441\u0442\u0430\u0442\u0438, \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0434\u0435 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043f\u0430\u043f\u043a\u0438 <code>.\/dags<\/code>, \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0443\u044e\u0449\u0435\u0439\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043c\u0430\u0448\u0438\u043d\u0430\u043c\u0438 \u043d\u0435\u0442 \u2014 \u0432\u0441\u0451 \u0434\u0430\u0433\u0438 \u043b\u0435\u0436\u0430\u0442 \u0432 <code>git<\/code> \u043d\u0430 \u043d\u0430\u0448\u0435\u043c Gitlab, \u0430 Gitlab CI \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u044b \u043f\u0440\u0438 \u043c\u0451\u0440\u0434\u0436\u0435 \u0432 <code>master<\/code>. <\/p><\/blockquote>\n<p>  <\/p>\n<h3 id=\"nemnogo-o-flower\">\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043e Flower<\/h3>\n<p>  <\/p>\n<p>\u041f\u043e\u043a\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u044b \u043c\u043e\u043b\u043e\u0442\u044f\u0442 \u043d\u0430\u0448\u0438 \u0442\u0430\u0441\u043e\u0447\u043a\u0438-\u043f\u0443\u0441\u0442\u044b\u0448\u043a\u0438, \u0432\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u043f\u0440\u043e \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043c \u043a\u043e\u0435-\u0447\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u2014 Flower.<\/p>\n<p>  <\/p>\n<p>\u0421\u0430\u043c\u0430\u044f \u043f\u0435\u0440\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0430 \u0441 \u0441\u0443\u043c\u043c\u0430\u0440\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u043d\u043e\u0434\u0430\u043c-\u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bc\/w5\/wh\/bcw5whqmvuqntxzpoutlb2of1ms.png\"><\/p>\n<p>  <\/p>\n<p>\u0421\u0430\u043c\u0430\u044f \u043d\u0430\u0441\u044b\u0449\u0435\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0430 \u0441 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0432\u0448\u0438\u043c\u0438\u0441\u044f \u0432 \u0440\u0430\u0431\u043e\u0442\u0443:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ah\/5j\/vl\/ah5jvlfpkg7y2uozio4rgggop0i.png\"><\/p>\n<p>  <\/p>\n<p>\u0421\u0430\u043c\u0430\u044f \u0441\u043a\u0443\u0447\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0430 \u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u0440\u043e\u043a\u0435\u0440\u0430:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/vu\/1r\/-q\/vu1r-qcyige_bcst4gxprnfdosi.png\"><\/p>\n<p>  <\/p>\n<p>\u0421\u0430\u043c\u0430\u044f \u044f\u0440\u043a\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0430 \u2014 \u0441 \u0433\u0440\u0430\u0444\u0438\u043a\u0430\u043c\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0442\u0430\u0441\u043a\u043e\u0432 \u0438 \u0438\u0445 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/sy\/kz\/nj\/sykznjfv-isk1sbgehe3p8oarym.png\"><\/p>\n<p>  <\/p>\n<h3 id=\"dogruzhaem-nedogruzhennoe\">\u0414\u043e\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u043d\u0435\u0434\u043e\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u0435<\/h3>\n<p>  <\/p>\n<p>\u0418\u0442\u0430\u043a, \u0432\u0441\u0435 \u0442\u0430\u0441\u043a\u0438 \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438, \u043c\u043e\u0436\u043d\u043e \u0443\u043d\u043e\u0441\u0438\u0442\u044c \u0440\u0430\u043d\u0435\u043d\u044b\u0445.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/e1\/yv\/xd\/e1yvxd4hsvgksvxqhsmaryl8xok.png\"><\/p>\n<p>  <\/p>\n<p>\u0410 \u0440\u0430\u043d\u0435\u043d\u044b\u0445 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043c\u0430\u043b\u043e \u2014 \u043f\u043e \u0442\u0435\u043c \u0438\u043b\u0438 \u0438\u043d\u044b\u043c \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c\u0438. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Airflow \u0432\u043e\u0442 \u044d\u0442\u0438 \u0441\u0430\u043c\u044b\u0435 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u044b \u0433\u043e\u0432\u043e\u0440\u044f\u0442 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e \u043d\u0435 \u0434\u043e\u0435\u0445\u0430\u043b\u0438.<\/p>\n<p>  <\/p>\n<p>\u041d\u0443\u0436\u043d\u043e \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043b\u043e\u0433 \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0443\u043f\u0430\u0432\u0448\u0438\u0435 task instances.<\/p>\n<p>  <\/p>\n<p>\u0416\u043c\u044f\u043a\u043d\u0443\u0432 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u043a\u0432\u0430\u0434\u0440\u0430\u0442, \u0443\u0432\u0438\u0434\u0438\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u043d\u0430\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/dn\/jk\/ut\/dnjkutovwwmbuzalpsyjvfix75y.png\"><\/p>\n<p>  <\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c, \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c Clear \u0443\u043f\u0430\u0432\u0448\u0435\u043c\u0443. \u0422\u043e \u0435\u0441\u0442\u044c, \u043c\u044b \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0442\u0430\u043c \u0447\u0442\u043e-\u0442\u043e \u0437\u0430\u0432\u0430\u043b\u0438\u043b\u043e\u0441\u044c, \u0438 \u0442\u043e\u0442 \u0436\u0435 \u0441\u0430\u043c\u044b\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0441 \u0442\u0430\u0441\u043a\u0430 \u0443\u0439\u0434\u0435\u0442 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0443.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/vo\/nk\/qf\/vonkqfmsancrdfpgnilniul9il8.png\"><\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043d\u044f\u0442\u043d\u043e, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a \u043c\u044b\u0448\u043a\u043e\u0439 \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u043a\u0440\u0430\u0441\u043d\u044b\u043c\u0438 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0430\u043c\u0438 \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0433\u0443\u043c\u0430\u043d\u043d\u043e \u2014 \u043d\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0436\u0434\u0435\u043c \u043e\u0442 Airflow. \u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043e\u0440\u0443\u0436\u0438\u0435 \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0440\u0430\u0436\u0435\u043d\u0438\u044f: <code>Browse\/Task Instances<\/code><\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/mp\/9j\/bd\/mp9jbdbaailkypko7olk4gsbvyc.png\"><\/p>\n<p>  <\/p>\n<p>\u0412\u044b\u0431\u0435\u0440\u0435\u043c \u0432\u0441\u0451 \u0440\u0430\u0437\u043e\u043c \u0438 <del>\u043e\u0431\u043d\u0443\u043b\u0438\u043c<\/del> \u043d\u0430\u0436\u043c\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u043d\u043a\u0442:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/lz\/af\/iq\/lzafiqlf4isqj42xmr4gv_h-hry.png\"><\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u043d\u0430\u0448\u0438 \u0442\u0430\u043a\u0441\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0442\u0430\u043a (\u043e\u043d\u0438 \u0443\u0436\u0435 \u0436\u0434\u0443\u0442 \u043d\u0435 \u0434\u043e\u0436\u0434\u0443\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0448\u0435\u0434\u0443\u043b\u0435\u0440 \u0438\u0445 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442):<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/fj\/an\/tt\/fjanttfztb2ctxaslke5k3_pehq.png\"><\/p>\n<p>  <\/p>\n<h3 id=\"soedineniya-huki-i-prochie-peremennye\">\u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f, \u0445\u0443\u043a\u0438 \u0438 \u043f\u0440\u043e\u0447\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435<\/h3>\n<p>  <\/p>\n<p>\u0421\u0430\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 DAG, <code>update_reports.py<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">from collections import namedtuple from datetime import datetime, timedelta from textwrap import dedent  from airflow import DAG from airflow.contrib.operators.vertica_operator import VerticaOperator from airflow.operators.email_operator import EmailOperator from airflow.utils.trigger_rule import TriggerRule  from commons.operators import TelegramBotSendMessage  dag = DAG('update_reports',           start_date=datetime(2020, 6, 7, 6),           schedule_interval=timedelta(days=1),           default_args={'retries': 3, 'retry_delay': timedelta(seconds=10)})  Report = namedtuple('Report', 'source target') reports = [Report(f'{table}_view', table) for table in [     'reports.city_orders',     'reports.client_calls',     'reports.client_rates',     'reports.daily_orders',     'reports.order_duration']]  email = EmailOperator(     task_id='email_success', dag=dag,     to='{{ var.value.all_the_kings_men }}',     subject='DWH Reports updated',     html_content=dedent(&quot;&quot;&quot;\u0413\u043e\u0441\u043f\u043e\u0434\u0430 \u0445\u043e\u0440\u043e\u0448\u0438\u0435, \u043e\u0442\u0447\u0435\u0442\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b&quot;&quot;&quot;),     trigger_rule=TriggerRule.ALL_SUCCESS)  tg = TelegramBotSendMessage(     task_id='telegram_fail', dag=dag,     tg_bot_conn_id='tg_main',     chat_id='{{ var.value.failures_chat }}',     message=dedent(&quot;&quot;&quot;\\          \u041d\u0430\u0442\u0430\u0448, \u043f\u0440\u043e\u0441\u044b\u043f\u0430\u0439\u0441\u044f, \u043c\u044b {{ dag.dag_id }} \u0443\u0440\u043e\u043d\u0438\u043b\u0438         &quot;&quot;&quot;),     trigger_rule=TriggerRule.ONE_FAILED)  for source, target in reports:     queries = [f&quot;TRUNCATE TABLE {target}&quot;,                f&quot;INSERT INTO {target} SELECT * FROM {source}&quot;]      report_update = VerticaOperator(         task_id=target.replace('reports.', ''),         sql=queries, vertica_conn_id='dwh',         task_concurrency=1, dag=dag)      report_update &gt;&gt; [email, tg]<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u0441\u0435 \u0432\u0435\u0434\u044c \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0434\u0435\u043b\u0430\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u043b\u043a\u0443 \u043e\u0442\u0447\u0435\u0442\u043e\u0432? \u042d\u0442\u043e \u0441\u043d\u043e\u0432\u0430 \u043e\u043d\u0430: \u0435\u0441\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432, \u043e\u0442\u043a\u0443\u0434\u0430 \u0437\u0430\u0431\u0440\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435; \u0435\u0441\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a, \u043a\u0443\u0434\u0430 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c; \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u043f\u043e\u0441\u0438\u0433\u043d\u0430\u043b\u0438\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0441\u0451 \u0441\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0438\u043b\u0438 \u0441\u043b\u043e\u043c\u0430\u043b\u043e\u0441\u044c (\u043d\u0443 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u043e \u043d\u0430\u0441, \u043d\u0435\u0442).<\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430 \u043f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043f\u043e \u0444\u0430\u0439\u043b\u0443 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u043e\u0432\u044b\u0435 \u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u044b\u0435 \u0448\u0442\u0443\u043a\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>from commons.operators import TelegramBotSendMessage<\/code> \u2014 \u043d\u0430\u043c \u043d\u0438\u0447\u0442\u043e \u043d\u0435 \u043c\u0435\u0448\u0430\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b, \u0447\u0435\u043c \u043c\u044b \u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c, \u0441\u0434\u0435\u043b\u0430\u0432 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043e\u0447\u043a\u0443 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0432 \u0420\u0430\u0437\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439. (\u041e\u0431 \u044d\u0442\u043e\u043c \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0435 \u043c\u044b \u0435\u0449\u0435 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043d\u0438\u0436\u0435);<\/li>\n<li><code>default_args={}<\/code> \u2014 \u0434\u0430\u0433 \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0434\u043d\u0438 \u0438 \u0442\u0435 \u0436\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0432\u0441\u0435\u043c \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0430\u043c;<\/li>\n<li><code>to='{{ var.value.all_the_kings_men }}'<\/code> \u2014 \u043f\u043e\u043b\u0435 <code>to<\/code> \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u0437\u0430\u0445\u0430\u0440\u0434\u043a\u043e\u0436\u0435\u043d\u043d\u044b\u043c, \u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c\u044b\u043c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Jinja \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c email-\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u044f \u0437\u0430\u0431\u043e\u0442\u043b\u0438\u0432\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0432 <code>Admin\/Variables<\/code>;<\/li>\n<li><code>trigger_rule=TriggerRule.ALL_SUCCESS<\/code> \u2014 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0430. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u043f\u0438\u0441\u044c\u043c\u043e \u043f\u043e\u043b\u0435\u0442\u0438\u0442 \u0431\u043e\u0441\u0441\u0430\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0432\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 <strong>\u0443\u0441\u043f\u0435\u0448\u043d\u043e<\/strong>;<\/li>\n<li><code>tg_bot_conn_id='tg_main'<\/code> \u2014 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b <code>conn_id<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0432 \u0441\u0435\u0431\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0432 <code>Admin\/Connections<\/code>;<\/li>\n<li><code>trigger_rule=TriggerRule.ONE_FAILED<\/code> \u2014 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 Telegram \u0443\u043b\u0435\u0442\u044f\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0443\u043f\u0430\u0432\u0448\u0438\u0445 \u0442\u0430\u0441\u043a\u043e\u0432;<\/li>\n<li><code>task_concurrency=1<\/code> \u2014 \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u043c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 task instances \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0430\u0441\u043a\u0430. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 <code>VerticaOperator<\/code> (\u0441\u043c\u043e\u0442\u0440\u044f\u0449\u0438\u0445 \u043d\u0430 \u043e\u0434\u043d\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u0443);<\/li>\n<li>\n<p><code>report_update &gt;&gt; [email, tg]<\/code> \u2014 \u0432\u0441\u0435 <code>VerticaOperator<\/code> \u0441\u043e\u0439\u0434\u0443\u0442\u0441\u044f \u0432 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u043f\u0438\u0441\u044c\u043c\u0430 \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u0432\u043e\u0442 \u0442\u0430\u043a:<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/tt\/gm\/ju\/ttgmjuqvhnw0jn3wifxrp5lorfu.png\"><\/p>\n<p>  <\/p>\n<p>\u041d\u043e \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432-\u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u0432 \u0441\u0442\u043e\u044f\u0442 \u0440\u0430\u0437\u043d\u044b\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430, \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d. \u0412 Tree View \u0432\u0441\u0451 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u043d\u0435\u0435 \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e:<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/wd\/td\/md\/wdtdmdsmptcbhiifo60nyvvahoi.png\"><\/p>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0421\u043a\u0430\u0436\u0443 \u043f\u0430\u0440\u0443 \u0441\u043b\u043e\u0432 \u043e <strong>\u043c\u0430\u043a\u0440\u043e\u0441\u0430\u0445<\/strong> \u0438 \u0438\u0445 \u0434\u0440\u0443\u0437\u044c\u044f\u0445 \u2014 <strong>\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445<\/strong>. <\/p>\n<p>  <\/p>\n<p>\u041c\u0430\u043a\u0440\u043e\u0441\u044b \u2014 \u044d\u0442\u043e Jinja-\u043f\u043b\u0435\u0439\u0441\u0445\u043e\u043b\u0434\u0435\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0440\u0430\u0437\u043d\u0443\u044e \u043f\u043e\u043b\u0435\u0437\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"sql\">SELECT     id,     payment_dtm,     payment_type,     client_id FROM orders.payments WHERE     payment_dtm::DATE = '{{ ds }}'::DATE<\/code><\/pre>\n<p>  <\/p>\n<p><code>{{ ds }}<\/code> \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u0442\u0441\u044f \u0432 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 <code>execution_date<\/code> \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <code>YYYY-MM-DD<\/code>: <code>2020-07-14<\/code>. \u0421\u0430\u043c\u043e\u0435 \u043f\u0440\u0438\u044f\u0442\u043d\u043e\u0435, \u0447\u0442\u043e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u043f\u0440\u0438\u0431\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0433\u0432\u043e\u0437\u0434\u044f\u043c\u0438 \u043a \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0443 \u0442\u0430\u0441\u043a\u0430 (\u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0438\u043a\u0443 \u0432 Tree View), \u0438 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u043b\u0435\u0439\u0441\u0445\u043e\u043b\u0434\u0435\u0440\u044b \u0440\u0430\u0441\u043a\u0440\u043e\u044e\u0442\u0441\u044f \u0432 \u0442\u0435 \u0436\u0435 \u0441\u0430\u043c\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u0441\u0432\u043e\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043d\u043e\u043f\u043a\u0438 Rendered \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0430\u0441\u043a-\u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0435. \u0412\u043e\u0442 \u0442\u0430\u043a \u0443 \u0442\u0430\u0441\u043a\u0430 \u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u043f\u0438\u0441\u044c\u043c\u0430:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/mk\/yo\/6f\/mkyo6fll0ukeg6jj38vklejac_e.png\"><\/p>\n<p>  <\/p>\n<p>\u0410 \u0442\u0430\u043a \u0443 \u0442\u0430\u0441\u043a\u0438 \u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/on\/ac\/rr\/onacrrefucb7vupbaoprergwrsu.png\"><\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 \u043c\u0430\u043a\u0440\u043e\u0441\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0437\u0434\u0435\u0441\u044c: <a href=\"https:\/\/airflow.apache.org\/docs\/stable\/macros-ref.html\" rel=\"nofollow\">Macros Reference<\/a><\/p>\n<p>  <\/p>\n<p>\u0411\u043e\u043b\u0435\u0435 \u0442\u043e\u0433\u043e, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043c\u0430\u043a\u0440\u043e\u0441\u044b, \u043d\u043e \u044d\u0442\u043e \u0443\u0436\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0434\u0440\u0443\u0433\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043c\u0438\u043c\u043e \u043f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0448\u0442\u0443\u043a, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0432\u043e\u0438\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 (\u0432\u044b\u0448\u0435 \u0432 \u043a\u043e\u0434\u0435 \u044f \u0443\u0436\u0435 \u044d\u0442\u0438\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f). \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 <code>Admin\/Variables<\/code> \u043f\u0430\u0440\u0443 \u0448\u0442\u0443\u043a:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/dc\/_t\/it\/dc_titm756tfq-g09mtuuannq4u.png\"><\/p>\n<p>  <\/p>\n<p>\u0412\u0441\u0451, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f: <\/p>\n<p>  <\/p>\n<pre><code class=\"python\">TelegramBotSendMessage(chat_id='{{ var.value.failures_chat }}')<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043a\u0430\u043b\u044f\u0440, \u0430 \u043c\u043e\u0436\u0435\u0442 \u043b\u0435\u0436\u0430\u0442\u044c \u0438 JSON. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 JSON-\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"json\">bot_config  {     &quot;bot&quot;: {         &quot;token&quot;: 881hskdfASDA16641,         &quot;name&quot;: &quot;Verter&quot;     },     &quot;service&quot;: &quot;TG&quot; }<\/code><\/pre>\n<p>  <\/p>\n<p>\u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u0443\u0442\u044c \u043a \u043d\u0443\u0436\u043d\u043e\u043c\u0443 \u043a\u043b\u044e\u0447\u0443: <code>{{ var.json.bot_config.bot.token }}<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0421\u043a\u0430\u0436\u0443 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u043e\u0434\u043d\u043e \u0441\u043b\u043e\u0432\u043e \u0438 \u043f\u043e\u043a\u0430\u0436\u0443 \u043e\u0434\u0438\u043d \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u043f\u0440\u043e <strong>\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f<\/strong>. \u0422\u0443\u0442 \u0432\u0441\u0451 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u043e: \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 <code>Admin\/Connections<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435, \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u0442\u0443\u0434\u0430 \u043d\u0430\u0448\u0438 \u043b\u043e\u0433\u0438\u043d\u044b\/\u043f\u0430\u0440\u043e\u043b\u0438 \u0438 \u0431\u043e\u043b\u0435\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b. \u0412\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/o-\/ez\/0e\/o-ez0evkxfjprnx_pm24juq0sog.png\"><\/p>\n<p>  <\/p>\n<p>\u041f\u0430\u0440\u043e\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c (\u0431\u043e\u043b\u0435\u0435 \u0442\u0449\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0435\u043c \u0432 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e), \u0430 \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u0438\u043f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f (\u043a\u0430\u043a \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u0434\u043b\u044f <code>tg_main<\/code>) \u2014 \u0434\u0435\u043b\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u0438\u043f\u043e\u0432 \u0437\u0430\u0448\u0438\u0442 \u0432 \u043c\u043e\u0434\u0435\u043b\u044f\u0445 Airflow \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044e \u0431\u0435\u0437 \u0432\u043b\u0435\u0437\u0430\u043d\u0438\u044f \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 \u043d\u0435 \u043f\u043e\u0434\u0434\u0430\u0435\u0442\u0441\u044f (\u0435\u0441\u043b\u0438 \u0432\u0434\u0440\u0443\u0433 \u044f \u0447\u0435\u0433\u043e-\u0442\u043e \u043d\u0435 \u0434\u043e\u0433\u0443\u0433\u043b\u0438\u043b \u2014 \u043f\u0440\u043e\u0448\u0443 \u043c\u0435\u043d\u044f \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u0442\u044c), \u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0440\u0435\u0434\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u043d\u0430\u043c \u043d\u0438\u0447\u0442\u043e \u043d\u0435 \u043f\u043e\u043c\u0435\u0448\u0430\u0435\u0442.<\/p>\n<p>  <\/p>\n<p>\u0410 \u0435\u0449\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0441 \u043e\u0434\u043d\u0438\u043c \u0438\u043c\u0435\u043d\u0435\u043c: \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u0435\u0442\u043e\u0434 <code>BaseHook.get_connection()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u0441\u0442\u0430\u0435\u0442 \u043d\u0430\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e \u0438\u043c\u0435\u043d\u0438, \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c <em>\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0433\u043e<\/em> \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0442\u0451\u0437\u043e\u043a (\u0431\u044b\u043b\u043e \u0431\u044b \u043b\u043e\u0433\u0438\u0447\u043d\u0435\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c Round Robin, \u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u043c \u044d\u0442\u043e \u043d\u0430 \u0441\u043e\u0432\u0435\u0441\u0442\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 Airflow).<\/p>\n<p>  <\/p>\n<blockquote><p>Variables \u0438 Connections, \u0431\u0435\u0437\u0443\u0441\u043b\u043e\u0432\u043d\u043e, \u043a\u043b\u0430\u0441\u0441\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430, \u043d\u043e \u0432\u0430\u0436\u043d\u043e \u043d\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u0442\u044c \u0431\u0430\u043b\u0430\u043d\u0441: \u043a\u0430\u043a\u0438\u0435 \u0447\u0430\u0441\u0442\u0438 \u0432\u0430\u0448\u0438\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0432\u044b \u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043a\u043e\u0434\u0435, \u0430 \u043a\u0430\u043a\u0438\u0435 \u2014 \u043e\u0442\u0434\u0430\u0435\u0442\u0435 \u043d\u0430 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 Airflow. C \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044f\u0449\u0438\u043a \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u043e \u0447\u0435\u0440\u0435\u0437 UI. \u0410 \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u2014 \u044d\u0442\u043e \u0432\u0441\u0451-\u0442\u0430\u043a\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u043a \u043c\u044b\u0448\u0435\u043a\u043b\u0438\u043a\u0443, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b (\u044f) \u0445\u043e\u0442\u0435\u043b\u0438 \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f.<\/p><\/blockquote>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\u043c\u0438 \u2014 \u044d\u0442\u043e \u043e\u0434\u043d\u0430 \u0438\u0437 \u0437\u0430\u0434\u0430\u0447 <strong>\u0445\u0443\u043a\u043e\u0432<\/strong>. \u0412\u043e\u043e\u0431\u0449\u0435 \u0445\u0443\u043a\u0438 Airflow \u2014 \u044d\u0442\u043e \u0442\u043e\u0447\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0435\u0433\u043e \u043a \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c. \u041a \u043f\u0440\u0438\u043c\u0435\u0440\u0443, <code>JiraHook<\/code> \u043e\u0442\u043a\u0440\u043e\u0435\u0442 \u0434\u043b\u044f \u043d\u0430\u0441 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 Jira (\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0447\u043a\u0438 \u043f\u043e\u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0442\u0443\u0434\u0430-\u0441\u044e\u0434\u0430), \u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>SambaHook<\/code> \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0448\u0438\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043d\u0430 <code>smb<\/code>-\u0442\u043e\u0447\u043a\u0443.<\/p>\n<p>  <\/p>\n<h3 id=\"razbiraem-kastomnyy-operator\">\u0420\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440<\/h3>\n<p>  <\/p>\n<p>\u0418 \u043c\u044b \u0432\u043f\u043b\u043e\u0442\u043d\u0443\u044e \u043f\u043e\u0434\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u043d <code>TelegramBotSendMessage<\/code><\/p>\n<p>  <\/p>\n<p>\u041a\u043e\u0434 <code>commons\/operators.py<\/code> \u0441 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">from typing import Union  from airflow.operators import BaseOperator  from commons.hooks import TelegramBotHook, TelegramBot  class TelegramBotSendMessage(BaseOperator):     &quot;&quot;&quot;Send message to chat_id using TelegramBotHook      Example:         &gt;&gt;&gt; TelegramBotSendMessage(         ...     task_id='telegram_fail', dag=dag,         ...     tg_bot_conn_id='tg_bot_default',         ...     chat_id='{{ var.value.all_the_young_dudes_chat }}',         ...     message='{{ dag.dag_id }} failed :(',         ...     trigger_rule=TriggerRule.ONE_FAILED)     &quot;&quot;&quot;     template_fields = ['chat_id', 'message']      def __init__(self,                  chat_id: Union[int, str],                  message: str,                  tg_bot_conn_id: str = 'tg_bot_default',                  *args, **kwargs):         super().__init__(*args, **kwargs)          self._hook = TelegramBotHook(tg_bot_conn_id)         self.client: TelegramBot = self._hook.client         self.chat_id = chat_id         self.message = message      def execute(self, context):         print(f'Send &quot;{self.message}&quot; to the chat {self.chat_id}')         self.client.send_message(chat_id=self.chat_id,                                  message=self.message)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0434\u0435\u0441\u044c, \u043a\u0430\u043a \u0438 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0432 Airflow, \u0432\u0441\u0451 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041e\u0442\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043e\u0442 <code>BaseOperator<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u043d\u043e\u0433\u043e Airflow-\u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0445 \u0448\u0442\u0443\u043a (\u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 \u0434\u043e\u0441\u0443\u0433\u0435)<\/li>\n<li>\u041e\u0431\u044a\u044f\u0432\u0438\u043b\u0438 \u043f\u043e\u043b\u044f <code>template_fields<\/code>, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 Jinja \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u043c\u0430\u043a\u0440\u043e\u0441\u044b \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/li>\n<li>\u041e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f <code>__init__()<\/code>, \u0440\u0430\u0441\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044f, \u0433\u0434\u0435 \u043d\u0430\u0434\u043e.<\/li>\n<li>\u041e\u0431 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0435\u0434\u043a\u0430 \u0442\u043e\u0436\u0435 \u043d\u0435 \u0437\u0430\u0431\u044b\u043b\u0438.<\/li>\n<li>\u041e\u0442\u043a\u0440\u044b\u043b\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0445\u0443\u043a <code>TelegramBotHook<\/code>, \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043e\u0442 \u043d\u0435\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442-\u043a\u043b\u0438\u0435\u043d\u0442.<\/li>\n<li>\u041e\u0432\u0435\u0440\u0440\u0430\u0439\u0434\u043d\u0443\u043b\u0438 (\u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438) \u043c\u0435\u0442\u043e\u0434 <code>BaseOperator.execute()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 Airfow \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0435\u0440\u0433\u0438\u0432\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0441\u0442\u0443\u043f\u0438\u0442 \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 \u2014 \u0432 \u043d\u0435\u043c \u043c\u044b \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u043d\u0430 \u0437\u0430\u0431\u044b\u0432 \u0437\u0430\u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f. (\u041b\u043e\u0433\u0438\u0440\u0443\u0435\u043c\u0441\u044f, \u043a\u0441\u0442\u0430\u0442\u0438, \u043f\u0440\u044f\u043c\u043e \u0432 <code>stdout<\/code> \u0438 <code>stderr<\/code> \u2014 Airflow \u0432\u0441\u0451 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442, \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u043e\u0431\u0435\u0440\u043d\u0435\u0442, \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442, \u043a\u0443\u0434\u0430 \u043d\u0430\u0434\u043e.)<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0432 <code>commons\/hooks.py<\/code>. \u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0444\u0430\u0439\u043b\u0438\u043a\u0430, \u0441 \u0441\u0430\u043c\u0438\u043c \u0445\u0443\u043a\u043e\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">from typing import Union  from airflow.hooks.base_hook import BaseHook from requests_toolbelt.sessions import BaseUrlSession  class TelegramBotHook(BaseHook):     &quot;&quot;&quot;Telegram Bot API hook      Note: add a connection with empty Conn Type and don't forget     to fill Extra:          {&quot;bot_token&quot;: &quot;YOuRAwEsomeBOtToKen&quot;}     &quot;&quot;&quot;     def __init__(self,                  tg_bot_conn_id='tg_bot_default'):         super().__init__(tg_bot_conn_id)          self.tg_bot_conn_id = tg_bot_conn_id         self.tg_bot_token = None         self.client = None         self.get_conn()      def get_conn(self):         extra = self.get_connection(self.tg_bot_conn_id).extra_dejson         self.tg_bot_token = extra['bot_token']         self.client = TelegramBot(self.tg_bot_token)         return self.client<\/code><\/pre>\n<p>  <\/p>\n<p>\u042f \u0434\u0430\u0436\u0435 \u043d\u0435 \u0437\u043d\u0430\u044e, \u0447\u0442\u043e \u0442\u0443\u0442 \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0442\u044c, \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043c\u0435\u0447\u0443 \u0432\u0430\u0436\u043d\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u043c\u0441\u044f, \u0434\u0443\u043c\u0430\u0435\u043c \u043d\u0430\u0434 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u2014 \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043e\u0434\u0438\u043d: <code>conn_id<\/code>;<\/li>\n<li>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b: \u044f \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u043b\u0441\u044f <code>get_conn()<\/code>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u044f \u043f\u043e\u043b\u0443\u0447\u0430\u044e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0438 \u0432\u0441\u0435\u0433\u043e-\u043d\u0430\u0432\u0441\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u044e \u0441\u0435\u043a\u0446\u0438\u044e <code>extra<\/code> (\u044d\u0442\u043e \u043f\u043e\u043b\u0435 \u0434\u043b\u044f JSON), \u0432 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u044f (\u043f\u043e \u0441\u0432\u043e\u0435\u0439 \u0436\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438!) \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0442\u043e\u043a\u0435\u043d Telegram-\u0431\u043e\u0442\u0430: <code>{&quot;bot_token&quot;: &quot;YOuRAwEsomeBOtToKen&quot;}<\/code>.<\/li>\n<li>\u0421\u043e\u0437\u0434\u0430\u044e \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043d\u0430\u0448\u0435\u0433\u043e <code>TelegramBot<\/code>, \u043e\u0442\u0434\u0430\u0432\u0430\u044f \u0435\u043c\u0443 \u0443\u0436\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0451. \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0438\u0437 \u0445\u0443\u043a\u0430 \u043c\u043e\u0436\u043d\u043e c \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>TelegramBotHook().clent<\/code> \u0438\u043b\u0438 <code>TelegramBotHook().get_conn()<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0418 \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0444\u0430\u0439\u043b\u0438\u043a\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043c\u0438\u043a\u0440\u043e\u043e\u0431\u0451\u0440\u0442\u043e\u0447\u043a\u0443 \u0434\u043b\u044f Telegram REST API, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0442\u0430\u0449\u0438\u0442\u044c \u0442\u043e\u0442 \u0436\u0435 <a href=\"https:\/\/github.com\/python-telegram-bot\/python-telegram-bot\" rel=\"nofollow\"><code>python-telegram-bot<\/code><\/a> \u0440\u0430\u0434\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 <code>sendMessage<\/code>.<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">class TelegramBot:     &quot;&quot;&quot;Telegram Bot API wrapper      Examples:         &gt;&gt;&gt; TelegramBot('YOuRAwEsomeBOtToKen', '@myprettydebugchat').send_message('Hi, darling')         &gt;&gt;&gt; TelegramBot('YOuRAwEsomeBOtToKen').send_message('Hi, darling', chat_id=-1762374628374)     &quot;&quot;&quot;     API_ENDPOINT = 'https:\/\/api.telegram.org\/bot{}\/'      def __init__(self, tg_bot_token: str, chat_id: Union[int, str] = None):         self._base_url = TelegramBot.API_ENDPOINT.format(tg_bot_token)         self.session = BaseUrlSession(self._base_url)         self.chat_id = chat_id      def send_message(self, message: str, chat_id: Union[int, str] = None):         method = 'sendMessage'          payload = {'chat_id': chat_id or self.chat_id,                    'text': message,                    'parse_mode': 'MarkdownV2'}          response = self.session.post(method, data=payload).json()         if not response.get('ok'):             raise TelegramBotException(response)  class TelegramBotException(Exception):     def __init__(self, *args, **kwargs):         super().__init__((args, kwargs))<\/code><\/pre>\n<p>  <\/p>\n<blockquote><p>\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u2014 \u0441\u043b\u043e\u0436\u0438\u0442\u044c \u0432\u0441\u0451 \u044d\u0442\u043e: <code>TelegramBotSendMessage<\/code>, <code>TelegramBotHook<\/code>, <code>TelegramBot<\/code> \u2014 \u0432 \u043f\u043b\u0430\u0433\u0438\u043d, \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0432 \u043e\u0431\u0449\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u0438 \u043e\u0442\u0434\u0430\u0442\u044c \u0432 Open Source.<\/p><\/blockquote>\n<p>\u041f\u043e\u043a\u0430 \u043c\u044b \u0432\u0441\u0451 \u044d\u0442\u043e \u0438\u0437\u0443\u0447\u0430\u043b\u0438, \u043d\u0430\u0448\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0443\u0441\u043f\u0435\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u0432\u0430\u043b\u0438\u0442\u044c\u0441\u044f \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043c\u043d\u0435 \u0432 \u043a\u0430\u043d\u0430\u043b \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435. \u041f\u043e\u0439\u0434\u0443 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0447\u0442\u043e \u043e\u043f\u044f\u0442\u044c \u043d\u0435 \u0442\u0430\u043a&#8230;<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/qv\/zw\/lk\/qvzwlk2viac_7tb9cr3nc9wo-1e.png\"><br \/>  <em>\u0412 \u043d\u0430\u0448\u0435\u043c \u0434\u0430\u0433\u0435 \u0447\u0442\u043e-\u0442\u043e \u0441\u043b\u043e\u043c\u0430\u043b\u043e\u0441\u044c! \u0410 \u043d\u0438 \u044d\u0442\u043e\u0433\u043e \u043b\u0438 \u043c\u044b \u0436\u0434\u0430\u043b\u0438? \u0418\u043c\u0435\u043d\u043d\u043e!<\/em><\/p>\n<p>  <\/p>\n<h3 id=\"nalivat-to-budesh\">\u041d\u0430\u043b\u0438\u0432\u0430\u0442\u044c-\u0442\u043e \u0431\u0443\u0434\u0435\u0448\u044c?<\/h3>\n<p>  <\/p>\n<p>\u0427\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0435, \u0447\u0442\u043e-\u0442\u043e \u044f \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u043b? \u0412\u0440\u043e\u0434\u0435 \u0431\u044b \u043e\u0431\u0435\u0449\u0430\u043b \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 SQL Server \u0432 Vertica \u043f\u0435\u0440\u0435\u043b\u0438\u0432\u0430\u0442\u044c, \u0438 \u0442\u0443\u0442 \u0432\u0437\u044f\u043b \u0438 \u0441\u044a\u0435\u0445\u0430\u043b \u0441 \u0442\u0435\u043c\u044b, \u043d\u0435\u0433\u043e\u0434\u044f\u0439!<\/p>\n<p>  <\/p>\n<p>\u0417\u043b\u043e\u0434\u0435\u044f\u043d\u0438\u0435 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u044b\u043c, \u044f \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u044f\u0437\u0430\u043d \u0431\u044b\u043b \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0430\u043c \u043a\u043e\u0435-\u043a\u0430\u043a\u0443\u044e \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u043b\u043e\u0433\u0438\u044e. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0435\u0445\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<p>  <\/p>\n<p>\u041f\u043b\u0430\u043d \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b \u0442\u0430\u043a\u043e\u0439:<\/p>\n<p>  <\/p>\n<ol>\n<li><del>\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u0430\u0433<\/del><\/li>\n<li><del>\u041d\u0430\u0433\u0435\u043d\u0435\u0440\u0438\u0442\u044c \u0442\u0430\u0441\u043a\u0438<\/del><\/li>\n<li><del>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u0432\u0441\u0451 \u043a\u0440\u0430\u0441\u0438\u0432\u043e<\/del><\/li>\n<li>\u041f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0442\u044c \u0437\u0430\u043b\u0438\u0432\u043a\u0430\u043c \u043d\u043e\u043c\u0435\u0440\u0430 \u0441\u0435\u0441\u0441\u0438\u0439<\/li>\n<li>\u0417\u0430\u0431\u0440\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 SQL Server<\/li>\n<li>\u041f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 Vertica<\/li>\n<li>\u0421\u043e\u0431\u0440\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u0418\u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u044d\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c, \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u043d\u0430\u0448\u0435\u043c\u0443 <code>docker-compose.yml<\/code>:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">docker-compose.db.yml<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"plaintext\">version: '3.4'  x-mssql-base: &amp;mssql-base   image: mcr.microsoft.com\/mssql\/server:2017-CU21-ubuntu-16.04   restart: always   environment:     ACCEPT_EULA: Y     MSSQL_PID: Express     SA_PASSWORD: SayThanksToSatiaAt2020     MSSQL_MEMORY_LIMIT_MB: 1024  services:   dwh:     image: jbfavre\/vertica:9.2.0-7_ubuntu-16.04    mssql_0:     &lt;&lt;: *mssql-base    mssql_1:     &lt;&lt;: *mssql-base    mssql_2:     &lt;&lt;: *mssql-base    mssql_init:     image: mio101\/py3-sql-db-client-base     command: python3 .\/mssql_init.py     depends_on:       - mssql_0       - mssql_1       - mssql_2     environment:       SA_PASSWORD: SayThanksToSatiaAt2020     volumes:       - .\/mssql_init.py:\/mssql_init.py       - .\/dags\/commons\/datasources.py:\/commons\/datasources.py<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u0422\u0430\u043c \u043c\u044b \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c:<\/p>\n<p>  <\/p>\n<ul>\n<li>Vertica \u043a\u0430\u043a \u0445\u043e\u0441\u0442 <code>dwh<\/code> \u0441 \u0441\u0430\u043c\u044b\u043c\u0438 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438,<\/li>\n<li>\u0442\u0440\u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 SQL Server,<\/li>\n<li>\u043d\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0431\u0430\u0437\u044b \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u043a\u043e\u0435-\u043a\u0430\u043a\u0438\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 (\u043d\u0438 \u0432 \u043a\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435 \u0437\u0430\u0433\u043b\u044f\u0434\u044b\u0432\u0430\u0439\u0442\u0435 \u0432 <code>mssql_init.py<\/code>!)<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0432\u0441\u0451 \u0434\u043e\u0431\u0440\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0447\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u043e\u0439, \u0447\u0435\u043c \u0432 \u043f\u0440\u043e\u0448\u043b\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ docker-compose -f docker-compose.yml -f docker-compose.db.yml up --scale worker=3<\/code><\/pre>\n<p>  <\/p>\n<p>\u0427\u0442\u043e \u043d\u0430\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b \u043d\u0430\u0448 \u0447\u0443\u0434\u043e\u0440\u0430\u043d\u0434\u043e\u043c\u0430\u0439\u0437\u0435\u0440, \u043c\u043e\u0436\u043d\u043e, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0432\u0448\u0438\u0441\u044c \u043f\u0443\u043d\u043a\u0442\u043e\u043c <code>Data Profiling\/Ad Hoc Query<\/code>:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/db\/0w\/ko\/db0wkohfga8zhu-6_v5wynw7t9s.png\"><br \/>  <em>\u0413\u043b\u0430\u0432\u043d\u043e\u0435, \u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430\u043c<\/em><\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 <strong>ETL-\u0441\u0435\u0441\u0441\u0438\u044f\u0445<\/strong> \u044f \u043d\u0435 \u0431\u0443\u0434\u0443, \u0442\u0430\u043c \u0432\u0441\u0451 \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e: \u0434\u0435\u043b\u0430\u0435\u043c \u0431\u0430\u0437\u0443, \u0432 \u043d\u0435\u0439 \u0442\u0430\u0431\u043b\u0438\u0447\u043a\u0443, \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0432\u0441\u0451 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u043e\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0435\u043b\u0430\u0435\u043c \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">with Session(task_name) as session:     print('Load', session.id, 'started')      # Load worflow     ...      session.successful = True     session.loaded_rows = 15<\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">session.py<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">from sys import stderr  class Session:     &quot;&quot;&quot;ETL workflow session      Example:         with Session(task_name) as session:             print(session.id)             session.successful = True             session.loaded_rows = 15             session.comment = 'Well done'     &quot;&quot;&quot;      def __init__(self, connection, task_name):         self.connection = connection         self.connection.autocommit = True          self._task_name = task_name         self._id = None          self.loaded_rows = None         self.successful = None         self.comment = None      def __enter__(self):         return self.open()      def __exit__(self, exc_type, exc_val, exc_tb):         if any(exc_type, exc_val, exc_tb):             self.successful = False             self.comment = f'{exc_type}: {exc_val}\\n{exc_tb}'             print(exc_type, exc_val, exc_tb, file=stderr)         self.close()      def __repr__(self):         return (f'&lt;{self.__class__.__name__} '                  f'id={self.id} '                  f'task_name=&quot;{self.task_name}&quot;&gt;')      @property     def task_name(self):         return self._task_name      @property     def id(self):         return self._id      def _execute(self, query, *args):         with self.connection.cursor() as cursor:             cursor.execute(query, args)             return cursor.fetchone()[0]      def _create(self):         query = &quot;&quot;&quot;             CREATE TABLE IF NOT EXISTS sessions (                 id          SERIAL       NOT NULL PRIMARY KEY,                 task_name   VARCHAR(200) NOT NULL,                  started     TIMESTAMPTZ  NOT NULL DEFAULT current_timestamp,                 finished    TIMESTAMPTZ           DEFAULT current_timestamp,                 successful  BOOL,                  loaded_rows INT,                 comment     VARCHAR(500)             );             &quot;&quot;&quot;         self._execute(query)      def open(self):         query = &quot;&quot;&quot;             INSERT INTO sessions (task_name, finished)             VALUES (%s, NULL)             RETURNING id;             &quot;&quot;&quot;         self._id = self._execute(query, self.task_name)         print(self, 'opened')         return self      def close(self):         if not self._id:             raise SessionClosedError('Session is not open')         query = &quot;&quot;&quot;             UPDATE sessions             SET                 finished    = DEFAULT,                 successful  = %s,                 loaded_rows = %s,                 comment     = %s             WHERE                 id = %s             RETURNING id;             &quot;&quot;&quot;         self._execute(query, self.successful, self.loaded_rows,                       self.comment, self.id)         print(self, 'closed',               ', successful: ', self.successful,               ', Loaded: ', self.loaded_rows,               ', comment:', self.comment)  class SessionError(Exception):     pass  class SessionClosedError(SessionError):     pass<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u041d\u0430\u0441\u0442\u0430\u043b\u0430 \u043f\u043e\u0440\u0430 <strong>\u0437\u0430\u0431\u0440\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435<\/strong> \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u043f\u043e\u043b\u0443\u0442\u043e\u0440\u0430 \u0441\u043e\u0442\u0435\u043d \u0442\u0430\u0431\u043b\u0438\u0446. \u0421\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u0437\u0430\u0442\u0435\u0439\u043b\u0438\u0432\u044b\u0445 \u0441\u0442\u0440\u043e\u0447\u0435\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">source_conn = MsSqlHook(mssql_conn_id=src_conn_id, schema=src_schema).get_conn()  query = f&quot;&quot;&quot;     SELECT          id, start_time, end_time, type, data     FROM dbo.Orders     WHERE         CONVERT(DATE, start_time) = '{dt}'     &quot;&quot;&quot;  df = pd.read_sql_query(query, source_conn)<\/code><\/pre>\n<p>  <\/p>\n<ol>\n<li>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0445\u0443\u043a\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0438\u0437 Airflow <code>pymssql<\/code>-\u043a\u043e\u043d\u043d\u0435\u043a\u0442 <\/li>\n<li>\u0412 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u0438\u043c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0432 \u0432\u0438\u0434\u0435 \u0434\u0430\u0442\u044b \u2014 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0435\u0451 \u043f\u043e\u0434\u0431\u0440\u043e\u0441\u0438\u0442 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440.<\/li>\n<li>\u0421\u043a\u0430\u0440\u043c\u043b\u0438\u0432\u0430\u0435\u043c \u043d\u0430\u0448 \u0437\u0430\u043f\u0440\u043e\u0441 <code>pandas<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u0441\u0442\u0430\u043d\u0435\u0442 \u0434\u043b\u044f \u043d\u0430\u0441 <code>DataFrame<\/code> \u2014 \u043e\u043d \u043d\u0430\u043c \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c.<\/li>\n<\/ol>\n<p>  <\/p>\n<blockquote><p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043f\u043e\u0434\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 <code>{dt}<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <code>%s<\/code> \u043d\u0435 \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u044f \u0437\u043b\u043e\u0431\u043d\u044b\u0439 \u0411\u0443\u0440\u0430\u0442\u0438\u043d\u043e, \u0430 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e <code>pandas<\/code> \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0432\u043b\u0430\u0434\u0430\u0442\u044c \u0441 <code>pymssql<\/code> \u0438 \u043f\u043e\u0434\u0441\u043e\u0432\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u043c\u0443 <code>params: List<\/code>, \u0445\u043e\u0442\u044f \u0442\u043e\u0442 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0447\u0435\u0442 <code>tuple<\/code>.<br \/>  \u0422\u0430\u043a\u0436\u0435 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <code>pymssql<\/code> \u0440\u0435\u0448\u0438\u043b \u0431\u043e\u043b\u044c\u0448\u0435 \u0435\u0433\u043e \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c, \u0438 \u0441\u0430\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u044a\u0435\u0445\u0430\u0442\u044c \u043d\u0430 <code>pyodbc<\/code>.<\/p><\/blockquote>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0435\u043c Airflow \u043d\u0430\u0448\u043f\u0438\u0433\u043e\u0432\u0430\u043b \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043d\u0430\u0448\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/oi\/e8\/7u\/oie87uj21o0bfrfv4dvkrxeybrk.png\"><\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0442\u043e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442\u044c \u0441\u043c\u044b\u0441\u043b\u0430 \u043d\u0435\u0442. \u041d\u043e \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0437\u0430\u043b\u0438\u0432\u043a\u0443 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0442\u043e\u0436\u0435 \u0441\u0442\u0440\u0430\u043d\u043d\u043e. \u041d\u043e \u044d\u0442\u043e \u0438 \u043d\u0435 \u043e\u0448\u0438\u0431\u043a\u0430. \u0410-\u0430-\u0430, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c?! \u0410 \u0432\u043e\u0442 \u0447\u0442\u043e:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">if df.empty:     raise AirflowSkipException('No rows to load')<\/code><\/pre>\n<p>  <\/p>\n<p><code>AirflowSkipException<\/code> \u0441\u043a\u0430\u0436\u0435\u0442 Airflow, \u0447\u0442\u043e \u043e\u0448\u0438\u0431\u043a\u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043d\u0435\u0442, \u0430 \u0442\u0430\u0441\u043a \u043c\u044b \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c. \u0412 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u0437\u0435\u043b\u0435\u043d\u044b\u0439 \u0438 \u043d\u0435 \u043a\u0440\u0430\u0441\u043d\u044b\u0439 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0438\u043a, \u0430 \u0446\u0432\u0435\u0442\u0430 pink.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0434\u0431\u0440\u043e\u0441\u0438\u043c \u043d\u0430\u0448\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u043c <strong>\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043b\u043e\u043d\u043e\u043a<\/strong>:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">df['etl_source'] = src_schema df['etl_id'] = session.id df['hash_id'] = hash_pandas_object(df[['etl_source', 'id']])<\/code><\/pre>\n<p>  <\/p>\n<p>\u0410 \u0438\u043c\u0435\u043d\u043d\u043e:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0411\u0414, \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0437\u0430\u0431\u0440\u0430\u043b\u0438 \u0437\u0430\u043a\u0430\u0437\u044b,<\/li>\n<li>\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043d\u0430\u0448\u0435\u0439 \u0437\u0430\u043b\u0438\u0432\u0430\u044e\u0449\u0435\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 (\u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043d\u043e\u0439 <em>\u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0442\u0430\u0441\u043a<\/em>),<\/li>\n<li>\u0425\u044d\u0448 \u043e\u0442 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0437\u0430\u043a\u0430\u0437\u0430 \u2014 \u0447\u0442\u043e\u0431\u044b \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 (\u0433\u0434\u0435 \u0432\u0441\u0451 \u0441\u0441\u044b\u043f\u0435\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u0443) \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u043a\u0430\u0437\u0430.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u0441\u044f \u043f\u0440\u0435\u0434\u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0430\u0433: \u0437\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0451 \u0432 Vertica. \u0410, \u043a\u0430\u043a \u043d\u0438 \u0441\u0442\u0440\u0430\u043d\u043d\u043e, \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 <del>\u044d\u0444\u0444\u0435\u043a\u0442\u043d\u044b\u0445<\/del> \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u2014 \u0447\u0435\u0440\u0435\u0437 CSV!<\/p>\n<p>  <\/p>\n<pre><code class=\"python\"># Export data to CSV buffer buffer = StringIO() df.to_csv(buffer,           index=False, sep='|', na_rep='NUL', quoting=csv.QUOTE_MINIMAL,           header=False, float_format='%.8f', doublequote=False, escapechar='\\\\') buffer.seek(0)  # Push CSV target_conn = VerticaHook(vertica_conn_id=target_conn_id).get_conn()  copy_stmt = f&quot;&quot;&quot;     COPY {target_table}({df.columns.to_list()})      FROM STDIN      DELIMITER '|'      ENCLOSED '&quot;'      ABORT ON ERROR      NULL 'NUL'     &quot;&quot;&quot;  cursor = target_conn.cursor() cursor.copy(copy_stmt, buffer)<\/code><\/pre>\n<p>  <\/p>\n<ol>\n<li>\u041c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043f\u0435\u0446\u043f\u0440\u0438\u0451\u043c\u043d\u0438\u043a <code>StringIO<\/code>.<\/li>\n<li><code>pandas<\/code> \u043b\u044e\u0431\u0435\u0437\u043d\u043e \u0441\u043b\u043e\u0436\u0438\u0442 \u0432 \u043d\u0435\u0433\u043e \u043d\u0430\u0448 <code>DataFrame<\/code> \u0432 \u0432\u0438\u0434\u0435 <code>CSV<\/code>-\u0441\u0442\u0440\u043e\u043a.<\/li>\n<li>\u041e\u0442\u043a\u0440\u043e\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043a \u043d\u0430\u0448\u0435\u0439 \u043b\u044e\u0431\u0438\u043c\u043e\u0439 Vertica \u0445\u0443\u043a\u043e\u043c.<\/li>\n<li>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>copy()<\/code> \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c \u043d\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u044f\u043c\u043e \u0432 \u0412\u0435\u0440\u0442\u0438\u043a\u0443!<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u0418\u0437 \u0434\u0440\u0430\u0439\u0432\u0435\u0440\u0430 \u0437\u0430\u0431\u0435\u0440\u0435\u043c, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u0447\u0435\u043a \u0437\u0430\u0441\u044b\u043f\u0430\u043b\u043e\u0441\u044c, \u0438 \u0441\u043a\u0430\u0436\u0435\u043c \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0443 \u0441\u0435\u0441\u0441\u0438\u0438, \u0447\u0442\u043e \u0432\u0441\u0451 \u041e\u041a:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">session.loaded_rows = cursor.rowcount session.successful = True<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0451.<\/p>\n<p>  <\/p>\n<blockquote><p>\u041d\u0430 \u043f\u0440\u043e\u0434\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0447\u043a\u0443 \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u0417\u0434\u0435\u0441\u044c \u0436\u0435 \u044f \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b \u0441\u0435\u0431\u0435 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442:<\/p><\/blockquote>\n<p>  <\/p>\n<pre><code class=\"python\">create_schema_query = f'CREATE SCHEMA IF NOT EXISTS {target_schema};' create_table_query = f&quot;&quot;&quot;     CREATE TABLE IF NOT EXISTS {target_schema}.{target_table} (          id         INT,          start_time TIMESTAMP,          end_time   TIMESTAMP,          type       INT,          data       VARCHAR(32),          etl_source VARCHAR(200),          etl_id     INT,          hash_id    INT PRIMARY KEY      );&quot;&quot;&quot;  create_table = VerticaOperator(     task_id='create_target',     sql=[create_schema_query,          create_table_query],     vertica_conn_id=target_conn_id,     task_concurrency=1,     dag=dag)<\/code><\/pre>\n<p>  <\/p>\n<blockquote><p>\u042f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>VerticaOperator()<\/code> \u0441\u043e\u0437\u0434\u0430\u044e \u0441\u0445\u0435\u043c\u0443 \u0411\u0414 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0443 (\u0435\u0441\u043b\u0438 \u0438\u0445 \u0435\u0449\u0435 \u043d\u0435\u0442, \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e). \u0413\u043b\u0430\u0432\u043d\u043e\u0435, \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0441\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p><\/blockquote>\n<p>  <\/p>\n<pre><code class=\"python\">for conn_id, schema in sql_server_ds:     load = PythonOperator(         task_id=schema,         python_callable=workflow,         op_kwargs={             'src_conn_id': conn_id,             'src_schema': schema,             'dt': '{{ ds }}',             'target_conn_id': target_conn_id,             'target_table': f'{target_schema}.{target_table}'},         dag=dag)      create_table &gt;&gt; load<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"podvodim-itogi\">\u041f\u043e\u0434\u0432\u043e\u0434\u0438\u043c \u0438\u0442\u043e\u0433\u0438<\/h3>\n<p>  <\/p>\n<blockquote><p>\u2014 \u041d\u0443 \u0432\u043e\u0442, \u2014 \u0441\u043a\u0430\u0437\u0430\u043b \u043c\u044b\u0448\u043e\u043d\u043e\u043a, \u2014 \u043d\u0435 \u043f\u0440\u0430\u0432\u0434\u0430 \u043b\u0438, \u0442\u0435\u043f\u0435\u0440\u044c<br \/>  \u0422\u044b \u0443\u0431\u0435\u0434\u0438\u043b\u0441\u044f, \u0447\u0442\u043e \u0432 \u043b\u0435\u0441\u0443 \u044f \u0441\u0430\u043c\u044b\u0439 \u0441\u0442\u0440\u0430\u0448\u043d\u044b\u0439 \u0437\u0432\u0435\u0440\u044c?<\/p><\/blockquote>\n<p><em>\u0414\u0436\u0443\u043b\u0438\u044f \u0414\u043e\u043d\u0430\u043b\u044c\u0434\u0441\u043e\u043d, \u00ab\u0413\u0440\u0443\u0444\u0444\u0430\u043b\u043e\u00bb<\/em><\/p>\n<p>  <\/p>\n<p>\u0414\u0443\u043c\u0430\u044e, \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0441 \u043c\u043e\u0438\u043c\u0438 \u043a\u043e\u043b\u043b\u0435\u0433\u0430\u043c\u0438 \u0443\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u0441\u043e\u0440\u0435\u0432\u043d\u043e\u0432\u0430\u043d\u0438\u0435: \u043a\u0442\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0441\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442 \u0441 \u043d\u0443\u043b\u044f ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441: \u043e\u043d\u0438 \u0441\u043e \u0441\u0432\u043e\u0438\u043c\u0438 SSIS \u0438 \u043c\u044b\u0448\u043a\u043e\u0439 \u0438 \u044f \u0441 Airflow\u2026 \u0410 \u043f\u043e\u0442\u043e\u043c \u0431\u044b \u043c\u044b \u0435\u0449\u0435 \u0441\u0440\u0430\u0432\u043d\u0438\u043b\u0438 \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d\u0438\u044f\u2026 \u0423\u0445, \u0434\u0443\u043c\u0430\u044e, \u0432\u044b \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u044f \u043e\u0431\u043e\u0439\u0434\u0443 \u0438\u0445 \u043f\u043e \u0432\u0441\u0435\u043c \u0444\u0440\u043e\u043d\u0442\u0430\u043c!<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0436\u0435 \u0447\u0443\u0442\u044c-\u0447\u0443\u0442\u044c \u043f\u043e\u0441\u0435\u0440\u044c\u0435\u0437\u043d\u0435\u0435, \u0442\u043e Apache Airflow \u2014 \u0437\u0430 \u0441\u0447\u0435\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u2014 \u0441\u0434\u0435\u043b\u0430\u043b \u043c\u043e\u044e \u0440\u0430\u0431\u043e\u0442\u0443 <strong>\u0433\u043e\u0440\u0430\u0437\u0434\u043e<\/strong> \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0438 \u043f\u0440\u0438\u044f\u0442\u043d\u0435\u0435.<\/p>\n<p>  <\/p>\n<p>\u0415\u0433\u043e \u0436\u0435 \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u0430\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u043e\u0441\u0442\u044c: \u043a\u0430\u043a \u0432 \u043f\u043b\u0430\u043d\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0442\u0430\u043a \u0438 \u043f\u0440\u0435\u0434\u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043a \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u0438 \u2014 \u0434\u0430\u0451\u0442 \u0432\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c Airflow \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432 \u043b\u044e\u0431\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438: \u0445\u043e\u0442\u044c \u0432 \u043f\u043e\u043b\u043d\u043e\u043c \u0446\u0438\u043a\u043b\u0435 \u0441\u0431\u043e\u0440\u0430, \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0445\u043e\u0442\u044c \u0432 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0440\u0430\u043a\u0435\u0442 (\u043d\u0430 \u041c\u0430\u0440\u0441, \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435). <\/p>\n<p>  <\/p>\n<h2 id=\"chast-zaklyuchitelnaya-spravochno-informacionnaya\">\u0427\u0430\u0441\u0442\u044c \u0437\u0430\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f, \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u043e-\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f<\/h2>\n<p>  <\/p>\n<h3 id=\"grabli-kotorye-my-sobrali-za-vas\">\u0413\u0440\u0430\u0431\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0441\u043e\u0431\u0440\u0430\u043b\u0438 \u0437\u0430 \u0432\u0430\u0441<\/h3>\n<p>  <\/p>\n<ul>\n<li>\n<p><strong><code>start_date<\/code><\/strong>. \u0414\u0430, \u044d\u0442\u043e \u0443\u0436\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u043c\u0430\u0441\u0438\u043a. \u0427\u0435\u0440\u0435\u0437 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0434\u0430\u0433\u0430 <code>start_date<\/code> \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u0432\u0441\u0435. \u041a\u0440\u0430\u0442\u043a\u043e, \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0432 <code>start_date<\/code> \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0434\u0430\u0442\u0443, \u0430 \u0432 <code>schedule_interval<\/code> \u2014 \u043e\u0434\u0438\u043d \u0434\u0435\u043d\u044c, \u0442\u043e DAG \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0441\u044f \u0437\u0430\u0432\u0442\u0440\u0430 \u043d\u0435 \u0440\u0430\u043d\u044c\u0448\u0435. <\/p>\n<p>  <\/p>\n<pre><code class=\"python\">start_date = datetime(2020, 7, 7, 0, 1, 2)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0421 \u043d\u0438\u043c \u0436\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0438 \u0435\u0449\u0435 \u043e\u0434\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f: <code>Task is missing the start_date parameter<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u044b \u0437\u0430\u0431\u044b\u043b\u0438 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u043a \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0443 \u0434\u0430\u0433.<\/p>\n<p>  <\/li>\n<li>\n<p><strong>\u0412\u0441\u0451 \u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435.<\/strong> \u0414\u0430, \u0438 \u0431\u0430\u0437\u044b (\u0441\u0430\u043c\u043e\u0433\u043e Airflow \u0438 \u043d\u0430\u0448\u0435\u0439 \u043e\u0431\u043c\u0430\u0437\u043a\u0438), \u0438 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440, \u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a, \u0438 \u0432\u043e\u0440\u043a\u0435\u0440\u044b. \u0418 \u043e\u043d\u043e \u0434\u0430\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e. \u041d\u043e \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0434\u0430\u0447 \u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0440\u043e\u0441\u043b\u043e, \u0438 \u043a\u043e\u0433\u0434\u0430 PostgreSQL \u0441\u0442\u0430\u043b \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043f\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u0437\u0430 20 \u0441 \u0432\u043c\u0435\u0441\u0442\u043e 5 \u043c\u0441, \u043c\u044b \u0435\u0433\u043e \u0432\u0437\u044f\u043b\u0438 \u0438 \u0443\u043d\u0435\u0441\u043b\u0438.<\/p>\n<p>  <\/li>\n<li>\n<p><strong>LocalExecutor.<\/strong> \u0414\u0430, \u043c\u044b \u0441\u0438\u0434\u0438\u043c \u043d\u0430 \u043d\u0451\u043c \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440, \u0438 \u043c\u044b \u0443\u0436\u0435 \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u043a\u0440\u0430\u044e \u043f\u0440\u043e\u043f\u0430\u0441\u0442\u0438. LocalExecutor\u2019\u0430 \u043d\u0430\u043c \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u0445\u0432\u0430\u0442\u0430\u043b\u043e, \u043d\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0440\u0438\u0448\u043b\u0430 \u043f\u043e\u0440\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c\u0441\u044f \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043e\u0434\u043d\u0438\u043c \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u043c, \u0438 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u043d\u0430\u043f\u0440\u044f\u0447\u044c\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0435\u0445\u0430\u0442\u044c \u043d\u0430 CeleryExecutor. \u0410 \u0432\u0432\u0438\u0434\u0443 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0441 \u043d\u0438\u043c \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0438 \u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u043e\u0439, \u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Celery \u0434\u0430\u0436\u0435 \u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u00ab\u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043f\u043e\u0439\u0434\u0435\u0442 \u0432 \u043f\u0440\u043e\u0434, \u0447\u0435\u0441\u0441\u043b\u043e\u0432\u043e!\u00bb<\/p>\n<p>  <\/li>\n<li>\n<p>\u041d\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <strong>\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 \u0441\u0440\u0435\u0434\u0441\u0442\u0432<\/strong>:<\/p>\n<p>  <\/p>\n<ul>\n<li><strong>Connections<\/strong> \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, <\/li>\n<li><strong>SLA Misses<\/strong> \u0434\u043b\u044f \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u0442\u0430\u0441\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0432\u043e\u0432\u0440\u0435\u043c\u044f,<\/li>\n<li><strong>XCom<\/strong> \u0434\u043b\u044f \u043e\u0431\u043c\u0435\u043d\u0430 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 (\u044f \u0441\u043a\u0430\u0437\u0430\u043b <strong>\u043c\u0435\u0442\u0430<\/strong>\u0434\u0430\u043d\u043d\u044b\u043c\u0438!) \u043c\u0435\u0436\u0434\u0443 \u0442\u0430\u0441\u043a\u0430\u043c\u0438 \u0434\u0430\u0433\u0430.<\/li>\n<\/ul>\n<p>  <\/li>\n<li>\n<p>\u0417\u043b\u043e\u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u043e\u0439. \u041d\u0443 \u0447\u0442\u043e \u0442\u0443\u0442 \u0441\u043a\u0430\u0437\u0430\u0442\u044c? \u0411\u044b\u043b\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u043e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u044f \u043d\u0430 \u0432\u0441\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u044b \u0443\u043f\u0430\u0432\u0448\u0438\u0445 \u0442\u0430\u0441\u043a\u043e\u0432. \u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u043c\u043e\u0451\u043c \u0440\u0430\u0431\u043e\u0447\u0435\u043c Gmail &gt;90k \u043f\u0438\u0441\u0435\u043c \u043e\u0442 Airflow, \u0438 \u0432\u0435\u0431-\u043c\u043e\u0440\u0434\u0430 \u043f\u043e\u0447\u0442\u044b \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u0440\u0430\u0442\u044c \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0447\u0435\u043c \u043f\u043e 100 \u0448\u0442\u0443\u043a \u0437\u0430 \u0440\u0430\u0437.<\/p>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<blockquote><p>\u0411\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0445 \u043a\u0430\u043c\u043d\u0435\u0439: <a href=\"https:\/\/cwiki.apache.org\/confluence\/display\/AIRFLOW\/Common+Pitfalls\" rel=\"nofollow\">Apache Airflow Pitfails<\/a><\/p><\/blockquote>\n<p>  <\/p>\n<h3 id=\"sredstva-eschyo-bolshey-avtomatizacii\">\u0421\u0440\u0435\u0434\u0441\u0442\u0432\u0430 \u0435\u0449\u0451 \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043c \u0435\u0449\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0433\u043e\u043b\u043e\u0432\u043e\u0439, \u0430 \u043d\u0435 \u0440\u0443\u043a\u0430\u043c\u0438, Airflow \u0437\u0430\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u0430 \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u043e\u0442 \u0447\u0442\u043e:<\/p>\n<p>  <\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/airflow.apache.org\/docs\/stable\/rest-api-ref.html\" rel=\"nofollow\">REST API<\/a> \u2014 \u043e\u043d \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u0438\u043c\u0435\u0435\u0442 \u0441\u0442\u0430\u0442\u0443\u0441 Experimental, \u0447\u0442\u043e \u043d\u0435 \u043c\u0435\u0448\u0430\u0435\u0442 \u0435\u043c\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u0421 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u0430\u0433\u0430\u0445 \u0438 \u0442\u0430\u0441\u043a\u0430\u0445, \u043d\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\/\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u0430\u0433, \u0441\u043e\u0437\u0434\u0430\u0442\u044c DAG Run \u0438\u043b\u0438 \u043f\u0443\u043b.<\/p>\n<p>  <\/li>\n<li>\n<p><a href=\"https:\/\/airflow.apache.org\/docs\/stable\/cli-ref.html\" rel=\"nofollow\">CLI<\/a> \u2014 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043c\u043d\u043e\u0433\u0438\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u044b \u0432 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 WebUI, \u0430 \u0432\u043e\u043e\u0431\u0449\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>backfill<\/code> \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432 \u0442\u0430\u0441\u043a\u043e\u0432.<br \/>  \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438\u0448\u043b\u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438, \u0433\u043e\u0432\u043e\u0440\u044f\u0442: \u00ab\u0410 \u0443 \u0432\u0430\u0441, \u0442\u043e\u0432\u0430\u0440\u0438\u0449, \u0435\u0440\u0443\u043d\u0434\u0430 \u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 1 \u043f\u043e 13 \u044f\u043d\u0432\u0430\u0440\u044f! \u0427\u0438\u043d\u0438-\u0447\u0438\u043d\u0438-\u0447\u0438\u043d\u0438-\u0447\u0438\u043d\u0438!\u00bb. \u0410 \u0442\u044b \u0442\u0430\u043a\u043e\u0439 \u0445\u043e\u0431\u0430: <br \/> \n<pre><code class=\"bash\">airflow backfill -s '2020-01-01' -e '2020-01-13' orders<\/code><\/pre>\n<\/li>\n<li>\u041e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0431\u0430\u0437\u044b: <code>initdb<\/code>, <code>resetdb<\/code>, <code>upgradedb<\/code>, <code>checkdb<\/code>.<\/li>\n<li><code>run<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043d\u0441\u0442\u0430\u043d\u0441 \u0442\u0430\u0441\u043a\u0430, \u0434\u0430 \u0435\u0449\u0435 \u0438 \u0437\u0430\u0431\u0438\u0442\u044c \u043d\u0430 \u0432\u0441\u0451 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u0411\u043e\u043b\u0435\u0435 \u0442\u043e\u0433\u043e, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e \u0447\u0435\u0440\u0435\u0437 <code>LocalExecutor<\/code>, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 Celery-\u043a\u043b\u0430\u0441\u0442\u0435\u0440.<\/li>\n<li>\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0434\u0435\u043b\u0430\u0435\u0442 <code>test<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0449\u0435 \u0438 \u0432 \u0431\u0430\u0437 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0438\u0448\u0435\u0442.<\/li>\n<li><code>connections<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043c\u0430\u0441\u0441\u043e\u0432\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u0448\u0435\u043b\u043b\u0430.<\/li>\n<\/ul>\n<p>  <\/li>\n<li>\n<p><a href=\"https:\/\/airflow.apache.org\/docs\/stable\/_api\/index.html\" rel=\"nofollow\">Python API<\/a> \u2014 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0445\u0430\u0440\u0434\u043a\u043e\u0440\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0430 \u043d\u0435 \u043a\u043e\u043f\u043e\u0448\u0435\u043d\u0438\u044f \u0432 \u043d\u0451\u043c \u0440\u0443\u0447\u0451\u043d\u043a\u0430\u043c\u0438. \u041d\u043e \u043a\u0442\u043e \u0436 \u043d\u0430\u043c \u043f\u043e\u043c\u0435\u0448\u0430\u0435\u0442 \u043f\u043e\u0439\u0442\u0438 \u0432 <code>\/home\/airflow\/dags<\/code>, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <code>ipython<\/code> \u0438 \u043d\u0430\u0447\u0430\u0442\u044c \u0431\u0435\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u0447\u0430\u0442\u044c? \u041c\u043e\u0436\u043d\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0442\u0430\u043a\u043e\u043c \u043a\u043e\u0434\u043e\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"python\">from airflow import settings from airflow.models import Connection  fields = 'conn_id conn_type host port schema login password extra'.split()  session = settings.Session() for conn in session.query(Connection).order_by(Connection.conn_id):   d = {field: getattr(conn, field) for field in fields}   print(conn.conn_id, '=', d)<\/code><\/pre>\n<p>  <\/li>\n<li>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0431\u0430\u0437\u0435 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 Airflow. \u041f\u0438\u0441\u0430\u0442\u044c \u0432 \u043d\u0435\u0451 \u044f \u043d\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e, \u0430 \u0432\u043e\u0442 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0442\u0430\u0441\u043a\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043c\u0435\u0442\u0440\u0438\u043a \u043c\u043e\u0436\u043d\u043e \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0438 \u043f\u0440\u043e\u0449\u0435, \u0447\u0435\u043c \u0447\u0435\u0440\u0435\u0437 \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 API. <\/p>\n<p>  <\/p>\n<p>\u0421\u043a\u0430\u0436\u0435\u043c, \u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u0442\u0430\u0441\u043a\u0438 \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b, \u0430 \u043c\u043e\u0433\u0443\u0442 \u0438\u043d\u043e\u0433\u0434\u0430 \u043f\u0430\u0434\u0430\u0442\u044c \u0438 \u044d\u0442\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e. \u041d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0432\u0430\u043b\u043e\u0432 \u2014 \u044d\u0442\u043e \u0443\u0436\u0435 \u043f\u043e\u0434\u043e\u0437\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0438 \u043d\u0430\u0434\u043e \u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c. <\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u041e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u043e, SQL!<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"sql\">WITH last_executions AS ( SELECT     task_id,     dag_id,     execution_date,     state,         row_number()         OVER (             PARTITION BY task_id, dag_id             ORDER BY execution_date DESC) AS rn FROM public.task_instance WHERE     execution_date &gt; now() - INTERVAL '2' DAY ), failed AS (     SELECT         task_id,         dag_id,         execution_date,         state,         CASE WHEN rn = row_number() OVER (             PARTITION BY task_id, dag_id             ORDER BY execution_date DESC)                  THEN TRUE END AS last_fail_seq     FROM last_executions     WHERE         state IN ('failed', 'up_for_retry') ) SELECT     task_id,     dag_id,     count(last_fail_seq)                       AS unsuccessful,     count(CASE WHEN last_fail_seq         AND state = 'failed' THEN 1 END)       AS failed,     count(CASE WHEN last_fail_seq         AND state = 'up_for_retry' THEN 1 END) AS up_for_retry FROM failed GROUP BY     task_id,     dag_id HAVING     count(last_fail_seq) &gt; 0<\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<h2 id=\"ssylki\">\u0421\u0441\u044b\u043b\u043a\u0438<\/h2>\n<p>  <\/p>\n<p>\u041d\u0443 \u0438 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e <del>\u043f\u0435\u0440\u0432\u044b\u0435 \u0434\u0435\u0441\u044f\u0442\u044c \u0441\u0441\u044b\u043b\u043e\u043a \u0438\u0437 \u0432\u044b\u0434\u0430\u0447\u0438 \u0433\u0443\u0433\u043b\u0430<\/del> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u0430\u043f\u043a\u0438 Airflow \u0438\u0437 \u043c\u043e\u0438\u0445 \u0437\u0430\u043a\u043b\u0430\u0434\u043e\u043a.<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/airflow.apache.org\/docs\/stable\/\" rel=\"nofollow\">Apache Airflow Documentation<\/a> \u2014 \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043d\u0430\u0434\u043e \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u043e\u0444. \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u043d\u043e \u043a\u0442\u043e \u0436\u0435 \u0447\u0438\u0442\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438?<\/li>\n<li><a href=\"https:\/\/airflow.apache.org\/docs\/stable\/best-practices.html\" rel=\"nofollow\">Best Practices<\/a> \u2014 \u043d\u0443 \u0445\u043e\u0442\u044f \u0431\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438 \u043e\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0439\u0442\u0435.<\/li>\n<li><a href=\"https:\/\/www.astronomer.io\/guides\/airflow-ui\/\" rel=\"nofollow\">The Airflow UI<\/a> \u2014 \u0441\u0430\u043c\u043e\u0435 \u043d\u0430\u0447\u0430\u043b\u043e: \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0445<\/li>\n<li><a href=\"https:\/\/medium.com\/@dustinstansbury\/understanding-apache-airflows-key-concepts-a96efed52b1a\" rel=\"nofollow\">Understanding Apache Airflow\u2019s key concepts<\/a> \u2014 \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u044b \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u043e\u043d\u044f\u0442\u0438\u044f, \u0435\u0441\u043b\u0438 (\u0432\u0434\u0440\u0443\u0433!) \u0432\u044b \u0447\u0442\u043e-\u0442\u043e \u043d\u0435 \u043f\u043e\u043d\u044f\u043b\u0438 \u0443 \u043c\u0435\u043d\u044f.<\/li>\n<li><a href=\"https:\/\/stlong0521.github.io\/20161023%20-%20Airflow.html\" rel=\"nofollow\">Tianlong&#8217;s Blog \u2014 A Guide On How To Build An Airflow Server\/Cluster<\/a> \u2014 \u043a\u0440\u0430\u0442\u043a\u0438\u0439 \u0433\u0430\u0439\u0434 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Airflow-\u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430.<\/li>\n<li><a href=\"https:\/\/eng.lyft.com\/running-apache-airflow-at-lyft-6e53bb8fccff\" rel=\"nofollow\">Running Apache Airflow At Lyft<\/a> \u2014 \u043f\u043e\u0447\u0442\u0438 \u0442\u0430\u043a\u0430\u044f \u0436\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f, \u0440\u0430\u0437\u0432\u0435 \u0447\u0442\u043e \u0444\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043c\u0430 \u043f\u043e\u0431\u043e\u043b\u044c\u0448\u0435, \u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043f\u043e\u043c\u0435\u043d\u044c\u0448\u0435.<\/li>\n<li><a href=\"https:\/\/www.sicara.ai\/blog\/2019-04-08-apache-airflow-celery-workers\" rel=\"nofollow\">How Apache Airflow Distributes Jobs on Celery workers<\/a> \u2014 \u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0432 \u0441\u0432\u044f\u0437\u043a\u0435 \u0441 Celery.<\/li>\n<li><a href=\"https:\/\/www.astronomer.io\/guides\/dag-best-practices\/\" rel=\"nofollow\">DAG Writing Best Practices in Apache Airflow<\/a> \u2014 \u043f\u0440\u043e \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u0442\u0430\u0441\u043a\u043e\u0432, \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043f\u043e ID \u0432\u043c\u0435\u0441\u0442\u043e \u0434\u0430\u0442\u044b, \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u043f\u0440\u043e\u0447\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0432\u0435\u0449\u0438.<\/li>\n<li><a href=\"https:\/\/www.astronomer.io\/guides\/managing-dependencies\/\" rel=\"nofollow\">Managing Dependencies in Apache Airflow<\/a> \u2014 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0442\u0430\u0441\u043a\u043e\u0432 \u0438 Trigger Rule, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b \u043b\u0438\u0448\u044c \u0432\u0441\u043a\u043e\u043b\u044c\u0437\u044c.<\/li>\n<li><a href=\"https:\/\/medium.com\/@hafizbadrie\/airflow-when-your-dag-is-far-behind-the-schedule-ea11bf02e44c\" rel=\"nofollow\">Airflow: When Your DAG is Far Behind The Schedule<\/a> \u2014 \u043a\u0430\u043a \u043f\u0440\u0435\u043e\u0434\u043e\u043b\u0435\u0432\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u00ab\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043a\u0430\u043a \u0437\u0430\u0434\u0443\u043c\u0430\u043d\u043e\u00bb \u0443 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430, \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u044b \u0442\u0430\u0441\u043a\u043e\u0432.<\/li>\n<li><a href=\"https:\/\/www.astronomer.io\/guides\/airflow-queries\/\" rel=\"nofollow\">Useful SQL queries for Apache Airflow<\/a> \u2014 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u043c Airflow.<\/li>\n<li><a href=\"https:\/\/technofob.com\/2019\/05\/30\/get-started-developing-workflows-with-apache-airflow\/\" rel=\"nofollow\">Get started developing workflows with Apache Airflow<\/a> \u2014 \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0440\u0430\u0437\u0434\u0435\u043b \u043f\u0440\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430.<\/li>\n<li><a href=\"http:\/\/bytepawn.com\/fetchr-airflow.html\" rel=\"nofollow\">Building the Fetchr Data Science Infra on AWS with Presto and Airflow<\/a> \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f \u043a\u043e\u0440\u043e\u0442\u043a\u0430\u044f \u0437\u0430\u043c\u0435\u0442\u043a\u0430 \u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043d\u0430 AWS \u0434\u043b\u044f Data Science.<\/li>\n<li><a href=\"https:\/\/www.astronomer.io\/blog\/7-common-errors-to-check-when-debugging-airflow-dag\/\" rel=\"nofollow\">7 Common Errors to Check when Debugging Airflow DAGs<\/a> \u2014 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 (\u043a\u043e\u0433\u0434\u0430 \u043a\u043e\u0435-\u043a\u0442\u043e \u0432\u0441\u0451-\u0442\u0430\u043a\u0438 \u043d\u0435 \u0447\u0438\u0442\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0439).<\/li>\n<li><a href=\"https:\/\/stackoverflow.com\/questions\/45280650\/store-and-access-password-using-apache-airflow\" rel=\"nofollow\">Store and access password using Apache Airflow<\/a> \u2014 \u0443\u043b\u044b\u0431\u043d\u0438\u0442\u0435\u0441\u044c, \u043a\u0430\u043a \u043b\u044e\u0434\u0438 \u043a\u043e\u0441\u0442\u044b\u043b\u044f\u0442 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u0435\u0439, \u0445\u043e\u0442\u044f \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Connections.<\/li>\n<li><a href=\"https:\/\/godatadriven.com\/blog\/the-zen-of-python-and-apache-airflow\/\" rel=\"nofollow\">The Zen of Python and Apache Airflow<\/a> \u2014 \u043d\u0435\u044f\u0432\u043d\u044b\u0439 \u043f\u0440\u043e\u0431\u0440\u043e\u0441 DAG, \u0437\u0430\u0431\u0440\u043e\u0441 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0441\u043d\u043e\u0432\u0430 \u043f\u0440\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0430 \u0435\u0449\u0435 \u043f\u0440\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u043a \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432 \u0442\u0430\u0441\u043a\u043e\u0432.<\/li>\n<li><a href=\"https:\/\/medium.com\/datareply\/airflow-lesser-known-tips-tricks-and-best-practises-cf4d4a90f8f\" rel=\"nofollow\">Airflow: Lesser Known Tips, Tricks, and Best Practises<\/a> \u2014 \u043e\u0431 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 <code>default arguments<\/code> \u0438 <code>params<\/code> \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u0445, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e Variables \u0438 Connections.<\/li>\n<li><a href=\"https:\/\/www.astronomer.io\/blog\/profiling-the-airflow-scheduler\/\" rel=\"nofollow\">Profiling the Airflow Scheduler<\/a> \u2014 \u0440\u0430\u0441\u0441\u043a\u0430\u0437 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0433\u043e\u0442\u043e\u0432\u044f\u0442 \u043a Airflow 2.0.<\/li>\n<li><a href=\"https:\/\/medium.com\/@xnuinside\/quick-tutorial-apache-airflow-with-3-celery-workers-in-docker-composer-9f2f3b445e4\" rel=\"nofollow\">Apache Airflow with 3 Celery workers in docker-compose<\/a> \u2014 \u043d\u0435\u043c\u043d\u043e\u0436\u043a\u043e \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u0440\u043e \u0434\u0435\u043f\u043b\u043e\u0439 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430 \u0432 <code>docker-compose<\/code>.<\/li>\n<li><a href=\"https:\/\/livebook.manning.com\/book\/data-pipelines-with-apache-airflow\/chapter-4\/v-4\/36\" rel=\"nofollow\">4 Templating Tasks Using the Airflow Context<\/a> \u2014 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0442\u0430\u0441\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432 \u0438 \u043f\u0440\u043e\u0431\u0440\u043e\u0441\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430.<\/li>\n<li><a href=\"https:\/\/www.astronomer.io\/guides\/error-notifications-in-airflow\/\" rel=\"nofollow\">Error Notifications in Airflow<\/a> \u2014 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u044f \u043f\u043e\u0447\u0442\u043e\u0439 \u0438 Slack.<\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/company\/mailru\/blog\/344398\/\">Airflow Workshop: \u0441\u043b\u043e\u0436\u043d\u044b\u0435 DAG\u2019\u0438 \u0431\u0435\u0437 \u043a\u043e\u0441\u0442\u044b\u043b\u0435\u0439<\/a> \u2014 \u0412\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0430\u0441\u043a\u043e\u0432, \u043c\u0430\u043a\u0440\u043e\u0441\u044b \u0438 XCom.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0418 \u0441\u0441\u044b\u043b\u043a\u0438, \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/airflow.apache.org\/docs\/stable\/macros-ref.html\" rel=\"nofollow\">Macros reference<\/a> \u2014 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u0445 \u043f\u043b\u0435\u0439\u0441\u0445\u043e\u043b\u0434\u0435\u0440\u044b.<\/li>\n<li><a href=\"https:\/\/cwiki.apache.org\/confluence\/display\/AIRFLOW\/Common+Pitfalls\" rel=\"nofollow\">Common Pitfalls \u2014 Airflow<\/a> \u2014 \u0420\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0434\u0430\u0433\u043e\u0432.<\/li>\n<li><a href=\"https:\/\/github.com\/puckel\/docker-airflow\" rel=\"nofollow\">puckel\/docker-airflow: Docker Apache Airflow<\/a> \u2014 <code>docker-compose<\/code> \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432, \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e.<\/li>\n<li><a href=\"https:\/\/github.com\/python-telegram-bot\/python-telegram-bot\" rel=\"nofollow\">python-telegram-bot\/python-telegram-bot: We have made you a wrapper you can&#8217;t refuse<\/a> \u2014 Python-\u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u0434\u043b\u044f Telegram REST API.<\/li>\n<\/ul>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/512386\/\"> https:\/\/habr.com\/ru\/post\/512386\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/512386\/\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u044f \u0414\u043c\u0438\u0442\u0440\u0438\u0439 \u041b\u043e\u0433\u0432\u0438\u043d\u0435\u043d\u043a\u043e \u2014 Data Engineer \u043e\u0442\u0434\u0435\u043b\u0430 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438 \u0433\u0440\u0443\u043f\u043f\u044b \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439 \u00ab\u0412\u0435\u0437\u0451\u0442\u00bb.<\/p>\n<p>  <\/p>\n<p>\u042f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c \u043e \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u2014 Apache Airflow. \u041d\u043e Airflow \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u0435\u043d \u0438 \u043c\u043d\u043e\u0433\u043e\u0433\u0440\u0430\u043d\u0435\u043d, \u0447\u0442\u043e \u0432\u0430\u043c \u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u0438\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c\u0441\u044f \u043a \u043d\u0435\u043c\u0443 \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0435\u0441\u044c \u043f\u043e\u0442\u043e\u043a\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0438\u043c\u0435\u0435\u0442\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0438 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0438\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0418 \u0434\u0430, \u044f \u0431\u0443\u0434\u0443 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c, \u043d\u043e \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c: \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435 \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u0432 \u0438 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0439.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/lf\/59\/ve\/lf59vefopsudfwc2ynlpomu_ntq.png\"><br \/>  <em>\u0427\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u0438\u0434\u0438\u0448\u044c, \u043a\u043e\u0433\u0434\u0430 \u0433\u0443\u0433\u043b\u0438\u0448\u044c \u0441\u043b\u043e\u0432\u043e Airflow \/ Wikimedia Commons<\/em><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-307527","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307527","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=307527"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307527\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=307527"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=307527"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=307527"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}