AWS: eksctl – “Put http://169.254.169.254/latest/api/token: net/http: request canceled”

Автор: | 16/04/2020
 

Имеется 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]

Готово.

Ссылки по теме