Yandex Cloud
Поиск
Связаться с экспертомПопробовать бесплатно
  • Кейсы
  • Документация
  • Блог
  • Все сервисы
  • Статус работы сервисов
  • Marketplace
    • Популярные
    • Инфраструктура и сеть
    • Платформа данных
    • Искусственный интеллект
    • Безопасность
    • Инструменты DevOps
    • Бессерверные вычисления
    • Управление ресурсами
  • Все решения
    • По отраслям
    • По типу задач
    • Экономика платформы
    • Безопасность
    • Техническая поддержка
    • Каталог партнёров
    • Обучение и сертификация
    • Облако для стартапов
    • Облако для крупного бизнеса
    • Центр технологий для общества
    • Облако для интеграторов
    • Поддержка IT-бизнеса
    • Облако для фрилансеров
    • Обучение и сертификация
    • Блог
    • Документация
    • Контент-программа
    • Мероприятия и вебинары
    • Контакты, чаты и сообщества
    • Идеи
    • Калькулятор цен
    • Тарифы
    • Акции и free tier
  • Кейсы
  • Документация
  • Блог
Создавайте контент и получайте гранты!Готовы написать своё руководство? Участвуйте в контент-программе и получайте гранты на работу с облачными сервисами!
Подробнее о программе
Проект Яндекса
© 2026 ООО «Яндекс.Облако»
Yandex Managed Service for YDB
  • Начало работы
    • Все руководства
    • Развертывание веб-приложения
    • Подключение к YDB из функции Yandex Cloud Functions на Python
    • Подключение к базе данных YDB из функции Yandex Cloud Functions на Node.js
    • Разработка навыка Алисы и сайта с авторизацией
    • Миграция базы данных из Yandex Managed Service for MySQL® с помощью Yandex Data Transfer
    • Поставка данных из YDB в Yandex Managed Service for Apache Kafka® с помощью Yandex Data Transfer
    • Поставка данных из YDB в Yandex Data Streams с помощью Yandex Data Transfer
    • Загрузка данных из Yandex Object Storage в YDB с помощью Yandex Data Transfer
    • Загрузка данных из YDB в Yandex Object Storage с помощью Yandex Data Transfer
    • Загрузка данных из YDB в Yandex Managed Service for ClickHouse® с помощью Yandex Data Transfer
    • Миграция данных из Yandex Managed Service for OpenSearch в YDB с помощью Yandex Data Transfer
    • Построение пайплайна CI/CD в GitLab с использованием serverless-продуктов
    • Поставка данных из Yandex Managed Service for PostgreSQL в Yandex Managed Service for YDB с помощью Yandex Data Transfer
    • Передача событий Yandex Cloud Postbox в Yandex Data Streams и их анализ с помощью Yandex DataLens
    • Поставка данных из Yandex Managed Service for Apache Kafka® в Data Streams с помощью Yandex Data Transfer
    • Поставка данных из Yandex Managed Service for Apache Kafka® с помощью Yandex Data Transfer
    • Сохранение потока данных Data Streams в Managed Service for ClickHouse®
    • Поставка данных из очереди Data Streams в Managed Service for Apache Kafka® с помощью Data Transfer
    • Поставка данных из очереди Data Streams в Managed Service for YDB с помощью Data Transfer
    • Захват изменений MySQL® и поставка в YDS
    • Захват изменений PostgreSQL и поставка в YDS
    • Интерактивная отладка функций Cloud Functions
    • Блокировка состояний Terraform с помощью Managed Service for YDB
    • Разработка пользовательской интеграции в API Gateway
    • Разработка CRUD API для сервиса фильмов
    • Создание интерактивного serverless-приложения с использованием WebSocket
      • Обзор
      • Консоль управления
      • Terraform
    • Интеграция Yandex Cloud Postbox с внешними системами через вебхуки
  • Управление доступом
  • Метрики Monitoring
  • Аудитные логи Audit Trails
  • Вопросы и ответы
  • Публичные материалы
  • Обучающие курсы

