본문으로 바로가기

[DevOps] atlantis + infracost 구성하기

category IT/DevOps 2023. 11. 9. 20:00
반응형

infracost 란?

Infracost는 Terraform 코드 변경 사항을 스캔하여 리소스가 생성되기 전에 간단하게 예정치를 확인할 수 있는 솔루션입니다.

infracost는 community버전을 무료로 제공해주고 있으며 Cloud 버전은 유료 버전으로 비용 대시보드, 알람, 레포트 등등 추가 서비스를 제공해주고 있습니다. 비용은 별도 연락을 요청하는것 보니 list price는 아닌것 같습니다.

infracost 구성해보기

infracost는 atlantis와 찰떡궁합인 서비스 입니다. atlantis 를 사용하여 terraform plan을 할때 3가지 방법을 제공합니다.

  • append-to-atlantis-comments
  • combined-infracost-comment
  • multiple-infracost-comments

이 세가지 방법중에 저는 첫번째 방법으로 아래와 같이 구축해보았습니다.

append-to-atlantis-comments

해당 방법은 Infracost를 Atlantis와 함께 사용할 수 있는 방법을 보여줍니다. 아래 예시처럼 Infracost 예상 비용은 Atlantis 출력 하단에 추가됩니다.

  • infracost 다운로드 및 설치
# Downloads the CLI based on your OS/arch and puts it in /usr/local/bin
curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh

infracost --version # Should show 0.10.13
  • infracost API key 획득 하기

infracost 설치 이후 API key를 획득해야 합니다. 키 획득을 위해 아래와 같이 명령어를 입력하면 로그인을 할 수 있는 url이 주어집니다.

infracost auth login

아래와 같이 출력되며 https://dashboard.infracost.io.... 와 같이 생성된 url에 접근하여 google 또는 github 계정으로 로그인합니다.

We're redirecting you to our log in page, please complete that,
and return here to continue using Infracost.

If the redirect doesn't work, use this URL:

https://dashboard.infracost.io/login?cli_port=xxxx&cli_state=xxxxxxx&cli_version=v0.10.12&os=linux&utm_source=cli

Waiting...

xdg-open: no method available for opening 'https://dashboard.infracost.io/login?cli_port=38846&cli_state=xxxxx&cli_version=v0.10.12&os=linux&utm_source=cli'

로그인 이후에 1번째 2번째 단계만 진행하면 api key가 자동으로 로컬 pc 클립보드에 저장이 됩니다. 혹시 모르니 메모장같은 곳에 저장해두고 infracost가 설치되어있는 곳에 환경변수로 아래와 같이 등록합니다.

export INFRACOST_API_KEY=<your-infracost-api-token>

  • atlantis 의 repos.yaml 수정
repos:
  - id: /.*/
    workflow: terraform-infracost
