구성도를 보면 ECS 클러스터가 public, private subnet을 걸쳐 2개가 있는데, AWS ECS 인프라 구축(1) 편에서 간단히 테스트하기 위해 public subnet에 ecs 클러스터를 배치했다. 추후 public subnet의 ecs 클러스터는 없어질 예정이며, NLB를 통해 private subnet으로 요청하게 만들 예정이다.
위 VPC 초기화와 동시에 NAT 게이트웨이가 2개 생성되고, elastic ip 주소가 설정된다.
release-me-subnet-private1-ap-northeast-2a -> release-me-subnet-private3-ap-northeast-2a
release-me-subnet-private2-ap-northeast-2b -> release-me-subnet-private4-ap-northeast-2b
서브넷 연결을 추가한다.
public subnet
inbound: 80, 443
outbound: 80, 443, service private subnet(7000 ~ 8000)
service private subnet
inbound: 서비스 애플리케이션 포트(7000 ~ 8000)
outbound: database private subnet(3306, 6379)
database private subnet
inbound: 3306, 6379
위와같이 필요한 리포지토리를 만들고 이미지를 푸시해야한다.
'푸시 명령 보기'를 누르면
어떤 명령어로 이미지를 푸시할 수 있는지 나오는데,
An error occurred (AccessDeniedException) when calling the GetAuthorizationToken operation: User: arn:aws:iam::192219970851:user/release-me is not authorized to perform: ecr:GetAuthorizationToken on resource: * because no identity-based policy allows the ecr:GetAuthorizationToken action
Error: Cannot perform an interactive login from a non TTY device
위와같은 에러가 발생하면 iam에서 AmazonEC2ContainerRegistryReadOnly 권한을 추가하면 된다.
새로운 계정을 만들고 서비스에 필요한 다른 권한도 미리 추가했다.
aws configure 로 cli login 후 푸시명령을 실행한다.
macos에서 빌드하는 경우 --platform=linux/amd64 옵션을 넣어준다.
docker build --platform=linux/amd64 -t release-me-nginx .
service pivate subnet에 대한 네트워크 설정
클러스터 생성 후 태스크를 정의한다.
https://docs.aws.amazon.com/ko_kr/AmazonECS/latest/developerguide/task_execution_IAM_role.html#create-task-execution-role
위 링크를 참고해 태스크 역할을 만들고 태스크 정의를 진행한다.
많은 정책이 부여되어있는데, 이후 발생한 권한문제를 해결하다보니 추가되었다.
여기서부터 많은 에러가 발생했다.
ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve ecr registry auth: service call has been retried 3 time(s): RequestError: send request failed caused by: Post "https://api.ecr.ap-northeast-2.amazonaws.com/": dial tcp 54.180.184.245:443: i/o timeout. Please check your task network configuration.
에러가 발생하며 배포실패되는 경우가 있는데, ecr에서 이미지를 pull 해오는데 실패해서 발생하는 에러로 보인다.
NACL은 모든 트래픽을 허용하고있고, internet gateway에 라우팅이 정상적으로 되어있었다.
알아보니 일반적으로 인스턴스나 컨테이너에 퍼블릭 IP 주소가 할당되어야 인터넷에 연결이 가능하다고한다.
네트워킹의 퍼블릭 IP를 끄고 배포하면 실패하고, 켜고 배포하면 성공하는것을 확인했다.하지만 위와같이 배포하는건 원하는 방식이 아니다. public subnet에 있는 모든 컨테이너가 public하게 노출되는것은 좋지 않다고 생각했고, ELB를 타고 요청이 들어와야한다. 즉, public subnet에 배포된 컨테이너들은 public ip를 가져서는 안된다.
ECR, S3등은 VPC 외부에 있는 서비스이므로, public ip 주소를 받지않은 컨테이너는 직접적으로 접근할 수 없는상태였다.Fargate의 경우, 위의 모든 조건들이 충족되더라도 "Auto-assign Public IP" 옵션이 꺼져 있으면 인터넷에 연결할 수 없습니다. 이는 Fargate가 "awsvpc" 네트워크 모드를 사용하기 때문입니다. "awsvpc" 네트워크 모드에서는 각 Fargate 작업에 대해 고유한 네트워크 인터페이스가 생성되며, 이 네트워크 인터페이스에 퍼블릭 IP가 할당되지 않으면 인터넷에 연결할 수 없습니다.
따라서 Fargate 작업에서 ECR에 접근하려면, 작업의 "Auto-assign Public IP" 옵션을 활성화하거나, VPC에서 NAT 게이트웨이를 사용하거나, AWS PrivateLink를 사용해야 합니다.처음에는 NAT 게이트웨이 사용을 고려했는데 AWS PrivateLink 가 이 상황에 특화된 해결책인것 같아서 사용해보려고한다.
AWS PrivateLink를 사용해 VPC 내부에서 직접 ECR, S3 등의 서비스에 접근할 수 있다고한다. 이를위해 ECR, S3에 대한 VPC 엔드포인트를 생성하고 Fargate 작업이 실행되는 subnet에 이 엔드포인트를 연결해야한다.
외부 인터넷을 이용하지 않아 데이터 전송비용도 들지않고, 보안이 향상되는 이점이 있어 더 좋은것같다.
VPC - 엔드포인트 메뉴를 타고 들어오면 엔드포인트를 생성할 수 있다.
위와같이 필요한 서비스에 대한 엔드포인트를 생성한다.
처음에는 ecr.dkr만 생성했다. ecr에 대한 접근권한이 없어서 이미지를 받아오지 못했다고 생각했는데 추후 발생하는 에러들을 해결하다보니 다른 엔드포인트들도 추가해야했다.
ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve ecr registry auth: service call has been retried 3 time(s): RequestError: send request failed caused by: Post "https://api.ecr.ap-northeast-2.amazonaws.com/": dial tcp 54.180.184.245:443: i/o timeout. Please check your task network configuration.
하지만 여전히 위 에러가 발생했다.
단순히 엔드포인트만 만들면 되는게 아니라 IAM에서 권한을 부여해야했다. ecr 정책을 만들고 역할에 추가했다.
당시에는 위에서 체크된 정책만 부여했다. 하지만, 추후 발생한 문제 해결을 위해 체크되지 않은 정책들을 추가로 부여했다.
20231122 다른 프로젝트 진행 중 어떤 엔트포인트가 필요한지에 대한 기록이 없어 이미지 첨부.
ecr 관련 엔드포인트는 443 인바운드 허용해야한다. 엔드포인트도 보안그룹 설정을 반드시 확인해야한다.
CannotPullContainerError: ref pull has been retried 5 time(s): failed to copy: httpReadSeeker: failed open: failed to do request: Get "https://prod-ap-northeast-2-starport-layer-bucket.s3.ap-northeast-2.amazonaws.com/46fda4-192219970851-2cc4d5c7-4239-261e-453f-036a3ea7dcab/b2c26cbf-5111-4dcb-a0c0-fc6261c6e08e?X-Amz-Security-Token=IQoJb3JpZ2luX2VjEDQaDmF...
권한을 부여했는데 다른 에러가 발생했다. CannotPullContainerError 에러는 보통 컨테이너 이미지를 끌어올리는 데 실패했을 때 발생한다고 한다. 에러내용에 s3 버킷에서 GET을 실패했다는 것 같은데, S3에서 Fargate 작업을 위한 이미지를 끌어올리려고 할 때 발생한 것으로 보인다. ECR에서 이미지를 가져오는게 아니었나? ECR에 저장되는 이미지가 내부적으로는 S3에 저장되는건가? s3에 들어가보니 관련데이터는 찾을 수 없었다.
결국 정확한 원인은 찾지 못하고, s3 접근권한을 부여하기로했다.
관련 IAM역할을 추가로 부여하고 s3에 대한 새로운 EndPoint(private link)를 생성했다.
ResourceInitializationError: failed to validate logger args: : signal: killed
또 새로운 에러가 발생했다. 로깅관련 에러로 생각되어서, 작업정의 로깅정책을 보니 CloudWatch를 사용해 로깅하고있다. 부여된 역할에 CloudWatch 권한이 없어서 그런가 싶어 모든 권한을 부여했지만 여전히 에러는 발생했고, 작업정의와 서비스생성에 CloudWatch 로깅옵션을 해제해야 배포되었다. 하지만 로깅없이 서비스 할 수 없기에 문제를 해결해야했다.
작업정의에 로깅은 해제하고, 서비스 생성에 로깅체크 -> 실패
작업정의에 로깅을 체크하고, 서비스 생성에 로깅해제 -> 실패
작업정의에 로깅을 체크하고, 서비스 생성에 로기체크 -> 실패
즉, 어떤 경우든 로깅하면 실패했다.
클라우드 워치에 로그그룹을 만들지 않아서 그런가 싶어서 수동으로 로그그룹을 만들어 테스트해봤지만 해결되지않았다.
그룹 없으면 자동생성 가능하게 권한도 부여되어있다.그러던 중 stackoverflow 에서
Fargate 서비스가 CloudWatch api 끝점에 연결할 수 없는 경우 오류가 나타납니다. 인터넷에 액세스할 수 없는 프라이빗 서브넷에서 실행 중인 fargate가 있는 경우 이런 일이 발생할 수 있습니다. 프라이빗 서브넷에 CloudWatch 로그 엔드포인트를 추가하거나 인터넷 연결을 추가할 수 있습니다.
라는 게시물을 봤다.
엔드포인트 생성 가능한지 확인해봐야겠다.
logs 엔드포인트를 생성했고, 성공적으로 배포했다.
로드밸런서로 접근이 가능했고,
태스크에 퍼블릭 IP역시 할당되지 않았다.
대부분의 문제는 ip를 할당하지 않고 컨테이너를 배포할 때 엔드포인트를 정의하지 않거나 iam 역할을 부여하지 않아서 발생했다.
얼마나 많은 시행착오를 경험하셨을지 상상이 안되네요 고맙습니다.