Dọn dẹp tài nguyên

Sau khi hoàn tất bài thực hành, chúng ta tiến hành dọn dẹp toàn bộ tài nguyên đã được tạo ra.

Hãy đọc phần này kỹ trước khi bắt đầu. Việc xoá các CloudFormation Stack là chưa đủ. Có hai nhóm tài nguyên vẫn sót lại và tiếp tục phát sinh chi phí:

  • AMI và EBS Snapshot do EC2 Image Builder tạo ra. CloudFormation không quản lý chúng, và theo tài liệu của DeleteImage, việc xoá image resource trong Image Builder cũng không xoá AMI và snapshot đi kèm.
  • Nội dung bên trong S3 logging bucket. CloudFormation không thể xoá một bucket còn dữ liệu, nên Stack pattern3-pipeline sẽ thất bại ở trạng thái DELETE_FAILED nếu bạn chưa dọn bucket.

Thứ tự thực hiện

Các Stack phụ thuộc lẫn nhau thông qua Export và Fn::ImportValue, nên bắt buộc phải xoá theo đúng thứ tự ngược lại với lúc tạo. Nếu xoá pattern3-base trước, CloudFormation sẽ từ chối vì các giá trị Export vẫn đang được Stack khác sử dụng.

Thứ tựStackGhi chú
1pattern3-automateXoá được ngay
2(không phải Stack)Xoá AMI và EBS Snapshot do Image Builder tạo
3pattern3-pipelinePhải dọn rỗng S3 bucket trước
4pattern3-appMất khoảng 5 đến 10 phút
5pattern3-baseXoá NAT Gateway, Elastic IP, VPC

Bước 1: Xoá Automation Stack

Từ CloudFormation Console, xoá Stack pattern3-automate. Hoặc dùng CLI:

aws cloudformation delete-stack --stack-name pattern3-automate --region ap-southeast-2

aws cloudformation wait stack-delete-complete --stack-name pattern3-automate --region ap-southeast-2

Bước 2: Xoá AMI và EBS Snapshot

Đây là bước quan trọng nhất về mặt chi phí. Mỗi lần pipeline chạy thành công đều sinh ra một AMI kèm một EBS Snapshot, và snapshot vẫn bị tính phí lưu trữ vô thời hạn cho đến khi bạn xoá.

  1. Liệt kê các AMI do bạn sở hữu để xác định AMI cần xoá:

    aws ec2 describe-images --owners self --region ap-southeast-2 --query "Images[].{Id:ImageId,Name:Name,Created:CreationDate}" --output table
    
  2. Với mỗi AMI, thu thập ID của các snapshot đi kèm rồi hủy đăng ký AMI:

AMI_ID=ami-xxxxxxxxxxxxxxxxx

SNAPSHOTS=$(aws ec2 describe-images --image-ids "$AMI_ID" --region ap-southeast-2 --query "Images[0].BlockDeviceMappings[].Ebs.SnapshotId" --output text)

aws ec2 deregister-image --image-id "$AMI_ID" --region ap-southeast-2

for SNAP in $SNAPSHOTS; do
  aws ec2 delete-snapshot --snapshot-id "$SNAP" --region ap-southeast-2
done
$AMI_ID = 'ami-xxxxxxxxxxxxxxxxx'

$SNAPSHOTS = (aws ec2 describe-images --image-ids $AMI_ID --region ap-southeast-2 --query "Images[0].BlockDeviceMappings[].Ebs.SnapshotId" --output text) -split '\s+'

aws ec2 deregister-image --image-id $AMI_ID --region ap-southeast-2

foreach ($SNAP in $SNAPSHOTS) {
    if ($SNAP) { aws ec2 delete-snapshot --snapshot-id $SNAP --region ap-southeast-2 }
}

Hãy hủy đăng ký AMI trước, rồi mới xoá snapshot. Làm ngược lại sẽ để lại một AMI hỏng không thể khởi chạy.

Ngoài ra, --owners self chỉ trả về AMI do chính bạn tạo, nên lệnh trên sẽ không ảnh hưởng đến các AMI công khai của Amazon. Tuy vậy, nếu tài khoản này còn AMI của công việc khác, hãy đối chiếu cột Name để chỉ xoá đúng AMI của bài thực hành.

  1. Xoá các image resource trong Image Builder để danh sách được gọn gàng.