В этой статье:

  • Подготовьте облако к работе
  • Необходимые платные ресурсы
  • Создайте инфраструктуру
  • Проверьте работу сокращателя ссылок
  • Как удалить созданные ресурсы
  1. Практические руководства
  2. Сокращатель ссылок
  3. Terraform

Сокращатель ссылок с помощью Terraform

Статья создана
Yandex Cloud
Улучшена
kvendingoldo
Обновлена 8 мая 2026 г.
  • Подготовьте облако к работе
    • Необходимые платные ресурсы
  • Создайте инфраструктуру
  • Проверьте работу сокращателя ссылок
  • Как удалить созданные ресурсы

Чтобы создать сокращатель ссылок с помощью Terraform:

  1. Подготовьте облако к работе.
  2. Создайте инфраструктуру.
  3. Проверьте работу сокращателя.

Если созданные ресурсы вам больше не нужны, удалите их.

Подготовьте облако к работеПодготовьте облако к работе

Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:

  1. Перейдите в консоль управления, затем войдите в Yandex Cloud или зарегистрируйтесь.
  2. На странице Yandex Cloud Billing убедитесь, что у вас подключен платежный аккаунт, и он находится в статусе ACTIVE или TRIAL_ACTIVE. Если платежного аккаунта нет, создайте его и привяжите к нему облако.

Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака.

Подробнее об облаках и каталогах.

Необходимые платные ресурсыНеобходимые платные ресурсы

В стоимость поддержки инфраструктуры для сокращателя ссылок входят:

  • плата за хранение данных (см. тарифы Yandex Object Storage);
  • плата за операции с базой данных YDB и хранение данных (см. тарифы Managed Service for YDB);
  • плата за количество вызовов функции, вычислительные ресурсы, выделенные для выполнения функции, и исходящий трафик (см. тарифы Cloud Functions);
  • плата за количество запросов к API-шлюзу и исходящий трафик (см. тарифы API Gateway).

Создайте инфраструктуруСоздайте инфраструктуру

Terraform позволяет быстро создать облачную инфраструктуру в Yandex Cloud и управлять ею с помощью файлов конфигураций. В файлах конфигураций хранится описание инфраструктуры на языке HCL (HashiCorp Configuration Language). При изменении файлов конфигураций Terraform автоматически определяет, какая часть вашей конфигурации уже развернута, что следует добавить или удалить.

Terraform распространяется под лицензией Business Source License, а провайдер Yandex Cloud для Terraform — под лицензией MPL-2.0.

Подробную информацию о ресурсах провайдера смотрите в документации на сайте Terraform или в зеркале.

