Книга «Terraform: инфраструктура на уровне кода»

    image Привет, Хаброжители! Книга предназначена для всех, кто отвечает за уже написанный код. Это относится к сисадминам, специалистам по эксплуатации, релиз-, SR-, DevOps-инженерам, разработчикам инфраструктуры, разработчикам полного цикла, руководителям инженерной группы и техническим директорам. Какой бы ни была ваша должность, если вы занимаетесь инфраструктурой, развертываете код, конфигурируете серверы, масштабируете кластеры, выполняете резервное копирование данных, мониторите приложения и отвечаете на вызовы в три часа ночи, эта книга для вас.

    В совокупности эти обязанности обычно называют операционной деятельностью (или системным администрированием). Раньше часто встречались разработчики, которые умели писать код, но не разбирались в системном администрировании; точно так же нередко попадались сисадмины без умения писать код. Когда-то такое разделение было приемлемым, но в современном мире, который уже нельзя представить без облачных вычислений и движения DevOps, практически любому разработчику необходимы навыки администрирования, а любой сисадмин должен уметь программировать.

    Вы не только научитесь управлять инфраструктурой в виде кода, используя Terraform, но и узнаете, как это вписывается в общую концепцию DevOps. Вот несколько вопросов, на которые вы сможете ответить по прочтении этой книги.

    • Зачем вообще использовать IaC?
    • Какая разница между управлением конфигурацией, оркестрацией, инициализацией ресурсов и шаблонизацией серверов?
    • Когда следует использовать Terraform, Chef, Ansible, Puppet, Salt, CloudFormation, Docker, Packer или Kubernetes?
    • Как работает система Terraform и как с ее помощью управлять инфраструктурой?
    • Как создавать модули Terraform, подходящие для повторного использования?
    • Как писать код для Terraform, который будет достаточно надежным для практического применения?
    • Как тестировать свой код для Terraform?
    • Как внедрить Terraform в свой процесс автоматического развертывания?
    • Как лучше всего использовать Terraform в командной работе?

    Что нового во втором издании
    Первое издание вышло в 2017 году. В мае 2019-го я готовил второе издание и был очень удивлен тому, как все изменилось за пару лет! Эта книга по своему объему почти в два раза превосходит предыдущую и включает две полностью новые главы. Кроме того, существенно обновлены все оригинальные главы и примеры кода.

    Если вы уже прочитали первое издание и хотите узнать, что изменилось, или вам просто интересно посмотреть, как эволюционировал проект Terraform, вот несколько основных моментов.

    • Четыре крупных обновления Terraform. Когда вышла первая книга, стабильной версией Terraform была 0.8. Спустя четыре крупных обновления Terraform имеет версию 0.12. За это время появились некоторые поразительные новшества, о которых пойдет речь далее. Чтобы обновиться, пользователям придется попотеть!
    • Улучшения в автоматическом тестировании. Существенно эволюционировали методики и инструментарий написания автоматических тестов для кода Terraform. Тестированию посвящена новая глава, седьмая, которая затрагивает такие темы, как модульные, интеграционные и сквозные тесты, внедрение зависимостей, распараллеливание тестов, статический анализ и др.
    • Улучшения в модулях. Инструментарий и методики создания модулей Terraform тоже заметно эволюционировали. В новой, шестой, главе вы найдете руководство по написанию испытанных модулей промышленного уровня с возможностью повторного использования — таких, которым можно доверить благополучие своей компании.
    • Улучшения в рабочем процессе. Глава 8 была полностью переписана согласно тем изменениям, которые произошли в процедуре интеграции Terraform в рабочий процесс команд. Там, помимо прочего, можно найти подробное руководство о том, как провести прикладной и инфраструктурный код через все основные этапы: разработку, тестирование и развертывание в промышленной среде.
    • HCL2. В Terraform 0.12 внутренний язык HCL обновился до HCL2. Это включает в себя поддержку полноценных выражений (чтобы вам не приходилось заворачивать все в ${…}!), развитые ограничители типов, условные выражения с отложенным вычислением, поддержку выражений null, for_each и for, вложенные блоки и др. Все примеры кода в этой книге были адаптированы для HCL2, а новые возможности языка подробно рассматриваются в главах 5 и 6.
    • Переработанные механизмы хранения состояния. В Terraform 0.9 появилась концепция внутренних хранилищ. Это полноценный механизм хранения и разделения состояния Terraform со встроенной поддержкой блокирования. В Terraform 0.9 также были представлены окружения состояния, которые позволяют управлять развертываниями в разных средах; но уже в версии 0.10 им на смену пришли рабочие области. Все эти темы рассматриваются в главе 3.
    • Вынос провайдеров из ядра Terraform. В Terraform 0.10 из ядра был вынесен код для всех провайдеров (то есть код для AWS, GCP, Azure и т. д.). Благодаря этому разработка провайдеров теперь ведется в отдельных репозиториях, в своем собственном темпе и с выпуском независимых версий. Однако теперь придется загружать код провайдера с помощью команды terraform init каждый раз, когда вы начинаете работать с новым модулем. Об этом пойдет речь в главах 2 и 7.
    • Большое количество новых провайдеров. В 2016 году проект Terraform официально поддерживал лишь несколько основных облачных провайдеров (AWS, GCP и Azure). Сейчас же их количество превысило 100, а провайдеров, разрабатываемых сообществом, и того больше1. Благодаря этому вы можете использовать код для работы не только с множеством разных облаков (например, теперь существуют провайдеры для Alicloud, Oracle Cloud Infrastructure, VMware vSphere и др.), но и с другими аспектами окружающего мира, включая системы управления версиями (GitHub, GitLab или BitBucket), хранилища данных (MySQL, PostreSQL или InfluxDB), системы мониторинга и оповещения (включая DataDog, New Relic или Grafana), платформы наподобие Kubernetes, Helm, Heroku, Rundeck или Rightscale и многое другое. Более того, сейчас у каждого провайдера намного лучше покрытие: скажем, провайдер для AWS охватывает большинство сервисов этой платформы, а поддержка новых сервисов часто появляется даже раньше, чем у CloudFormation!
    • Реестр модулей Terraform. В 2017 году компания HashiCorp представила реестр модулей Terraform (registry.terraform.io) — пользовательский интерфейс, который облегчает просмотр и загрузку открытых универсальных модулей Terraform, разрабатываемых сообществом. В 2018 году была добавлена возможность запускать внутри своей организации закрытый реестр. В Terraform 0.11 появился полноценный синтаксис для загрузки модулей из реестра. Подробнее об этом читайте в разделе «Управление версиями» на с. 153.
    • Улучшенная обработка ошибок. В Terraform 0.9 обновилась обработка ошибок состояния: если при записи состояния в удаленное хранилище обнаруживается ошибка, это состояние сохраняется локально, в файле errored.tfstate. В Terraform 0.12 механизм был полностью переработан. Теперь ошибки перехватываются раньше, а сообщения о них стали более понятными и содержат путь к файлу, номер строчки и фрагмент кода.
    • Много других мелких изменений. Было сделано много менее значительных изменений, включая появление локальных переменных (см. раздел «Локальные переменные модулей» на с. 144), новые «аварийные люки» для взаимодействия с внешним миром с помощью скриптов (например, подраздел «Модули вне Terraform» на с. 242), выполнение plan в рамках команды apply (см. раздел «Развертывание одного сервера» на с. 64), исправление циклических проблем с create_before_destroy, значительное улучшение параметра count, которое позволяет ссылаться в нем на источники данных и ресурсы (см. раздел «Циклы» на с. 160), десятки новых встроенных функций, обновленное наследование provider и многое другое.

    Отрывок. Как тестировать код Terraform


    (вопросам автоматического тестирования в книге уделено далее гораздо больше внимания)

    Мир DevOps полон разных страхов: все боятся допустить простой в работе, потерять данные или быть взломанными. При внесении любого изменения вы всегда спрашиваете себя, какие последствия оно будет иметь. Будет ли оно вести себя одинаково во всех окружениях? Вызовет ли оно очередной перебой в работе? И, если это случится, насколько вам придется задержаться на работе на этот раз, чтобы все исправить? По мере роста компании все больше ставится на кон, что делает процесс развертывания еще страшнее и повышает риск ошибок. Многие компании пытаются минимизировать этот риск за счет менее частых развертываний, но в итоге каждое отдельное развертывание становится более крупным и склонным к ошибкам.

    Если вы управляете своей инфраструктурой в виде кода, у вас появляется лучший способ минимизации рисков: тесты. Их цель — дать вам достаточно уверенности для внесения изменений. Ключевым словом здесь является уверенность: никакие тесты не могут гарантировать отсутствие ошибок, поэтому вы скорее имеете дело с вероятностью. Если удастся запечатлеть всю свою инфраструктуру и процессы развертывания в виде кода, вы сможете проверить этот код в тестовом окружении. В случае успеха есть большой шанс того, что этот же код будет работать и в промышленных условиях. В мире страха и неопределенности высокая вероятность и уверенность дорого стоят.

    В этой главе мы пройдемся по процессу тестирования инфраструктурного кода, как ручного, так и автоматического, с акцентом на последний.

    Ручные тесты:

    • основы ручного тестирования;
    • очистка ресурсов после тестов.

    Автоматические тесты:

    • модульные тесты;
    • интеграционные тесты;
    • сквозные тесты;
    • другие подходы к тестированию.

    Ручные тесты


    Размышляя о том, как тестировать код Terraform, полезно провести некоторые параллели с тестированием кода, написанного на языках программирования общего назначения, таких как Ruby. Представьте, что вы пишете простой веб-сервер на Ruby в файле web-server.rb:

    class WebServer < WEBrick::HTTPServlet::AbstractServlet
      def do_GET(request, response)
         case request.path
         when "/"
             response.status = 200
             response['Content-Type'] = 'text/plain'
             response.body = 'Hello, World'
         else
             response.status = 404
             response['Content-Type'] = 'text/plain'
             response.body = 'Not Found'
         end
      end
    end

    Этот код вернет ответ 200 OK с телом Hello, World для URL-адреса /; для любого другого адреса ответ будет 404. Как бы вы протестировали этот код вручную? Обычно для этого добавляют еще немного кода, чтобы запускать веб-сервер локально:

    # Этот код выполняется, только если скрипт был вызван непосредственно
    # из терминала, но не при подключении из другого файла
    if __FILE__ == $0
      # Запускаем сервер локально на порте 8000
      server = WEBrick::HTTPServer.new :Port => 8000
      server.mount '/', WebServer
    
      # Останавливаем сервер нажатием Ctrl+C
      trap 'INT' do server.shutdown end
    
      # Запускаем сервер
      server.start
    end

    Если запустить этот файл в терминале, он загрузит веб-сервер на порте 8000:

    $ ruby web-server.rb
    [2019-05-25 14:11:52] INFO WEBrick 1.3.1
    [2019-05-25 14:11:52] INFO ruby 2.3.7 (2018-03-28) [universal.x86_64-darwin17]
    [2019-05-25 14:11:52] INFO WEBrick::HTTPServer#start: pid=19767 port=8000

    Чтобы проверить работу этого сервера, можно воспользоваться браузером или curl:

    $ curl localhost:8000/
    Hello, World
    
    $ curl localhost:8000/invalid-path
    Not Found

    Теперь представьте, что мы изменили этот код, добавив в него точку входа /api, которая возвращает 201 Created и тело в формате JSON:

    class WebServer < WEBrick::HTTPServlet::AbstractServlet
      def do_GET(request, response)
         case request.path
         when "/"
             response.status = 200
             response['Content-Type'] = 'text/plain'
             response.body = 'Hello, World'
         when "/api"
             response.status = 201
             response['Content-Type'] = 'application/json'
             response.body = '{"foo":"bar"}'
         else
             response.status = 404
             response['Content-Type'] = 'text/plain'
             response.body = 'Not Found'
         end
      end
    end

    Чтобы вручную протестировать этот обновленный код, нажмите Ctrl+C и перезагрузите веб-сервер, запустив скрипт еще раз:

    $ ruby web-server.rb
    [2019-05-25 14:11:52] INFO WEBrick 1.3.1
    [2019-05-25 14:11:52] INFO ruby 2.3.7 (2018-03-28) [universal.x86_64-darwin17]
    [2019-05-25 14:11:52] INFO WEBrick::HTTPServer#start: pid=19767 port=8000
    ^C
    [2019-05-25 14:15:54] INFO going to shutdown ...
    [2019-05-25 14:15:54] INFO WEBrick::HTTPServer#start done.
    
    $ ruby web-server.rb
    [2019-05-25 14:11:52] INFO WEBrick 1.3.1
    [2019-05-25 14:11:52] INFO ruby 2.3.7 (2018-03-28) [universal.x86_64-darwin17]
    [2019-05-25 14:11:52] INFO WEBrick::HTTPServer#start: pid=19767 port=8000

    Для проверки новой версии можно снова воспользоваться командой curl:

    $ curl localhost:8000/api
    {"foo":"bar"}

    Основы ручного тестирования


    Как будет выглядеть подобного рода ручное тестирование в Terraform? Например, из предыдущих глав у вас остался код для развертывания ALB. Вот фрагмент файла modules/networking/alb/main.tf:

    resource "aws_lb" "example" {
       name                     = var.alb_name
       load_balancer_type = "application"
       subnets                  = var.subnet_ids
       security_groups      = [aws_security_group.alb.id]
    }
    
    resource "aws_lb_listener" "http" {
       load_balancer_arn = aws_lb.example.arn
       port                      = local.http_port
       protocol                = "HTTP"
    
       # По умолчанию возвращаем простую страницу 404
       default_action {
          type = "fixed-response"
    
          fixed_response {
            content_type = "text/plain"
            message_body = "404: page not found"
            status_code = 404
          }
        }
    }
    
    resource "aws_security_group" "alb" {
       name = var.alb_name
    }
    
    # (...)

    Если сравнить этот листинг с кодом на Ruby, можно заметить одно довольно очевидное отличие: AWS ALB, целевые группы, прослушиватели, группы безопасности и любые другие ресурсы нельзя развертывать на собственном компьютере.

    Из этого следует ключевой вывод о тестировании № 1: тестирование кода Terraform не может проходить локально.

    Это относится не только к Terraform, но и к большинству средств IaC. Единственный практичный способ выполнять ручное тестирование в Terraform — развернуть код в реальном окружении (то есть в AWS). Иными словами, самостоятельный запуск команд terraform apply и terraform destroy, которым вы занимались, читая книгу, — это и есть ручное тестирование в Terraform.

    Это одна из причин, почему так важно иметь в папке examples каждого модуля простые в развертывании примеры (см. главу 6). Чтобы протестировать модуль alb, проще всего воспользоваться демонстрационным кодом, который вы создали в examples/alb:

    provider "aws" {
       region = "us-east-2"
    
       # Разрешаем любую версию провайдера AWS вида 2.x
       version = "~> 2.0"
    }
    
    module "alb" {
        source = "../../modules/networking/alb"
    
        alb_name = "terraform-up-and-running"
        subnet_ids = data.aws_subnet_ids.default.ids
    }

    Чтобы развернуть этот пример, нужно выполнить команду terraform apply, как вы это неоднократно делали:

    $ terraform apply
    
    (...)
    
    Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
    
    Outputs:
    
    alb_dns_name = hello-world-stage-477699288.us-east-2.elb.amazonaws.com

    В конце развертывания можно использовать такой инструмент, как curl, чтобы, к примеру, убедиться, что по умолчанию ALB возвращает 404:

    $ curl \
       -s \
       -o /dev/null \
       -w "%{http_code}" \
    hello-world-stage-477699288.us-east-2.elb.amazonaws.com
    
    404

    Проверка инфраструктуры

    Наша инфраструктура включает в себя балансировщик нагрузки, работающий по HTTP, поэтому, чтобы убедиться в ее работоспособности, мы используем curl и HTTP-запросы. Другие типы инфраструктуры могут потребовать иные методы проверки. Например, если ваш инфраструктурный код развертывает базу данных MySQL, для его тестирования придется использовать клиент MySQL. Если ваш инфраструктурный код развертывает VPN-сервер, для его тестирования понадобится клиент VPN. Если ваш инфраструктурный код развертывает сервер, который вообще не прослушивает никаких запросов, вам придется зайти на сервер по SSH и выполнить локально кое-какие команды. Этот список можно продолжить. Базовую структуру тестирования, описанную в этой главе, можно применить к инфраструктуре любого вида. Однако этапы проверки будут зависеть от того, что именно вы проверяете.

    Напомню: ALB возвращает 404 ввиду отсутствия в конфигурации других правил прослушивателя, а действие по умолчанию в модуле alb имеет ответ 404:

    resource "aws_lb_listener" "http" {
       load_balancer_arn = aws_lb.example.arn
       port                      = local.http_port
       protocol                = "HTTP"
    
       # По умолчанию возвращаем простую страницу 404
       default_action {
          type = "fixed-response"
    
          fixed_response {
           content_type = "text/plain"
           message_body = "404: page not found"
           status_code = 404
          }
       }
    }

    Итак, вы уже умеете запускать и тестировать свой код. Теперь можно приступить к внесению изменений. Каждый раз, когда вы что-то меняете (чтобы, например, действие по умолчанию возвращало 401), вам нужно использовать команду terraform apply, чтобы развернуть новый код:

    $ terraform apply
    
    (...)
    
    Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
    
    Outputs:
    
    alb_dns_name = hello-world-stage-477699288.us-east-2.elb.amazonaws.com

    Чтобы проверить новую версию, можно заново запустить curl:

    $ curl \
       -s \
       -o /dev/null \
       -w "%{http_code}" \
       hello-world-stage-477699288.us-east-2.elb.amazonaws.com
    401

    Когда закончите, выполните команду terraform destroy, чтобы удалить ресурсы:

    $ terraform destroy
    
    (...)
    
    Apply complete! Resources: 0 added, 0 changed, 5 destroyed.

    Иными словами, при работе с Terraform каждому разработчику нужны хорошие примеры кода для тестирования и настоящее окружение для разработки (вроде учетной записи AWS), которое служит эквивалентом локального компьютера и используется для выполнения тестов. В процессе ручного тестирования вам, скорее всего, придется создавать и удалять большое количество компонентов инфраструктуры, и это может привести к множеству ошибок. В связи с этим окружение должно быть полностью изолировано от более стабильных сред, предназначенных для финального тестирования и в особенности для промышленного применения.

    Учитывая вышесказанное, настоятельно рекомендую каждой команде разработчиков подготовить изолированную среду, в которой вы сможете создавать и удалять любую инфраструктуру без последствий. Чтобы минимизировать вероятность конфликтов между разными разработчиками (представьте, что два разработчика пытаются создать балансировщик нагрузки с одним и тем же именем), идеальным решением будет выделить каждому члену команды отдельную, полностью изолированную среду. Например, если вы используете Terraform в сочетании с AWS, каждый разработчик в идеале должен иметь собственную учетную запись, в которой он сможет тестировать все, что ему захочется.

    Очистка ресурсов после тестов


    Наличие множества изолированных окружений необходимо для высокой продуктивности разработчиков, но, если не проявлять осторожность, у вас может накопиться много лишних ресурсов, которые захламят все ваши среды и будут стоить вам круглую сумму.
    Чтобы держать расходы под контролем, регулярно чистите свои изолированные среды. Это ключевой вывод о тестировании № 2.

    Вам как минимум следует создать в команде такую культуру, когда после окончания тестирования разработчики удаляют все, что они развернули, с помощью команды terraform destroy. Возможно, удастся найти инструменты для очистки лишних или старых ресурсов, которые можно запускать на регулярной основе (скажем, с помощью cron). Вот некоторые примеры для разных сред развертывания.

    • cloud-nuke (http://bit.ly/2OIgM9r). Это инструмент с открытым исходным кодом, который может удалить все ресурсы в вашей облачной среде. Сейчас он поддерживает целый ряд сервисов в AWS (вроде Amazon EC2 Instances, ASG, ELB и т. д.). Поддержка других ресурсов и облаков (Google Cloud, Azure) ожидается в будущем. Его ключевая особенность — возможность удаления всех ресурсов, начиная с определенного возраста. Например, cloud-nuke часто запускают ежедневно с помощью cron, чтобы удалить в каждой изолированной среде все ресурсы старше двух дней. При этом предполагается, что любая инфраструктура, которую разработчик подготовил для ручного тестирования, становится ненужной по прошествии пары суток:

      $ cloud-nuke aws --older-than 48h
    • Janitor Monkey (http://bit.ly/2M4GoLB). Это инструмент с открытым исходным кодом, чистящий ресурсы AWS по графику, который можно настроить (по умолчанию — раз в неделю). Он поддерживает гибкие правила, которые определяют, подлежит ли ресурс удалению, и даже может отправить владельцу ресурса уведомление за несколько дней до очистки. Это часть проекта Netflix Simian Army, в который также входит инструмент Chaos Monkey для проверки устойчивости приложений. Имейте в виду, что у проекта Simian Army больше нет активной поддержки, но разные его части подхватываются другими командами: например, на смену Janitor Monkey пришел инструмент Swabbie (http://bit.ly/2OLrOLb).
    • aws-nuke (http://bit.ly/2ZB8lOe). Это инструмент с открытым исходным кодом для удаления всего содержимого учетной записи AWS. Учетные записи и ресурсы, подлежащие удалению, указываются в конфигурационном файле в формате YAML:

      # Регионы для удаления
      regions:
      - us-east-2
      
      # Учетные записи для удаления
      accounts:
         "111111111111": {}
      
      # Удалять только эти ресурсы
      resource-types:
         targets:
         - S3Object
         - S3Bucket
         - IAMRole

      Запускается aws-nuke следующим образом:

      $ aws-nuke -5c config.yml

    Автоматические тесты


    Концепция автоматического тестирования состоит в том, что для проверки поведения настоящего кода пишутся тесты. В главе 8 вы узнаете, что с помощью CI-сервера эти тесты можно запускать после каждой отдельной фиксации. Если они не будут пройдены, фиксацию сразу же можно отменить или исправить. Таким образом, ваш код всегда будет в рабочем состоянии.

    Существует три типа автоматических тестов.

    • Модульные тесты. Проверяют функциональность одного небольшого фрагмента кода — модуля. Его определение может варьироваться, но в языках программирования общего назначения под ним понимают отдельную функцию или класс. Внешние зависимости (например, базы данных, веб-сервисы и даже файловая система) заменяются тестовыми mock-объектами. Это позволяет полностью контролировать их поведение (например, mock-объект базы данных может возвращать ответ, прописанный вручную) и убедиться в том, что ваш код справляется с множеством разных сценариев.
    • Интеграционные тесты. Проверяют корректность совместной работы нескольких модулей. В языках программирования общего назначения интеграционный тест состоит из кода, который позволяет убедиться в корректном взаимодействии нескольких функций или классов. Реальные зависимости используются вперемешку с mock-объектами: например, если вы тестируете участок приложения, который обращается к базе данных, стоит тестировать его с настоящей БД, а другие зависимости, скажем, систему аутентификации, заменить mock-объектами.
    • Сквозные тесты. Подразумевают запуск всей вашей архитектуры (например, приложений, хранилищ данных, балансировщиков нагрузки) и проверку работы системы как единого целого. Обычно эти тесты выполняются как бы от имени конечного пользователя: допустим, Selenium может автоматизировать взаимодействие с вашим продуктом через браузер. В сквозном тестировании везде используются реальные компоненты без каких-либо mock-объектов, а архитектура при этом является отражением настоящей промышленной системы (но с меньшим количеством или с менее дорогими серверами, чтобы сэкономить).

    У каждого типа тестов свое назначение, и с их помощью можно выявить разного рода ошибки, поэтому их стоит применять совместно. Модульные тесты выполняются быстро и позволяют сразу получить представление о внесенных изменениях и проверить разнообразные комбинации. Это дает уверенность в том, что элементарные компоненты вашего кода (отдельные модули) ведут себя так, как вы ожидали. Однако тот факт, что модули работают корректно по отдельности, вовсе не означает, что они смогут работать совместно, поэтому, чтобы убедиться в совместимости ваших элементарных компонентов, нужны интеграционные тесты. С другой стороны, корректное поведение разных частей системы не гарантирует, что эта система будет работать как следует после развертывания в промышленной среде, поэтому, чтобы проверить ваш код в условиях, приближенных к реальным, необходимы сквозные тесты.

    » Более подробно с книгой можно ознакомиться на сайте издательства
    » Оглавление
    » Отрывок

    Для Хаброжителей скидка 25% по купону — Terraform

    По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
    Издательский дом «Питер»
    Компания

    Похожие публикации

    Комментарии 1

      0

      "Инфраструктура на уровне кода"?
      Простите, что?

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое