Một vài mẹo hay khi sử dụng AWS CloudFormation

Hi everyone!
Mình là Nguyễn Văn Dương đến từ Group 1.
Hôm nay mình sẽ giới thiệu đến mọi người một số mẹo nhỏ khi sử dụng AWS CloudFormation.

Giới thiệu

CloudFormation là một service được cung cấp bởi AWS với mục đích hỗ trợ user model hoá và set up AWS resources. Vì vậy, sử dụng CloudFormation giúp user tiết kiệm thời gian quản lý resources và có thể dành nhiều thời gian hơn cho việc phát triển ứng dụng.
Bạn chỉ cần định nghĩa template và CloudFormation sẽ giúp bạn provisioning và configuring resources mà bạn đã định nghĩa.
Đối với SA hay Devops, CloudFormation là một tool vô cùng hữu dụng giúp bạn "Infrastructure as Code(IaC)". Các Cloudformation template(định dạng yaml) có thể lưu trữ và sử dụng như source code thông thường, nghĩa là bạn có thể quản lý templates bằng source code control tools(git, etc) và cũng có thể sử dụng các tool CI/CD để build/deploy infrastructure.

Giới thiệu thế là đủ rồi, sau đây mình sẽ giới thiệu một vài mẹo cơ bản khi thao tác với CloudFormation.

1. Thay vì đặt tên - hãy sử dụng tags

Có nhiều resource khi định nghĩa bằng CloudFormation không thể cập nhật hoặc thay thế khi chúng được đặt tên; Ví dụ như S3 bucket. Vì vậy, hãy để CloudFormation auto-generate tên resources khi có thể. Nếu một số resource cần phải được đặt tên để xác định, hãy dùng tags để thay thế. Sử dụng tags sẽ giúp bạn dễ dàng quản lý các resource trên AWS.
Tên resource được tạo tự động có thể được cung cấp thông qua CloudFormation output. Tuy nhiên tên resource được tạo ra tự động có thể là trở ngại khi sử dụng ở các template khác(vì nó ngẫu nhiên nên rất khó nhớ). Để giải quyết vấn đề này, bạn có thể cân nhắc sử dụng thêm SSM Parameter Store để lưu trữ chúng và gọi ra khi cần thiết.

2. Sử dụng Nested Stacks

Đối với những hệ thống lớn với số lượng resource lớn, việc định nghĩa tất cả vào trong một template là việc gần như không thể.
Do đó, việc sử dụng nested stacks giúp bạn chia nhỏ các template ra thành nhiều component. Sử dụng nested stacks có những lợi ích sau:

  • Có thể tái sử dụng các compomnents.
  • Chia nhỏ template giúp dễ dàng quản lý.
  • Có thể tránh được chuyện vượt quá giới hạn của CloudFormation template khi định nghĩa quá nhiều resource strong một template.

3. Sử dụng Parameter Metadata

Các Parameter nên được nhóm lại với nhau khi có thể để giúp người dùng dễ dàng điền các parameter nằm trong cùng một group có liên quan với nhau. Việc sử dụng Parameter Metadata chỉ thực sự có tác dụng khi sử dụng CloudFormation Console để provision các resource.
Sau đây là một ví dụ:

    Metadata: 
      AWS::CloudFormation::Interface: 
        ParameterGroups: 
          - 
            Label: 
              default: "Network Configuration"
            Parameters: 
              - VPCID
              - SubnetId
              - SecurityGroupID
          - 
            Label: 
              default: "Amazon EC2 Configuration"
            Parameters: 
              - InstanceType
              - KeyName
        ParameterLabels: 
          VPCID: 
            default: "Which VPC should this be deployed to?"

4. Sử dụng default template parameters thay vì hard-coding

Ở một số trường hợp, có những giá trị là cố định và được sự dụng duy nhất một lần trong template, tất nhiên có thể hard-code giá trị đó vào template body mà không cần phải khai báo gì cả. Tuy nhiên, việc đó có thể dẫn đến việc không hiểu rõ resource có giá trị đó là resource nào.
Hãy tưởng tượng, có một người nào đó trước bạn định nghĩa template và hard-code vào body template. Sau đó, bạn là người tiếp quản công việc đó và bạn phải hiểu rõ mọi thứ được định nghĩa trong template. Tuy nhiên vì nó được hard-code nên bạn khó mà hiểu được mục đích sử dụng cũng như giá trị đó liên quan đến resource nào, đúng không?
Chính vì thế, bạn nên sử dụng default template parameters với Parameter name, description tường minh để giải quyết vấn để ở trên. Nên nhớ, vì đó là một giá trị cố định nên AllowedValues chỉ có một giá duy nhất.
Sau đây là một ví dụ:

    CloudFrontHostedZoneId:
        Type: string
        Default: Z2FDTNDATAQYW2
        AllowedValues:
            - Z2FDTNDATAQYW2
        Description: "The Default Hosted Zone for CloudFront Distributions"

5. Sử dụng SSM Parameters

Có rất nhiều parameter type trong CloudFormation.
Trong trường hợp sau:

  • Bạn muốn sử dụng giá trị ở nhiều template mà không cần phải truyền giá trị vào khi execute template.
  • Bạn muốn đảm bảo giá trị sử dụng được lưu trữ tập trung ở một nơi khác và giá trị đó có thể được thay đổi mà không cần phải sửa template.
  • Parameters có thể thay đổi tuỳ theo environment hay account.

Thì SSM Parameters là một lựa chọn tuyệt vời.

Cú pháp:

AWS::SSM::Parameter::Value
AWS::SSM::Parameter::Value<List>
AWS::SSM::Parameter::Value
AWS::SSM::Parameter::Value
AWS::SSM::Parameter::Value<List>

Ví dụ:

Parameters:
    DevApiUrl:
        Type: AWS::SSM::Parameter::Value
        Default: '/DEV/API_URL'

Tuy nhiên, CloudFormation không support list SSM parameter(ví dụ: List<AWS::SSM::Parameter::Value<String>>).
CloudFormation cũng không support SecureString SSM Parameter.

6. Sử dụng !Sub

CloudFormation có rất nhiêu intrinsic functions như: Fn::Join, Fn::Split, Fn::Sub, Ref, etc.
Trong số đó, một intrinsic function mà mình rất hay sử dụng là Fn::Sub. Fn::Sub là một công cụ hữu ích khi bạn muốn định nghĩa một template "sạch sẽ, ưa nhìn". Fn::Sub cũng được mình sử dụng để thay thế Fn::Join ở trong nhiều trường hợp.
Sau đây, mình sẽ đưa ra ví dụ khi sử dụng Fn::JoinFn::Sub để bạn tham khảo.
Sử dụng Fn::Join thì sẽ như thế này:

!Join
        - ''
        - - 'arn:'
           - !Ref AWS::Partition
           - ':s3:::elasticbeanstalk-*-'
           - !Ref AWS::AccountId

Sử dụng Fn::Sub:

!Sub 'arn:${AWS::Partition}:s3:::elasticbeanstalk-*-${AWS::AccountId}'

Ngắn gọn hơn nhiều phải không nào?
Ngoài ra, Fn::Sub vô cùng hữu ích khi định nghĩa multi-line string trong template, ví dụ như StepFunctions DefinitionString property hay EventBridge properties.

7. Sử dụng list ARNs

Việc sử dụng Fn::Sub có thể thay thế Fn::Join hay Fn::Split trong nhiều trường hợp nhưng không phải tất cả.
Trong trường hợp sau đây, Fn::JoinFn::Split cần được sử dụng.
Bạn truyền vào một list gồm các Account Identifier:

Parameters:
    AccountIds:
        Type: CommaDelimitedList

Giá trị truyền vào:

"01234567890,1111111111,2222222222"

Bạn muốn biến nó thành list ARNs như sau:

- "arn:aws:iam::01234567890:root"
- "arn:aws:iam::11111111111:root"
- "arn:aws:iam::22222222222:root"

Sử dụng cụm thủ thuật sau đây, và điều ước thành sự thật:

!Split
    - ','
    - !Sub
        - 'arn:aws:iam::${inner}:root'
        - inner: !Join
            - ':root,arn:aws:iam::'
            - !Ref AccountIds

Chúng ta hãy nói rõ một chút nhé.

  • inner: !Join sẽ tạo ra string sau:
    '01234567890:root,awn:aws:iam::1111111111:root,arn:aws:iam::2222222222'
  • !Sub sẽ đặt kết quả của ① vào vị trí của ${inner} trong arn:aws:iam::${inner}:root, kết quả:

    'arn:aws:iam::01234567890:root,awn:aws:iam::1111111111:root,arn:aws:iam::2222222222:root'
  • !Split sẽ split kết quả của ② với separator',' và ta có kết qua như ý:
    ['arn:aws:iam::01234567890:root','awn:aws:iam::1111111111:root','arn:aws:iam::2222222222:root']

Chính vì thế, hãy luôn là người viết template khôn ngoan và lựa chọn đúng intrinsic functions nhé.

Kết luận

Trên đây là một vài mẹo khi sử dụng AWS CloudFormation. Nếu có điều gì góp ý hay có những mẹo hay khác nữa, bạn đừng ngần ngại góp ý nhé.
Cám ơn bạn rất nhiều vì đã giành thời gian đọc blog này.
See ya!

Leave a Reply

Your email address will not be published. Required fields are marked *