Для создания инфраструктуры с помощью Terraform:

  1. Установите Terraform, получите данные для аутентификации и укажите источник для установки провайдера Yandex Cloud (раздел Настройте провайдер, шаг 1).

  2. Подготовьте файлы с описанием инфраструктуры:

    Готовая конфигурация
    Вручную
    1. Клонируйте репозиторий с конфигурационными файлами.

      git clone https://github.com/yandex-cloud-examples/yc-serverless-url-shortener.git
      
    2. Перейдите в директорию с репозиторием. В ней должны появиться файлы:

      • serverless-url-shortener.tf — конфигурация создаваемой инфраструктуры;
      • serverless-url-shortener.auto.tfvars — файл с пользовательскими данными;
      • index.html — HTML-страница вашего сервиса;
      • function.zip — архив с кодом функции Cloud Functions.
    1. Создайте папку для конфигурационных файлов.

    2. Создайте в папке:

      1. Конфигурационный файл serverless-url-shortener.tf:

        serverless-url-shortener.tf
        # Объявление переменных для конфиденциальных параметров
        
        variable "cloud_id" {
          type = string
        }
        
        variable "folder_id" {
          type = string
        }
        
        variable "bucket_name" {
          type = string
        }
        
        # Настройка провайдера
        
        terraform {
          required_providers {
            yandex = {
              source = "yandex-cloud/yandex"
            }
          }
        }
        
        provider "yandex" {
          cloud_id  = var.cloud_id
          folder_id = var.folder_id
        }
        
        # Создание сервисного аккаунта
        
        resource "yandex_iam_service_account" "shortener_sa" {
          name        = "serverless-shortener"
          description = "Service account for the URL shortener"
        }
        
        # Назначение роли сервисному аккаунту
        
        resource "yandex_resourcemanager_folder_iam_member" "shortener_role" {
          folder_id = var.folder_id
          role      = "editor"
          member    = "serviceAccount:${yandex_iam_service_account.shortener_sa.id}"
        }
        
        # Создание статического ключа
        
        resource "yandex_iam_service_account_static_access_key" "shortener_sa_key" {
          service_account_id = yandex_iam_service_account.shortener_sa.id
          description        = "Static access key for the service account"
        }
        
        # Создание бакета
        
        resource "yandex_storage_bucket" "shortener_bucket" {
          bucket     = var.bucket_name
          access_key = yandex_iam_service_account_static_access_key.shortener_sa_key.access_key
          secret_key = yandex_iam_service_account_static_access_key.shortener_sa_key.secret_key
          max_size = 1073741824
          anonymous_access_flags {
            read        = true
            list        = false
            config_read = false
          }
        
          website {
            index_document = "index.html"
          }
        }
        
        # Загрузка объекта в бакет
        
        resource "yandex_storage_object" "shortener_index" {
          bucket        = yandex_storage_bucket.shortener_bucket.bucket
          key           = "index.html"
          source        = "index.html"
          acl           = "public-read"
          access_key    = yandex_iam_service_account_static_access_key.shortener_sa_key.access_key
          secret_key    = yandex_iam_service_account_static_access_key.shortener_sa_key.secret_key
          content_type  = "text/html"
        }
        
        # Создание базы данных YDB
        
        resource "yandex_ydb_database_serverless" "shortener_db" {
          name        = "shortener-ydb-main"
          location_id = "ru-central1"
        }
        
        
        # Создание таблицы YDB
        
        resource "yandex_ydb_table" "test_table" {
          path              = "links"
          connection_string = yandex_ydb_database_serverless.shortener_db.ydb_full_endpoint
          column {
            name     = "id"
            type     = "Utf8"
            not_null = true
          }
          column {
            name     = "link"
            type     = "Utf8"
            not_null = true
          }
          primary_key = ["id"]
          
          depends_on = [ yandex_ydb_database_serverless.shortener_db ]
          }
        
        # Создание функции
        
        resource "yandex_function" "shortener_function" {
          name               = "shortener-function-main"
          description        = "Function for the URL shortener"
          runtime            = "python312"
          entrypoint         = "index.handler"
          memory             = 256
          execution_timeout  = 5
          service_account_id = yandex_iam_service_account.shortener_sa.id
        
          content {
            zip_filename = "function.zip"
          }
        
          user_hash = filesha256("function.zip")
        
          environment = {
            endpoint = "grpcs://${yandex_ydb_database_serverless.shortener_db.ydb_api_endpoint}"
            database = yandex_ydb_database_serverless.shortener_db.database_path
          }
        }
        
        resource "yandex_function_iam_binding" "public_invoker" {
          function_id = yandex_function.shortener_function.id
          role        = "functions.functionInvoker"
          members     = ["system:allUsers"]
        }
        
        # Создание API-шлюза
        
        resource "yandex_api_gateway" "shortener_gateway" {
          name = "shortener-gateway-main"
        
          spec = jsonencode({
            openapi = "3.0.0"
            info = {
              title   = "Shortener API Gateway"
              version = "1.0.0"
            }
            paths = {
              "/" = {
                get = {
                  "x-yc-apigateway-integration" = {
                    type               = "object_storage"
                    bucket             = yandex_storage_bucket.shortener_bucket.bucket
                    object             = "index.html"
                    presigned_redirect = false
                    service_account_id = yandex_iam_service_account.shortener_sa.id
                  }
                  operationId = "static"
                }
              }
              "/shorten" = {
                post = {
                  "x-yc-apigateway-integration" = {
                    type        = "cloud_functions"
                    function_id = yandex_function.shortener_function.id
                  }
                  operationId = "shorten"
                }
              }
              "/r/{id}" = {
                get = {
                  "x-yc-apigateway-integration" = {
                    type        = "cloud_functions"
                    function_id = yandex_function.shortener_function.id
                  }
                  operationId = "redirect"
                  parameters = [
                    {
                      description = "ID of the shortened link"
                      explode     = false
                      in          = "path"
                      name        = "id"
                      required    = true
                      schema = {
                        type = "string"
                      }
                      style = "simple"
                    }
                  ]
                }
              }
            }
          })
        }
        
        # URL для работы с сокращателем ссылок
        
        output "url" {
          value = "https://${yandex_api_gateway.shortener_gateway.domain}"
        }
        
      2. Файл с пользовательскими данными serverless-url-shortener.auto.tfvars:

        serverless-url-shortener.auto.tfvars
        cloud_id    = "<идентификатор_облака>" 
        folder_id   = "<идентификатор_каталога>"
        bucket_name = "<имя_бакета>"
        
      3. HTML-страница вашего сервиса index.html:

        index.html
        <!DOCTYPE html>
        <html lang="en">
        
        <head>
          <meta charset="UTF-8">
          <title>Сокращатель URL</title>
          <!-- предостережет от лишнего GET запроса на адрес /favicon.ico -->
          <link rel="icon" href="data:;base64,iVBORw0KGgo=">
        </head>
        
        <body>
          <h1>Добро пожаловать</h1>
          <form action="javascript:shorten()">
            <label for="url">Введите ссылку:</label><br>
            <input id="url" name="url" type="text"><br>
            <input type="submit" value="Сократить">
          </form>
          <p id="shortened"></p>
        </body>
        
        <script>
          function shorten() {
            const link = document.getElementById("url").value
            fetch("/shorten", {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json'
              },
              body: link
            })
            .then(response => response.json())
            .then(data => {
              const url = data.url
              document.getElementById("shortened").innerHTML = `<a href=${url}>${url}</a>`
            })
            .catch(error => {
              document.getElementById("shortened").innerHTML = `<p>Произошла ошибка ${error}, попробуйте еще раз</p>`
            })
          }
        </script>
        
        </html>
        
      4. Файл с кодом функции Cloud Functions index.py:

        index.py
        import ydb
        import urllib.parse
        import hashlib
        import base64
        import json
        import os
        import traceback
        
        def decode(event, body):
            is_base64_encoded = event.get('isBase64Encoded')
            if is_base64_encoded:
                body = str(base64.b64decode(body), 'utf-8')
            return body
        
        def response(statusCode, headers, isBase64Encoded, body):
            # Всегда отдаём строку в body
            if not isinstance(body, str):
                body = json.dumps(body, ensure_ascii=False)
            return {
                'statusCode': statusCode,
                'headers': headers,
                'isBase64Encoded': isBase64Encoded,
                'body': body,
            }
        
        def get_config():
            endpoint = os.getenv("endpoint")
            database = os.getenv("database")
            if endpoint is None or database is None:
                raise AssertionError("Нужно указать обе переменные окружения: endpoint и database")
            credentials = ydb.iam.MetadataUrlCredentials()
            return ydb.DriverConfig(endpoint, database, credentials=credentials)
        
        def execute(config, query, params):
            with ydb.Driver(config) as driver:
                try:
                    driver.wait(timeout=5, fail_fast=True)
                except Exception as e:
                    print("Connect failed to YDB:", e)
                    print(driver.discovery_debug_details())
                    raise
        
                session = driver.table_client.session().create()
                prepared_query = session.prepare(query)
                return session.transaction(ydb.SerializableReadWrite()).execute(
                    prepared_query,
                    params,
                    commit_tx=True
                )
        
        def insert_link(id, link):
            config = get_config()
            query = """
            DECLARE $id AS Utf8;
            DECLARE $link AS Utf8;
        
            UPSERT INTO links (id, link) VALUES ($id, $link);
            """
            params = {'$id': id, '$link': link}
            execute(config, query, params)
        
        def find_link(id):
            config = get_config()
            query = """
            DECLARE $id AS Utf8;
        
            SELECT link FROM links where id=$id;
            """
            params = {'$id': id}
            result_set = execute(config, query, params)
        
            if not result_set or not result_set[0].rows:
                return None
        
            # Учитываем структуру результата от ydb
            return result_set[0].rows[0].link
        
        def shorten(event):
            try:
                body = event.get('body')
                if body is None:
                    return response(400, {'Content-Type': 'application/json'}, False,
                                    {'error': 'В теле запроса отсутствует тело'})
        
                body = decode(event, body)
        
                # Попробуем распарсить JSON с ключом url, иначе считаем body как plain string
                url_value = None
                try:
                    parsed = json.loads(body)
                    if isinstance(parsed, dict):
                        url_value = parsed.get('url')
                    else:
                        # если отправили не-объект JSON, игнорируем
                        url_value = None
                except Exception:
                    # body не JSON — считаем, что это plain URL
                    url_value = body
        
                if not url_value:
                    return response(400, {'Content-Type': 'application/json'}, False,
                                    {'error': 'Ожидался параметр url в теле запроса'})
        
                # Очищаем URL от эвентуальных кодированных символов
                clean_url = urllib.parse.unquote(url_value).strip()
                if not clean_url:
                    return response(400, {'Content-Type': 'application/json'}, False,
                                    {'error': 'Пустой url'})
        
                link_id = hashlib.sha256(clean_url.encode('utf8')).hexdigest()[:6]
                insert_link(link_id, clean_url)
        
                # Возвращаем относительный путь — фронт сам допишет origin
                return response(200, {'Content-Type': 'application/json'}, False,
                                {'url': f'/r/{link_id}'})
            except Exception as e:
                print("Exception in shorten():", e)
                traceback.print_exc()
                return response(500, {'Content-Type': 'application/json'}, False,
                                {'error': 'internal server error'})
        
        def redirect(event):
            try:
                # защитно доставать path params
                path_params = event.get('pathParams') or event.get('pathParameters') or {}
                link_id = path_params.get('id')
                if not link_id:
                    # возможно пришёл полный путь в event['url'] или ['path']
                    url = event.get('url') or event.get('path') or ''
                    if url and url.startswith('/r/'):
                        link_id = url.split('/r/')[-1].split('?')[0]
        
                if not link_id:
                    return response(400, {'Content-Type': 'application/json'}, False, {'error': 'id отсутствует'})
        
                redirect_to = find_link(link_id)
        
                if redirect_to:
                    return response(302, {'Location': redirect_to}, False, '')
                return response(404, {'Content-Type': 'application/json'}, False, {'error': 'Данной ссылки не существует'})
            except Exception as e:
                print("Exception in redirect():", e)
                traceback.print_exc()
                return response(500, {'Content-Type': 'application/json'}, False, {'error': 'internal server error'})
        
        def get_result(url, event):
            if url == "/shorten" or url.startswith("/shorten"):
                return shorten(event)
            if url.startswith("/r/"):
                return redirect(event)
            return response(404, {'Content-Type': 'application/json'}, False, {'error': 'Данного пути не существует'})
        
        def handler(event, context):
            url = event.get('url') or event.get('path') or ''
            if url:
                # Иногда URL из шлюза приходит с вопросительным знаком на конце
                if url.endswith('?'):
                    url = url[:-1]
                return get_result(url, event)
            return response(404, {'Content-Type': 'application/json'}, False, {'error': 'Функция должна вызываться через API Gateway.'})
        
      5. Файл с параметрами окружения функции Cloud Functions requirements.txt:

        ydb
        
    3. Создайте в папке архив function.zip, содержащий файлы index.py и requirements.txt.

    4. В файле serverless-url-shortener.auto.tfvars задайте пользовательские параметры:

      • cloud_id — идентификатор облака.
      • folder_id — идентификатор каталога.
      • bucket_name — имя бакета, в котором будут создаваться ресурсы.

    Более подробную информацию о параметрах используемых ресурсов в Terraform см. в документации провайдера:

    • Сервисный аккаунт — yandex_iam_service_account.
    • Статический ключ — yandex_iam_service_account_static_access_key.
    • Бакет — yandex_storage_bucket.
    • Объект — yandex_storage_object.
    • База данных Managed Service for YDB — yandex_ydb_database_serverless.
    • Таблица Managed Service for YDB — yandex_ydb_table.
    • Функция — yandex_function.
    • API-шлюз — yandex_api_gateway.
  3. Создайте ресурсы:

    1. В терминале перейдите в директорию с конфигурационным файлом.

    2. Проверьте корректность конфигурации с помощью команды:

      terraform validate
      

      Если конфигурация является корректной, появится сообщение:

      Success! The configuration is valid.
      
    3. Выполните команду:

      terraform plan
      

      В терминале будет выведен список ресурсов с параметрами. На этом этапе изменения не будут внесены. Если в конфигурации есть ошибки, Terraform на них укажет.

    4. Примените изменения конфигурации:

      terraform apply
      
    5. Подтвердите изменения: введите в терминале слово yes и нажмите Enter.

  4. Скопируйте URL, полученный в результате создания инфраструктуры, чтобы проверить работу сокращателя ссылок.

