Имеется Docker-образ с eksctl
.
Имеется ЕС2 с Linux, на которой установлен eksctl
.
К ЕС2 поключен AWS IAM Instance Profile с политикой AdminAccess.
На ЕС2 работает Jenkins, в Docker-контейнере, и свои джобы он запускает в отдельных контейнерах.
Среди прочих – есть джоба на создание Elastic Kubernetes Service, которая вызывается из контейнера с eksctl
.
Проблема: при запуске напрямую с хоста – аутентификация проходит, ресурсы доступны:
[simterm]
$ eksctl --region us-east-2 get cluster NAME REGION bttrm-eks-dev-0 us-east-2 bttrm-eks-prod-0 us-east-2 eksctl-bttrm-eks-production-1 us-east-2
[/simterm]
Содержание
Put http://169.254.169.254/latest/api/token: net/http: request canceled (Client.Timeout exceeded while awaiting headers)) from ec2metadata/GetToken
А при запуске из Docker-контейнера – возникает ошибка “Put http://169.254.169.254/latest/api/token: net/http: request canceled (Client.Timeout exceeded while awaiting headers)) from ec2metadata/GetToken“:
[simterm]
admin@jenkins-production:~$ docker run bttrm/kubectl-aws:2.5 eksctl get cluster [!] retryable error (RequestError: send request failed caused by: Put http://169.254.169.254/latest/api/token: net/http: request canceled (Client.Timeout exceeded while awaiting headers)) from ec2metadata/GetToken - will retry after delay of 46.10367ms
[/simterm]
Но почему?
И что вообще за ошибка?
AWS Config и аутентификация
Второй интересный момент, который заметил, и который помог двигаться в нужном направлении – это файл настроек AWS CLI: если прокинуть его в Docker, и в нём будут какие-то актуальные данные доступа – то всё работает, как ожидается.
Т.е. проблема возникает только тогда, когда eksctl
не может аутентифироваться через ACCESS и SECRET ключи.
Файл конфига:
[simterm]
admin@jenkins-production:~$ cat .aws/config [default] output = json region = us-east-2
[/simterm]
Секретов:
[simterm]
admin@jenkins-production:~$ cat .aws/credentials [default] aws_access_key_id = AKI***D4Q aws_secret_access_key = QUC***BTI
[/simterm]
Пробуем с пробросом конфига:
[simterm]
admin@jenkins-production:~$ docker run -v /home/admin/.aws:/root/.aws/ bttrm/kubectl-aws:2.5 eksctl get cluster NAME REGION bttrm-eks-dev-0 us-east-2 bttrm-eks-prod-0 us-east-2 eksctl-bttrm-eks-production-1 us-east-2
[/simterm]
И без него:
[simterm]
admin@jenkins-production:~$ docker run bttrm/kubectl-aws:2.5 eksctl get cluster [!] retryable error (RequestError: send request failed caused by: Put http://169.254.169.254/latest/api/token: net/http: request canceled (Client.Timeout exceeded while awaiting headers)) from ec2metadata/GetToken - will retry after delay of 32.822673ms
[/simterm]
AWS EC2 Instance Metadata
Первое, что бросается в глаза – это адрес 169.254.169.254.
Почему? Потому что явно помним, что вроде такой же адрес использовался для получения метаданных инстанса, например – он использовался в AWS: ротация ключей IAM пользователей, EC2 IAM Roles и Jenkins.
Глянем дальше, на URI – latest/api/token – тут явно идёт запрос на получение токена аутентификации через Instance Role хоста – логично, так и должно быть: раз eksctl
не смог получить данные из переменных окружения и файла настроек – он пытается получить их через Instance Profile.
Но почему PUT
? Ведь раньше было, кажется, через GET
? Да и логичнее GET
звучит.
После недолгого гугления выяснилось, что AWS обновил процесс аутентификации при получении метаданных с ЕС2, см. Configuring the instance metadata service и ссылки в конце поста.
Пробуем получить токен с хоста напрямую:
[simterm]
admin@jenkins-production:~$ curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" AQA***Cyg==
[/simterm]
И из контейнера:
[simterm]
admin@jenkins-production:~$ docker run bttrm/kubectl-aws:2.5 curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" ... curl: (56) Recv failure: Connection reset by peer
[/simterm]
И ничего.
И какого?
А что мы можем сделать?
А мы можем просто пробросить сеть с хоста в контейнер, а не использовать сеть самого Docker:
[simterm]
admin@jenkins-production:~$ docker run --net=host bttrm/kubectl-aws:2.5 curl -sX PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" AQA***ZmA==
[/simterm]
Готово.
Ссылки по теме
- Retrieving Instance Metadata
- Getting Credentials from EC2 Instance Metadata
- Configuring the Instance Metadata Service
- AWS CLI Configuration Variables
- Serverless using AWS profiles only half working
- Assumed role not found when defined in ~/.aws/config
- AWS Enhances Metadata Service Security with IMDSv2
- Getting started with Version 2 of AWS EC2 Instance Metadata service (IMDSv2)