RE:NODE

Эксплуатация11 мин чтения

AWS CLI с S3-совместимым хранилищем

Как направить AWS CLI на любой S3-совместимый эндпоинт: профили, endpoint_url, path-style, настройка контрольных сумм для новых версий и cp, sync, ls и presign на практике.

0 прочтений

AWS CLI работает с любым S3-совместимым хранилищем, как только узнаёт три вещи, которые иначе предполагает Amazon: URL эндпоинта, то, что бакет указывается в пути, а не в имени хоста, и регион для подписи. Положите все три в именованный профиль, и каждая команда aws s3 будет работать без изменений: aws s3 ls --profile renode покажет ваши бакеты, aws s3 sync ./backups s3://my-bucket/backups --profile renode зеркалирует папку. Свежие версии CLI добавляют для многих серверов не от Amazon ещё одно требование: настройку, которая не даёт CLI отправлять заголовки контрольных сумм, непонятные серверу. В этой статье всё это настраивается, а затем разбираются команды, которыми вы действительно будете пользоваться, с флагами, которые имеют значение.

Установите CLI и проверьте версию#

Используйте AWS CLI версии 2. Это самодостаточный установщик для Windows, macOS и Linux из документации Amazon, и он не зависит от системного Python. Пакеты в дистрибутивах Linux иногда отстают на годы или всё ещё версии 1, так что проверьте:

bash
$ aws --versionaws-cli/2.27.50 Python/3.13.4 Linux/6.8.0 exe/x86_64.ubuntu.24

Всё начиная с 2.13 поддерживает endpoint_url в конфигурационном файле, и именно это делает возможным профиль для своего эндпоинта. До этого приходилось передавать --endpoint-url в каждой команде. Версия 1 тоже работает с этим флагом, но это старая линейка; начинать на ней новую настройку незачем.

Учётные данные, эндпоинт и path-style в одном профиле#

CLI читает два файла в ~/.aws/ (%USERPROFILE%\.aws\ в Windows): credentials для ключей и config для всего остального. Создайте именованный профиль, чтобы ваше S3-совместимое хранилище никогда не смешивалось с аккаунтом Amazon, который у вас тоже может быть.

bash
$ aws configure --profile renodeAWS Access Key ID [None]: RNAKEXAMPLE123AWS Secret Access Key [None]: ****************************************Default region name [None]: us-east-1Default output format [None]: json

Это записывает ключи и регион. Эндпоинт и стиль адресации добавьте, отредактировав ~/.aws/config:

