AWS Notes
5.6K subscribers
444 photos
42 videos
10 files
2.8K links
AWS Notes — Amazon Web Services Educational and Information Channel

Chat: https://xn--r1a.website/aws_notes_chat

Contacts: @apple_rom, https://www.linkedin.com/in/roman-siewko/
Download Telegram
Общий способ добавления в шаблон #vpc_endpoint (без политик):

vpcEndpointS3:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !ImportValue vpc4
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
RouteTableIds:
- !ImportValue rtbVpc4Dmz
- !ImportValue rtbVpc4Private


#CloudFormation #templates
Если добавлять #vpc_endpoints на что-то (с политиками), например, лишь для загрузки пакетов для обновления #AMI (), то вид следующий:

vpcEndpointS3:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !ImportValue vpc4
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal: '*'
Action:
- 's3:GetObject'
Resource:
- 'arn:aws:s3:::packages.*.amazonaws.com/*'
- 'arn:aws:s3:::repo.*.amazonaws.com/*'
RouteTableIds:
- !ImportValue rtbVpc4Dmz
- !ImportValue rtbVpc4Private


#CloudFormation #templates
Для #vpc_endpoint типа Gateway не требуется PrivateDnsEnabled, его указывать не нужно (иначе будет #error).

vpcEndpointDynamoDb:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !ImportValue vpc4
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.dynamodb'
RouteTableIds:
- !ImportValue rtbVpc4Dmz
- !ImportValue rtbVpc4Private


Также #vpc_endpoint типа Gateway требует RouteTableIds и работает лишь с ним (указание SubnetIds и/или SecurityGroupIds даст #error). В то время как для #vpc_endpoint типа Interface, наоборот, требуется SubnetIds и/или SecurityGroupIds:

vpcEndpointEc2Messages:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !ImportValue vpc4
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ec2messages'
PrivateDnsEnabled: true
VpcEndpointType: Interface
SubnetIds:
- !ImportValue subnetVpc4PrivateAppA
- !ImportValue subnetVpc4PrivateAppB
SecurityGroupIds:
- !ImportValue sgVpc4Dummy