workflows:
  terraform-infracost:
    plan:
      steps:
        - init
        - plan
        - show # this writes the plan JSON to $SHOWFILE
        - env:
            name: INFRACOST_OUTPUT
            command: 'echo "/tmp/$BASE_REPO_OWNER-$BASE_REPO_NAME-$PULL_NUM-$WORKSPACE-${REPO_REL_DIR//\//-}-infracost.json"'
        # Run Infracost breakdown and save to a tempfile, namespaced by this project, PR, workspace and dir
        - run: |
            infracost breakdown --path=$SHOWFILE \
                                --format=json \
                                --log-level=info \
                                --out-file=$INFRACOST_OUTPUT \
                                --project-name=$REPO_REL_DIR
        - run: |
            # Read the breakdown JSON and get costs using jq.
            # Note jq comes as standard as part of infracost-atlantis Docker images. If you are using the base atlantis
            # image you'll need to manually install jq. e.g:
            # curl https://stedolan.github.io/jq/download/linux64/jq > /usr/local/bin/jq; chmod +x /usr/local/bin/jq
            past_total_monthly_cost=$(cat $INFRACOST_OUTPUT | jq -r "(.pastTotalMonthlyCost // 0) | tonumber")
            total_monthly_cost=$(cat $INFRACOST_OUTPUT | jq -r "(.totalMonthlyCost // 0) | tonumber")
            diff_total_monthly_cost=$(cat $INFRACOST_OUTPUT | jq -r "(.diffTotalMonthlyCost // 0) | tonumber")

            currency=$(cat $INFRACOST_OUTPUT | jq -r '.currency | select (.!=null)')
            if [ "$currency" = "" ] || [ "$currency" = "USD" ]; then
              currency="$"
            elif [ "$currency" = "EUR" ]; then
              currency="€"
            elif [ "$currency" = "GBP" ]; then
              currency="£"
            else
              currency="$currency " # Space is needed so output is "INR 123"
            fi

            # Run infracost output to get the diff output
            diff_output=$(infracost output --no-color --format diff --show-skipped --path=$INFRACOST_OUTPUT)

            change_symbol () {
              local old=$1
              local new=$2

              local change_symbol="+"
              if [ "$(echo "$new < $old" | bc -l)" = 1 ]; then
                change_symbol=""
              fi

              printf "%s" "$change_symbol"
            }

            percent_display () {
              local old=$1
              local new=$2

              local percent
              local sym

              percent=$(calculate_percentage "$old" "$new")
              sym=$(change_symbol "$old" "$new")

              local s=""
              if [ -n "$percent" ]; then
                s="$(printf "%.0f" "$percent")"
                s=" ($sym$s%%)"
              fi

              printf "%s" "$s"
            }

            calculate_percentage () {
              local old=$1
              local new=$2

              local percent=""

              # If both old and new costs are greater than 0
              if [ "$(echo "$old > 0" | bc -l)" = 1 ] && [ "$(echo "$new > 0" | bc -l)" = 1 ]; then
                percent="$(echo "scale=6; $new / $old * 100 - 100" | bc)"
              fi

              # If both old and new costs are less than or equal to 0
              if [ "$(echo "$old <= 0" | bc -l)" = 1 ] && [ "$(echo "$new <= 0" | bc -l)" = 1 ]; then
                percent="0"
              fi

              printf "%s" "$percent"
            }

            format_cost () {
              cost=$1

              if [ -z "$cost" ] || [ "$cost" = "null" ]; then
                echo "-"
              elif [ "$(echo "$cost < 100" | bc -l)" = 1 ]; then
                printf "$currency%0.2f" "$cost"
              else
                printf "$currency%0.0f" "$cost"
              fi
            }

            percent=$(percent_display "$past_total_monthly_cost" "$total_monthly_cost" | sed "s/%/%%/g")

            change_word="increase"
            if [ "$(echo "$past_total_monthly_cost > $total_monthly_cost" | bc -l)" = 1 ]; then
              change_word="decrease"
            fi

            msg="##### Infracost estimate #####"
            msg="${msg}\n\n"
            msg="${msg}Monthly cost will $change_word by $(format_cost $diff_total_monthly_cost)$percent\n"
            msg="${msg}\n"
            msg="${msg}Previous monthly cost: $(format_cost $past_total_monthly_cost)\n"
            msg="${msg}New monthly cost: $(format_cost $total_monthly_cost)\n"
            msg="${msg}\n"
            msg="${msg}Infracost output:\n"
            msg="${msg}\n"
            msg="${msg}$(echo "$diff_output" | sed 's/^/    /' | sed "s/%/%%/g")\n"

            printf "$msg"

만약 terragrunt를 같이 사용하고 계시면 아래 부분을 수정해야합니다. 아래 init, plan, show는 atlantis 내부에 built in 되어있는 명령어로 일부 옵션이 생략되어있기 때문입니다.

steps:
  - init
  - plan
  - show # this writes the plan JSON to $SHOWFILE

아래와 같이 수정해주시면 됩니다.

steps:
  - run: terragrunt init -no-color
  - run: terragrunt plan -input=false -out=$PLANFILE -no-color
  - run: terragrunt show -json $PLANFILE > $SHOWFILE -no-color

자 그럼 git pr을 올려서 atlantis plan을 해볼까요? 짜잔~ 아래와 같이 예상금액이 추가 ouput으로 표시됩니다.

참고

반응형