Image Builder có hai loại ARN khác nhau, rất dễ nhầm.

LoạiDạng ARNLệnh trả về
Image version.../image/<ten-recipe>/1.0.0list-images
Image build version.../image/<ten-recipe>/1.0.0/2list-image-build-versions

1.0.0 là phiên bản của recipe, còn số cuối là lần build thứ mấy của phiên bản đó. Chạy pipeline 3 lần sẽ tạo ra 1.0.0/1, 1.0.0/2, 1.0.0/3.

Lệnh delete-image chỉ nhận image build version ARN, tức là dạng có số build ở cuối. Nếu bạn truyền ARN lấy từ list-images thì sẽ gặp lỗi:

An error occurred (InvalidParameterValueException) when calling the DeleteImage operation:
The value supplied for parameter 'imageBuildVersionArn' is not valid.
The supplied Arn is not a valid Image Builder Image Build Version Arn.

Vì vậy phải qua hai bước: lấy image version ARN trước, rồi từ đó liệt kê các build version.

# Buoc 1: lay danh sach image version ARN
aws imagebuilder list-images --owner Self --region ap-southeast-2 --query "imageVersionList[].arn" --output text

# Buoc 2: voi moi image version, liet ke cac build version roi xoa tat ca
for VER in $(aws imagebuilder list-images --owner Self --region ap-southeast-2 --query "imageVersionList[].arn" --output text); do
  for BUILD in $(aws imagebuilder list-image-build-versions --image-version-arn "$VER" --region ap-southeast-2 --query "imageSummaryList[].arn" --output text); do
    echo "Dang xoa $BUILD"
    aws imagebuilder delete-image --image-build-version-arn "$BUILD" --region ap-southeast-2
  done
done
# Buoc 1: lay danh sach image version ARN
$VERSIONS = (aws imagebuilder list-images --owner Self --region ap-southeast-2 --query "imageVersionList[].arn" --output text) -split '\s+'

# Buoc 2: voi moi image version, liet ke cac build version roi xoa tat ca
foreach ($VER in $VERSIONS) {
    if (-not $VER) { continue }
    $BUILDS = (aws imagebuilder list-image-build-versions --image-version-arn $VER --region ap-southeast-2 --query "imageSummaryList[].arn" --output text) -split '\s+'
    foreach ($BUILD in $BUILDS) {
        if (-not $BUILD) { continue }
        Write-Output "Dang xoa $BUILD"
        aws imagebuilder delete-image --image-build-version-arn $BUILD --region ap-southeast-2
    }
}

Bước 3: Xoá Pipeline Stack

  1. Lấy tên S3 logging bucket từ Output của Stack, rồi dọn rỗng bucket. Nếu bỏ qua bước dọn rỗng, Stack sẽ dừng ở trạng thái DELETE_FAILED:
BUCKET=$(aws cloudformation describe-stacks --stack-name pattern3-pipeline --region ap-southeast-2 --query "Stacks[0].Outputs[?OutputKey=='Pattern3LoggingBucketName'].OutputValue" --output text)

echo "Logging bucket: $BUCKET"

aws s3 rm "s3://${BUCKET}" --recursive --region ap-southeast-2
$BUCKET = aws cloudformation describe-stacks --stack-name pattern3-pipeline --region ap-southeast-2 --query "Stacks[0].Outputs[?OutputKey=='Pattern3LoggingBucketName'].OutputValue" --output text

Write-Output "Logging bucket: $BUCKET"

aws s3 rm "s3://$BUCKET" --recursive --region ap-southeast-2
  1. Xoá Stack:
    aws cloudformation delete-stack --stack-name pattern3-pipeline --region ap-southeast-2
    
    aws cloudformation wait stack-delete-complete --stack-name pattern3-pipeline --region ap-southeast-2
    

Nếu bạn làm phần 4 và phần 5 thủ công từ Console, hãy xoá các tài nguyên đó ở đây, trước khi sang Bước 5. CloudFormation không quản lý chúng nên xoá Stack sẽ không dọn được.