Указывать несколько SubnetIds из одной подзоны нельзя (иначе будет #error), потому можно указать лишь Security Group (например, пустую - заглушку). Хотя можно и то, и другое.

#CloudFormation #templates
ReadOnly #bucket_policy для доступа к файлам/бакету из других аккаунтов ("расширенная версия" - не забываем добавлять ListBucket для возможности копировать с профиксом/рекурсией, а также GetBucketLocation для определения региона бакета):

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Access to files/* for accounts: dev1, dev2",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::111111111111:root",
"arn:aws:iam::222222222222:root"
]
},
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::shared-bucket/files/*",
"arn:aws:s3:::shared-bucket"
]
}
]
}


#s3 #cross_account #access
Пример экономии при использовании #vpc_endpoint для #s3.
Если коротко, то при использовании приватных подсетей, когда исходящий трафик идёт через NAT GW - через него же идут запросы к сервисам амазона, в частности, #s3, что тарифицируется (т.к. "выходит в интернет" как обычный https запрос). Чтобы такого не происходило, стоит поднимать #vpc_endpoint и тогда трафик будет внутренним (не будет "выходить в интернет").

#cost_saving
На данный момент через #CloudFormation нельзя создавать #ES в #VPC, нужно предварительно создать #IAM #role, для этого выполнить #aws_cli:

aws iam create-service-linked-role --aws-service-name es.amazonaws.com
Сервисы #AWS по их наличию в регионах.

#aws_region
Разложенные по применению бакет или /* #s3 операции.
То есть, чтобы не ошибиться и не получить #error "Action does not apply to any resource(s) in statement", которая появляется из-за того, что требуется операция к чисто бакету, а не /*, лучше всегда использовать в ресурсах:

            "Resource": [
"arn:aws:s3:::some-bucket/*",
"arn:aws:s3:::some-bucket"
]
В официальной документации по #cross_account #s3_replication ошибки - пример указан с неполными правами в #bucket_policy для аккаунта источника:
...
"Action":["s3:ReplicateObject", "s3:ReplicateDelete"],
...
Полные указаны в примере предыдущего поста. Их нужно добавить и для роли источника, особенно с учётом, если нужно менять владельца.
Потому, для простоты, можно сразу давать полные s3:* - т.е. в приведённой ссылке на документацию по #cross_region #s3 #replication будет работать, если сделать так:
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "Full access for other account",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "s3:*" ,
"Resource": [
"arn:aws:s3:::replication-bucket",
"arn:aws:s3:::replication-bucket/*"
]
}
]
}
Аналогично сделать и в роли аккаунта источника:
- Effect: Allow
Action: 's3:*'
Resource: ## destination bucket
- 'arn:aws:s3:::replication-bucket'
- 'arn:aws:s3:::replication-bucket/*'
Для доступа к #s3 можно использовать #iam, #bucket_policy и #bucket_acl - что же лучше использовать?
Если коротко, то всегда лучше выбирать #IAM.
Когда не получается или нужен как раз #resource_based подход - выбираем #bucket_policy (без него не обойтись для случаев #cross_account).
Использовать ACL не стоит ни разу - это и старое и неудобное.
При использовании #cross_account #s3_replication нужно учитывать, что destination #s3 бакет должен:
- существовать на момент отработки #CloudFormation #templates
- иметь #versioning (обязательно требуется для #replication)
- быть в другом #region

Пример кода - cross-account cross-region s3 bucket replication.

Также стоит обратить внимание, что роль на репликацию создаётся в аккаунте источника (которая указывается в ReplicationConfiguration бакета), а не в аккаунте репликации, как может показаться логичным. Мы даём доступ внешнему аккаунту (с бакетом источника) к себе в аккаунте репликации.
Использование в #CloudFormation #templates автоматического получения самого нового #AMI_ID через #ssm_parameters, что было описано в посте и как это рекомендовано в блоге амазона - весьма опасная практика для #prod систем.
Дело в том, что в таком случае вы не контролируете все параметры и когда обновляете прод можете получить нежелательное обновление #AMI. Теоретически процессы должны быть выстроены так, чтобы это не влияло и где-то даже было желательным сценарием (т.к. всегда самый последний - самый защищённый), однако может привести к нежелательным задержкам просто на само обновление. Но страшней, когда порушатся временные костыли.
Потому лучше использовать #mappings для этого - пусть ручное обновление, но контролируемое. А чтобы #по-быстрому получить #latest_ami_id - можно использовать скрипты.

#AmiLinux2:

for region in $(aws ec2 describe-regions --region us-east-1 --query 'Regions[].[RegionName]' --output text | sort); \
do printf " ${region}:\n AmiId: $(aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --query 'Parameters[0].[Value]' --output text --region $region)\n" ; done

#ECS_optimized:

for region in $(aws ec2 describe-regions --region us-east-1 --query 'Regions[].[RegionName]' --output text | sort); \
do printf " ${region}:\n AmiId: $(aws ssm get-parameters --names /aws/service/ecs/optimized-ami/amazon-linux/recommended/image_id --query 'Parameters[0].[Value]' --output text --region $region)\n" ; done

Примеры кода.
Из рубрик #error + #issue. Пренеприятная проблема была получена при попытке срочного разворота важного окружения. В процессе подъёма #autoscaling_group для #ecs_cluster с несколькими рабочими нодами и сервисами на них с важным моментом - docker image лежит не в ECR, а в #private_docker_hub, то на давно отрепетированном сценарии (правда лишь с одной нодой) вылезла страннейшая ошибка - вторая (!) нода не могла загрузить контейнер. Т.е. первая грузила и успешно работала, а вторая (такая же, этот же образ) - зависала на ошибке:
STOPPED (CannotPullContainerError: API error (404): pull ac)

Не получив образ по таймауту срабатывал откат. Ошибка нерегулярная, т.к. с энной попытки получалось задеплоить и успешно работать. Либо поставить одну ноду - и тогда ни разу не было такой ошибки.
Гуглинг показал, что у такой же ошибки есть братья по разуму, где, судя по всему, такая ситуация возникала именно в связке докерхаб + новый #ecs_agent. И в данном случае он как раз был обновлён. потому наверняка это одна из причин.
После детального изучения выяснилось, что в результате, видимо, каких-то неадекватных лагов с отдачей второго образа, амазоновская команда для подключения в #autoscalig_group:
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource autoscalingGroup --region ${AWS::Region}

вылетала в ошибку и расположенный за ней код не исполнялся! И если, как в моём случае, именно после этой команды задавалась переменная ECS_ENGINE_AUTH_DATA для авторизации на докере, то, получается, она не попадала в ecs.config и агент после никак не мог получить доступ к приватному репозиторию.
Изменения последовательности команд - решило проблему. При чём важно учесть, что есть и другие команды, которые обладают таким поведением, потому важный код помещаем в начало #UserData, а проблемные - в самый конец и с учётом важности:
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource autoscalingGroup --region ${AWS::Region}
stop ecs
start ecs
В дополнение к ec2 - ещё и отличный каталог типов инстансов для #RDS - https://ec2instances.info/rds/

#info #comparison
При конструировании DNS для #s3 бакет нужно быть максимально внимательным - неоднозначное толкование официальной документации, где говорится про «you can use either of these endpoints» в реальности может давать различные проблемы.
С одной стороны, используя вариант с
.s3.amazonaws.com
даёт в результате перенаправление, а значит изначально #error #307 и лишь потом нужный файл/страницу. Это может приводить к некорректной работе критическим к такому поведению вещей. Например, когда через s3 бакеты подтягиваются конфиг-файлы #nginx, то такое поведение даст ошибку «unexpected end of file, expecting ";" or "}" in /etc/nginx/conf.d/...», т.к. получив 307 он не будет делать ещё один запрос по новому location из ответа. Потому правильно использовать именно вариант типа:
!Join ['',[!Ref Bucket, '.s3-', !Ref 'AWS::Region' , '.amazonaws.com' ]]
Однако бывает и противоположная ситуация, например, с регионом N.Virginia. Для #CloudFront #Origin (в том числе для Logging бакета) DomainName вариант bucket.s3-us-east-1.amazonaws.com даёт стабильные #error 502 для #distribution. Правильный вариант с bucket.s3.amazonaws.com:
Origins:
- DomainName: !Join ['',[!Ref Bucket, '.s3.amazonaws.com' ]]

#issue