Проверьте работу сокращателя ссылокПроверьте работу сокращателя ссылок

Чтобы проверить правильность взаимодействия компонентов сервиса:

  1. Откройте в браузере скопированный ранее URL сокращателя.

  2. В поле для ввода введите ссылку, которую вы хотите сократить.

  3. Нажмите кнопку Сократить.

    Ниже отобразится сокращенная ссылка.

  4. Перейдите по сокращенной ссылке — должна открыться та же страница, что и по ссылке до сокращения.

Как удалить созданные ресурсыКак удалить созданные ресурсы

Чтобы перестать платить за созданные ресурсы:

  1. Откройте конфигурационный файл serverless-url-shortener.tf и удалите описание создаваемой инфраструктуры из файла.

  2. Примените изменения:

    1. В терминале перейдите в директорию с конфигурационным файлом.

    2. Проверьте корректность конфигурации с помощью команды:

      terraform validate
      

      Если конфигурация является корректной, появится сообщение:

      Success! The configuration is valid.
      
    3. Выполните команду:

      terraform plan
      

      В терминале будет выведен список ресурсов с параметрами. На этом этапе изменения не будут внесены. Если в конфигурации есть ошибки, Terraform на них укажет.

    4. Примените изменения конфигурации:

      terraform apply
      
    5. Подтвердите изменения: введите в терминале слово yes и нажмите Enter.

См. такжеСм. также

  • Сокращатель ссылок с помощью консоли управления

Была ли статья полезна?

Предыдущая
Консоль управления
Следующая
Обзор
Создавайте контент и получайте гранты!Готовы написать своё руководство? Участвуйте в контент-программе и получайте гранты на работу с облачными сервисами!
Подробнее о программе
Проект Яндекса
© 2026 ООО «Яндекс.Облако»