Tài nguyênLệnh xoá
Image pipelineaws imagebuilder delete-image-pipeline --image-pipeline-arn <ARN>
Image recipeaws imagebuilder delete-image-recipe --image-recipe-arn <ARN>
Componentaws imagebuilder delete-component --component-build-version-arn <ARN>
Infrastructure configurationaws imagebuilder delete-infrastructure-configuration --infrastructure-configuration-arn <ARN>
SSM Automation documentaws ssm delete-document --name pattern3-automate-CreateImage
S3 bucketaws s3 rb s3://<TEN_BUCKET> --force
Security groupaws ec2 delete-security-group --group-id <SG_ID>
IAM roleXoá instance profile, detach policy, xoá inline policy, rồi mới xoá role

Security group là cái quan trọng nhất phải xoá trước Bước 5. Nó nằm trong VPC của pattern3-base, và AWS không cho xoá một VPC nào còn security group không phải default bên trong. Bỏ sót nó thì Stack pattern3-base sẽ dừng ở DELETE_FAILED.

Xoá IAM Role bằng bốn lệnh theo đúng thứ tự:

ROLE=pattern3-recipe-instance-role

aws iam remove-role-from-instance-profile --instance-profile-name $ROLE --role-name $ROLE
aws iam delete-instance-profile --instance-profile-name $ROLE
aws iam detach-role-policy --role-name $ROLE --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
aws iam detach-role-policy --role-name $ROLE --policy-arn arn:aws:iam::aws:policy/EC2InstanceProfileForImageBuilder
aws iam delete-role-policy --role-name $ROLE --policy-name pattern3-recipe-instance-policy
aws iam delete-role --role-name $ROLE

Bước 4: Xoá Application Stack

Bước này sẽ xoá Application Load Balancer, Auto Scaling Group, Launch Template, các EC2 instance và IAM Role đi kèm.

aws cloudformation delete-stack --stack-name pattern3-app --region ap-southeast-2

aws cloudformation wait stack-delete-complete --stack-name pattern3-app --region ap-southeast-2

Quá trình mất khoảng 5 đến 10 phút vì phải chờ Auto Scaling Group hạ số lượng instance về 0 và Load Balancer hủy đăng ký các target.

Bước 5: Xoá Infrastructure Stack

Bước cuối cùng sẽ xoá NAT Gateway, Elastic IP, Internet Gateway, Subnets và VPC.

aws cloudformation delete-stack --stack-name pattern3-base --region ap-southeast-2

aws cloudformation wait stack-delete-complete --stack-name pattern3-base --region ap-southeast-2

NAT Gateway là tài nguyên tốn phí nhiều nhất của bài thực hành này, tính theo giờ kể cả khi không có lưu lượng đi qua. Hãy chắc chắn Stack pattern3-base đã ở trạng thái DELETE_COMPLETE.

Xác minh đã dọn sạch

Chạy các lệnh sau, tất cả đều phải trả về kết quả rỗng:

# Khong con Stack nao cua bai thuc hanh
aws cloudformation describe-stacks --region ap-southeast-2 --query "Stacks[?starts_with(StackName, 'pattern3')].{Name:StackName,Status:StackStatus}" --output table

# Khong con AMI nao do minh so huu
aws ec2 describe-images --owners self --region ap-southeast-2 --query "Images[].ImageId" --output text

# Khong con EBS Snapshot nao do minh so huu
aws ec2 describe-snapshots --owner-ids self --region ap-southeast-2 --query "Snapshots[].SnapshotId" --output text

# Khong con NAT Gateway nao dang hoat dong
aws ec2 describe-nat-gateways --region ap-southeast-2 --filter "Name=state,Values=available,pending" --query "NatGateways[].NatGatewayId" --output text

# Khong con Elastic IP nao chua duoc gan
aws ec2 describe-addresses --region ap-southeast-2 --query "Addresses[?AssociationId==null].PublicIp" --output text

Sau khi dọn xong, bạn nên kiểm tra lại AWS Cost Explorer hoặc Billing Dashboard sau 24 giờ để chắc chắn không còn tài nguyên nào phát sinh chi phí.

Chúc mừng bạn đã hoàn thành bài thực hành.