~/.aws/config
[profile renode]region = us-east-1output = jsonendpoint_url = https://s3.example.comrequest_checksum_calculation = when_requiredresponse_checksum_validation = when_requireds3 =  addressing_style = path
~/.aws/credentials
[renode]aws_access_key_id = RNAKEXAMPLE123aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Зачем нужна каждая строка:

  • endpoint_url - базовый URL хранилища. Если у вас есть имя хоста с HTTPS, используйте его; обычный эндпоинт http://host:port тоже работает, но передаёт ваши данные без шифрования.
  • region - любое имя, которое принимает сервер. Оно входит в подпись каждого запроса, поэтому должно быть одинаковым. us-east-1 - общепринятая заглушка, и с ним CLI не отправляет ограничение местоположения при создании бакетов.
  • addressing_style = path - помещает бакет в путь URL (https://s3.example.com/bucket/key), а не в поддомен (https://bucket.s3.example.com/key), для которого нужны wildcard-DNS и wildcard-сертификат, которых у эндпоинта может не быть. Вложенный блок s3 = с настройками с отступом - синтаксис CLI для параметров конкретного сервиса.
  • request_checksum_calculation и response_checksum_validation - объяснены в следующем разделе.

Обратите внимание на асимметрию: в config секция называется [profile renode], а в credentials - [renode]. Ошибка здесь - самая частая причина The config profile (renode) could not be found.

В RE:NODE панель показывает access key и secret key, сгенерированные для сервера хранилища, а первый бакет уже существует. Эндпоинт - это адрес и порт тарифа по обычному HTTP или ваше собственное имя хоста по HTTPS, после того как вы направите A-запись на слот прокси тарифа, который выпускает и продлевает сертификат.

Изменение контрольных сумм в новых версиях CLI#

В январе 2025 года AWS CLI 2.23.0 и вышедшие одновременно с ним AWS SDK изменили умолчание: теперь загрузки вычисляют контрольную сумму CRC и отправляют её в новых заголовках при каждом запросе, где операция это поддерживает, а скачивания проверяют контрольные суммы, если сервер их возвращает. Amazon S3 эти заголовки понимает. Многие S3-совместимые серверы, по крайней мере поначалу, не понимали, и результатом были упавшие загрузки с ошибками, в которых контрольные суммы вообще не упоминаются: SignatureDoesNotMatch, MissingContentLength, XAmzContentSHA256Mismatch или общая InvalidArgument.

Две настройки в профиле выше возвращают старое поведение - контрольные суммы только там, где операция их требует:

bash
$ aws configure set request_checksum_calculation when_required --profile renode$ aws configure set response_checksum_validation when_required --profile renode

Или в виде переменных окружения, которые действуют на каждый инструмент на основе SDK в той же оболочке:

bash
export AWS_REQUEST_CHECKSUM_CALCULATION=when_requiredexport AWS_RESPONSE_CHECKSUM_VALIDATION=when_required

Если ваш сервер правильно обрабатывает новые контрольные суммы, вы ничего важного не теряете, задав эти настройки: целостность по-прежнему защищают TLS и Content-MD5 или хеш payload там, где они требуются. Если загрузки падают со странными ошибками подписи в CLI, который в прошлом году работал, это первое, что нужно проверить. Те же настройки есть в boto3 и других SDK, о них - в статье S3 из Node и Python.

Переменные окружения вместо файлов#

Для контейнеров, задач CI и разовых скриптов переменные окружения часто проще конфигурационных файлов. CLI читает:

ПеременнаяНазначение
AWS_ACCESS_KEY_IDAccess key
AWS_SECRET_ACCESS_KEYSecret key
AWS_REGION или AWS_DEFAULT_REGIONРегион для подписи
AWS_ENDPOINT_URL_S3Эндпоинт только для S3
AWS_ENDPOINT_URLЭндпоинт для всех сервисов
AWS_PROFILEКакой профиль использовать

У path-style адресации в CLI нет собственной переменной окружения, поэтому держите минимальный конфигурационный файл с блоком s3 = addressing_style = path, даже когда ключи берутся из окружения. AWS_CONFIG_FILE указывает CLI на конфигурационный файл в другом месте, не в ~/.aws/config, что удобно в контейнерах.

Для разовой команды к другому эндпоинту --endpoint-url https://s3.example.com в командной строке переопределяет всё остальное. Как не допустить попадания секретного ключа в историю оболочки и репозитории, разобрано в статье переменные окружения и секреты.

Повседневные команды#

Высокоуровневые команды aws s3 сами занимаются multipart-загрузками, повторами и рекурсией.

КомандаЧто делает
aws s3 lsСписок бакетов
aws s3 ls s3://bucket/prefix/Один уровень «папки»
aws s3 ls s3://bucket --recursive --human-readable --summarizeВсе объекты, размеры и итог
aws s3 mb s3://new-bucketСоздать бакет
aws s3 cp file s3://bucket/keyЗагрузить один файл
aws s3 cp s3://bucket/key fileСкачать один файл
aws s3 cp dir s3://bucket/prefix/ --recursiveЗагрузить каталог
aws s3 mvСкопировать, затем удалить источник
aws s3 rm s3://bucket/prefix/ --recursiveУдалить всё под префиксом
aws s3 rb s3://bucket --forceУдалить бакет со всем содержимым
aws s3 presign s3://bucket/key --expires-in 3600Временная ссылка на скачивание

С профилем из примера выше каждая команда принимает --profile renode, либо вы один раз на оболочку выполняете export AWS_PROFILE=renode.

bash
$ export AWS_PROFILE=renode$ aws s3 ls2026-10-08 09:12:44 my-first-bucket$ aws s3 cp ./world-2026-10-08.tar.gz s3://my-first-bucket/minecraft/upload: ./world-2026-10-08.tar.gz to s3://my-first-bucket/minecraft/world-2026-10-08.tar.gz

Косая черта в конце назначения означает «в этот префикс, с сохранением имени файла». Без неё ключ будет ровно тем, что вы написали.

Как проверить, что загрузка действительно на месте

Команда, которая напечатала upload: и завершилась со статусом 0, действительно отработала, но скрипты должны проверять, а не доверять. Самая дешёвая проверка - размер: aws s3api head-object возвращает ContentLength, и сравнение с размером локального файла ловит обрезанные источники - классический случай: дамп, упавший на полпути и давший маленький, но правдоподобный на вид файл. Для однокомпонентной загрузки ETag обычно равен MD5 содержимого, поэтому md5sum локального файла должен с ним совпасть; для multipart-загрузки ETag заканчивается на - и число частей и никогда не совпадёт с MD5, так что сравнивайте размеры или храните рядом с объектом файл .sha256.

bash
$ stat -c %s world-2026-10-08.tar.gz734003200$ aws s3api head-object --bucket my-first-bucket \    --key minecraft/world-2026-10-08.tar.gz --query ContentLength734003200

Настоящая проверка - скачать его снова в другом месте и открыть; как часто и как именно - в статье проверка восстановления до того, как оно понадобится.

cp читает из стандартного ввода, когда источник - -, и это позволяет передавать бэкап потоком, не записывая временный файл:

bash
$ mysqldump --single-transaction app | gzip \    | aws s3 cp - s3://my-first-bucket/db/app-$(date +%F).sql.gz

Для потоков больше примерно 50 GB добавьте --expected-size с приблизительным числом байт, чтобы CLI мог выбрать размер частей, укладывающийся в лимит в 10 000 частей. Полноценная задача вокруг этого построена в статье дампы баз данных в S3 по расписанию.

presign создаёт URL с подписью в строке запроса. Время жизни по умолчанию - 3600 секунд, максимум - семь дней (604 800 секунд). Любой, у кого есть этот URL, может скачать объект, пока срок не истёк, так что обращайтесь с ним как с паролем с ограниченным сроком. Сторона загрузки, которую CLI сгенерировать не может, разобрана в статье presigned URL в S3 для загрузок.

sync: что он сравнивает и как фильтровать#

aws s3 sync копирует всё новое или изменённое из источника в назначение - из локального каталога в бакет, из бакета в локальный каталог или из бакета в бакет.

bash
$ aws s3 sync ./backups s3://my-first-bucket/backups --dryrun$ aws s3 sync ./backups s3://my-first-bucket/backups

Он считает файл изменённым, когда отличается размер или локальное время изменения новее, чем Last-Modified объекта. Содержимое он не сравнивает. Это меняют два флага:

  • --size-only - сравнивать только размер. Полезно, когда метки времени ненадёжны, например после копирования файлов без сохранения времени. Пропускает изменения, при которых размер не меняется.
  • --exact-timestamps - при скачивании считать изменёнными файлы того же размера с любым расхождением во времени.

По умолчанию sync никогда ничего не удаляет. --delete удаляет из назначения объекты, которых больше нет в источнике, - а это превращает ошибку в источнике в ошибку в назначении. Sync с --delete - это зеркало, а не бэкап. Всегда сначала запускайте его с --dryrun и читайте вывод.

Фильтры - это шаблоны --exclude и --include, которые применяются по порядку, причём более поздние имеют приоритет:

bash
# Only .tar.gz files$ aws s3 sync ./backups s3://my-first-bucket/backups \    --exclude "*" --include "*.tar.gz"# Everything except logs and temp files$ aws s3 sync ./site s3://my-first-bucket/site \    --exclude "*.log" --exclude "tmp/*"

Sync работает и в обратном направлении, и так выполняется восстановление: aws s3 sync s3://my-first-bucket/backups ./restore скачивает всё, чего нет локально или что отличается. Направляйте его в пустой каталог, а не в рабочий, проверьте, что пришло, и только потом переносите на место. Sync из бакета в бакет на одном эндпоинте (aws s3 sync s3://bucket-a s3://bucket-b) использует копирование на стороне сервера, так что через вашу машину ничего не проходит.

Шаблоны сопоставляются с путём относительно каталога-источника. --exclude "*" --include "*.tar.gz" работает; в обратном порядке исключается всё, потому что побеждает более поздний --exclude "*".

Скорость, multipart и полоса пропускания#

CLI загружает файлы больше 8 MB частями по 8 MB, по десять запросов одновременно. Для больших файлов по быстрому каналу помогают более крупные части и большая параллельность; на медленном или общем канале ограничение полосы не даёт загрузке задушить всё остальное.

~/.aws/config
[profile renode]s3 =  addressing_style = path  max_concurrent_requests = 10  multipart_threshold = 64MB  multipart_chunksize = 64MB  max_bandwidth = 20MB/s

max_bandwidth принимает байты в секунду с суффиксом, так что 20MB/s - это 160 Мбит/с. Поднимать max_concurrent_requests выше 20-30 при работе с одним небольшим эндпоинтом редко помогает: ограничением становятся сервер и сеть между вами. Очень крупные части означают, что упавшую часть дольше повторять; разумный диапазон - 16-64 MB.

Низкоуровневые команды через s3api#

aws s3api один к одному соответствует операциям API. Он многословен, но даёт доступ ко всему:

bash
# Metadata of one object: size, ETag, content type, user metadata$ aws s3api head-object --bucket my-first-bucket --key db/app-2026-10-08.sql.gz# Keys and sizes under a prefix, filtered with JMESPath$ aws s3api list-objects-v2 --bucket my-first-bucket --prefix db/ \    --query 'Contents[].[Key,Size]' --output text# Find and abort abandoned multipart uploads$ aws s3api list-multipart-uploads --bucket my-first-bucket$ aws s3api abort-multipart-upload --bucket my-first-bucket \    --key big.tar --upload-id 'EXAMPLEuploadID'

При загрузке файлов, которые будет запрашивать браузер, задавайте тип содержимого явно - aws s3 cp logo.svg s3://bucket/ --content-type image/svg+xml, - потому что угаданный binary/octet-stream заставляет браузеры скачивать файл вместо показа.

Устранение неполадок#

`Could not connect to the endpoint URL`. Неверный хост, порт или схема в endpoint_url либо файрвол. Попробуйте curl -I на эндпоинт: любой HTTP-ответ, даже 403, доказывает, что сетевой путь в порядке.

`InvalidAccessKeyId`. Access key неверный, или CLI использует не тот профиль, о котором вы думаете. aws configure list --profile renode показывает, какие учётные данные и регион он определил и откуда.

`SignatureDoesNotMatch`. Неверный secret key, регион отличается от того, что ожидает сервер, системные часы сбиты на минуты или - в CLI 2.23 и новее - заголовки контрольных сумм, описанные выше. Проверьте часы через date, затем настройки контрольных сумм.

`RequestTimeTooSkewed`. Часы машины слишком расходятся с часами сервера. Исправляйте синхронизацию времени (timedatectl в Linux с systemd), а не что-то в CLI.

`AccessDenied` на одном бакете, но не на другом. Ключи принадлежат другому серверу или аккаунту, чем бакет, или имя бакета написано с ошибкой - некоторые серверы на бакет, который вам не виден, отвечают отказом в доступе, а не «не найдено».

`NoSuchBucket` или ошибки DNS для `bucket.s3.example.com`. CLI использует virtual-hosted адресацию. Добавьте addressing_style = path.

`SSL: CERTIFICATE_VERIFY_FAILED`. Вы используете HTTPS-эндпоинт с сертификатом, которому CLI не доверяет, или IP-адрес с сертификатом, выпущенным на имя. Используйте имя хоста из сертификата. --no-verify-ssl убирает ошибку, убирая защиту; не оставляйте его в скриптах.

Во всех остальных случаях --debug выводит заголовки каждого запроса и ответа, и видно, что именно было подписано и что ответил сервер.

FAQ#

Нужен ли аккаунт AWS, чтобы пользоваться AWS CLI?

Нет. CLI - это просто клиент. Направьте его на любой S3-совместимый эндпоинт с ключами этого эндпоинта, и он ни разу не обратится к Amazon.

Можно ли использовать один профиль для Amazon S3 и другого провайдера?

Используйте отдельные профили. У каждого профиля свой эндпоинт, ключи и стиль адресации, а --profile или AWS_PROFILE выбирает один из них. Смешивание их в одном профиле - это то, как бэкапы оказываются не в том месте.

Почему sync заново загружает файлы, которые не менялись?

Обычно из-за времени изменения: что-то тронуло файлы, или их скопировали без сохранения меток времени, и локальные копии выглядят новее. --size-only этого избегает, если вы доверяете размерам. Другая причина - префикс назначения, отличающийся от прошлого запуска косой чертой на конце или опечаткой.

Sync с --delete - это бэкап?

Нет. Он делает назначение таким же, как источник, включая удаления и повреждения. Для бэкапов с историей используйте инструмент, который хранит снимки, например restic - см. бэкапы restic в S3, - или rclone с каталогом для изменённых и удалённых файлов.

Можно ли копировать между бакетами двух разных провайдеров?

Не одной командой. aws s3 cp и sync используют один эндпоинт за вызов, так что копирование от одного провайдера к другому - это скачивание в локальный каталог с одним профилем и загрузка с другим. rclone умеет сделать это за один шаг, передавая данные потоком между двумя remote.

Как запускать загрузки по расписанию через cron?

Cron работает с минимальным окружением, поэтому передайте задаче всё явно: полный путь к aws, AWS_PROFILE и HOME (чтобы CLI нашёл ~/.aws). Перенаправляйте вывод в файл журнала и проверяйте его, потому что задача cron, которая молча падает, - это бэкап, которого не существует.


Комментарии

Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.

0/2000