Имеется ЕС2 с Linux, на которой установлен eksctl.
К ЕС2 поключен AWS IAM Instance Profile с политикой AdminAccess.
На ЕС2 работает Jenkins, в Docker-контейнере, и свои джобы он запускает в отдельных контейнерах.
Среди прочих — есть джоба на создание Elastic Kubernetes Service, которая вызывается из контейнера с eksctl.
Проблема: при запуске напрямую с хоста — аутентификация проходит, ресурсы доступны:
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
Содержание
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«:
admin@jenkins-production:~$ docker run bttrm/kubectl-aws:2.5 eksctl get cluster
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
Но почему?
И что вообще за ошибка?
AWS Config и аутентификация
Второй интересный момент, который заметил, и который помог двигаться в нужном направлении — это файл настроек AWS CLI: если прокинуть его в Docker, и в нём будут какие-то актуальные данные доступа — то всё работает, как ожидается.
Т.е. проблема возникает только тогда, когда eksctl не может аутентифироваться через ACCESS и SECRET ключи.
Файл конфига:
admin@jenkins-production:~$ cat .aws/config
[default]
output = json
region = us-east-2
Секретов:
admin@jenkins-production:~$ cat .aws/credentials
[default]
aws_access_key_id = AKI***D4Q
aws_secret_access_key = QUC***BTI
Пробуем с пробросом конфига:
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
И без него:
admin@jenkins-production:~$ docker run bttrm/kubectl-aws:2.5 eksctl get cluster
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
AWS EC2 Instance Metadata
Первое, что бросается в глаза — это адрес 169.254.169.254.
Глянем дальше, на URI — latest/api/token — тут явно идёт запрос на получение токена аутентификации через Instance Role хоста — логично, так и должно быть: раз eksctl не смог получить данные из переменных окружения и файла настроек — он пытается получить их через Instance Profile.
Но почему PUT? Ведь раньше было, кажется, через GET? Да и логичнее GET звучит.
После недолгого гугления выяснилось, что AWS обновил процесс аутентификации при получении метаданных с ЕС2, см. Configuring the instance metadata service и ссылки в конце поста.
Пробуем получить токен с хоста напрямую:
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==
И из контейнера:
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
И ничего.
И какого?
А что мы можем сделать?
А мы можем просто пробросить сеть с хоста в контейнер, а не использовать сеть самого Docker:
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"