dev-ops/aws

[AWS] EventBridge Scheduler와 Lambda로 개발 서버 비용 절감하기

heemang_e 2025. 5. 6. 02:13

 

1. 서론

개발 서버용 EC2 인스턴스는 운영 서버와 달리 24시간 가동이 불필요하다. 운영 서버는 실제 사용자에게 서비스를 제공하는 인프라이므로 항상 안정적으로 동작해야 하지만, 개발 서버는 개발자가 코드를 작성하고 테스트하기 위한 용도로만 사용되기 때문이다. 개발자는 주로 업무 시간 내에만 작업을 수행하므로 업무 시간 외에는 EC2 인스턴스에 접근하지 않는다.

예를 들어, 업무 시간이 10:00~19:00라면 해당 시간에만 개발 서버 인스턴스가 가동되도록 하고, 그 외 시간에는 중지하면 된다. 당연히 주말에는 업무 시간이 아니므로 24시간 중지 상태를 유지해도 된다.

 

회사 업무 중 우선순위가 높은 작업 중 하나는 AWS 비용 절감이다. 우리 회사는 메가존 클라우드의 Hyper Billing 서비스를 사용하므로 AWS Cost Explorer에서 직접 비용을 확인할 수 없다. 따라서 메가존 클라우드를 통해 지난 2개월간의 비용을 분석하였는데 EC2 인스턴스 비용이 압도적으로 높았다.

따라서 EC2 인스턴스 비용 절감을 최우선으로 생각하여 유휴 시간에도 running 상태를 유지하는 개발 서버 인스턴스의 비용을 줄이는 작업을 수행하였다. 업무 시간에만 인스턴스가 running 상태가 되고, 업무 시간 외에는 stopped 상태가 되도록 구현했다. 또한 필요시 업무 시간 외에도 인스턴스를 수동으로 실행할 수 있는 방법도 마련했다.

이러한 방식으로 약 58%의 비용을 절감할 수 있었다. 비용 절감을 할 수 있었던 방법에 대해서 공유하고자 게시물을 작성한다.

 

2. 업무 시간에만 EC2 인스턴스를 가동하기

2-1. 작업 프로세스

flowchart TD
  A1[EC2 인스턴스 준비 완료] --> A2[UserData 스크립트 적용 완료]
  A2 --> A3[stopped → running 시 UserData로 앱 자동 실행]

  subgraph 자동화 프로세스
    B1[EventBridge Scheduler]
    B2[AWS Lambda 함수]
    B3[EC2 인스턴스 Start API 호출]
    B1 --> B2 --> B3
  end

  subgraph 수동 프로세스
    C1[Bash 스크립트 실행]
    C2[AWS CLI로 EC2 인스턴스 Start 요청]
    C1 --> C2
  end

  A3 --> B1
  A3 --> C1

  B3 --> D1[EC2 인스턴스 running]
  C2 --> D1

  D1 --> D2[UserData 스크립트 재실행 → 앱 정상 구동]

  subgraph 야간/주말 처리
    E1[EventBridge Scheduler]
    E2[AWS Lambda 함수]
    E3[EC2 인스턴스 Stop API 호출]
    E1 --> E2 --> E3
    E3 --> E4[EC2 인스턴스 stopped]
  end

  %% 시간 설정 추가 섹션
  subgraph 시간 설정
    S1[인스턴스 시작 시간: 08:00]
    S2[인스턴스 종료 시간: 22:00]
  end

 

2-2. EC2 인스턴스에 태그 추가

개발 서버로 사용 중인 인스턴스는 "[DEV]" 접두어로 식별한다(예: "[DEV] Officemail").
아래 쉘 스크립트는 이름에 "[DEV]"가 포함된 모든 인스턴스에 Environment=dev 태그를 추가한다. 이 태그를 기준으로 인스턴스 자동 시작/종료 스케줄러를 구현한다.

#!/usr/bin/env bash
set -euo pipefail

REGION=ap-northeast-2

INSTANCE_IDS=(
  $(aws ec2 describe-instances \
    --region "$REGION" \
    --query 'Reservations[].Instances[?starts_with(Tags[?Key==`Name`]|[0].Value, `[DEV]`)].InstanceId' \
    --output text)
)

for id in "${INSTANCE_IDS[@]}"; do
  aws ec2 create-tags \
    --region "$REGION" \
    --resources "$id" \
    --tags Key=Environment,Value=dev
done

 

2-3. 인스턴스 시작/종료를 위한 AWS Lambda 함수 구현

나는 TypeScript 기반의 Pulumi IaC를 사용하여 AWS 인프라를 관리하고 있다. 아래는 인스턴스 상태를 제어하기 위한 Lambda 함수 코드이다. Lambda는 TypeScript를 지원하지 않기 때문에 JavaScript로 컴파일해야 한다. 아래는 TS -> JS로 컴파일하는 방법이다.

$ npx esbuild index.ts --bundle --platform=node --target=node18 --minify --outfile=index.js

 

다음은 stopped 상태의 인스턴스를 running 상태로 변경하는 TypeScript 코드이다. 위 명령어를 통해 TS -> JS로 컴파일 후에 Lambda 함수로 등록한다. 

import { EC2Client, StartInstancesCommand } from "@aws-sdk/client-ec2";
import { getEc2InstancesByEnvironmentTag } from "../../ec2/ec2InstanceService";
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";

// 필요한 IAM 권한:
// ec2:DescribeInstances, ec2:StartInstances

// Lambda 핸들러 (stopped 상태의 인스턴스를 running 상태로 변경)
export const startStoppedEc2InstancesHandler = async (event: any = {}): Promise<void> => {
  const environment = process.env.ENVIRONMENT || "test";

  if (environment === "prod") {
    console.warn("prod 환경에서는 인스턴스 시작 작업을 실행하지 않습니다.");
    return;
  }

  const instances = await getEc2InstancesByEnvironmentTag(environment);

  const stoppedInstances = instances.filter(inst => inst.state === "stopped");

  if (stoppedInstances.length === 0) {
    console.log("stopped 상태의 인스턴스가 없습니다.");
    return;
  }

  const client = new EC2Client({});
  const instanceIds = stoppedInstances.map(inst => inst.instanceId);
  const command = new StartInstancesCommand({ InstanceIds: instanceIds });
  const result = await client.send(command);

  const sts = new STSClient({});
  const identity = await sts.send(new GetCallerIdentityCommand({}));

  console.log(`[DEBUG] 실행한 IAM User: ${identity.Arn}`);
  console.log(`[DEBUG] 다음 인스턴스를 시작했습니다: ${instanceIds.join(", ")}`);
  console.log("[DEBUG] StartInstances 결과:", JSON.stringify(result));
};

 

다음은 running 상태의 인스턴스를 stopped 상태로 변경하는 Lambda 함수이다. 위와 마찬가지로 운영 서버(prod)를 대상으로 작업하지 못하도록 방어 코드를 추가했다.

import { EC2Client, StopInstancesCommand } from "@aws-sdk/client-ec2";
import { getEc2InstancesByEnvironmentTag } from "../../ec2/ec2InstanceService";
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";

// 필요한 IAM 권한:
// ec2:DescribeInstances, ec2:StopInstances

// Lambda 핸들러 (running 상태의 인스턴스를 stopped 상태로 변경)
export const stopRunningEc2InstancesHandler = async (event: any = {}): Promise<void> => {
  const environment = process.env.ENVIRONMENT || "test";

  if (environment === "prod") {
    console.warn("prod 환경에서는 인스턴스 중지 작업을 실행하지 않습니다.");
    return;
  }

  const instances = await getEc2InstancesByEnvironmentTag(environment);

  const runningInstances = instances.filter(inst => inst.state === "running");

  if (runningInstances.length === 0) {
    console.log("running 상태의 인스턴스가 없습니다.");
    return;
  }

  const client = new EC2Client({});
  const instanceIds = runningInstances.map(inst => inst.instanceId);
  const command = new StopInstancesCommand({ InstanceIds: instanceIds });
  const result = await client.send(command);

  const sts = new STSClient({});
  const identity = await sts.send(new GetCallerIdentityCommand({}));

  console.log(`[DEBUG] 실행한 IAM User: ${identity.Arn}`);
  console.log(`[DEBUG] 다음 인스턴스를 중지했습니다: ${instanceIds.join(", ")}`);
  console.log("[DEBUG] StopInstances 결과:", JSON.stringify(result));
};

 

Lambda 함수가 EC2 인스턴스 조회 및 시작/중지 그리고 태그 정보를 조회하기 위해 다음 권한들이 필요하다.

  • ec2:DescribeInstances: EC2 인스턴스의 메타데이터를 조회할 수 있는 권한
  • ec2:StartInstances: EC2 인스턴스를 시작할 수 있는 권한
  • ec2:StopInstances: EC2 인스턴스를 중지할 수 있는 권한
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Action": [
				"ec2:DescribeInstances",
				"ec2:StartInstances",
				"ec2:StopInstances"
			],
			"Effect": "Allow",
			"Resource": "*"
		}
	]
}

 

2-4. Amazon EventBridge Scheduler 설정

업무 시간에 맞춰 인스턴스를 자동으로 시작/종료하는 스케줄러를 등록하기 위해 EventBridge Scheduler를 사용한다.

 

Scheduler가 Lambda 함수를 호출할 수 있도록 다음 권한을 가진 IAM 역할이 필요하다.

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Action": "lambda:InvokeFunction",
			"Effect": "Allow",
			"Resource": "*"
		}
	]
}

Pulumi IaC에서는 다음 코드를 통해 Scheduler에게 Lambda 함수를 invoke 할 수 있는 권한을 포함하는 Role을 생성할 수 있다.

const schedulerRole = new aws.iam.Role("ec2SchedulerSchedulerRole", {
    name: `ec2SchedulerSchedulerRole-${environment}`,
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [
            {
                Effect: "Allow",
                Principal: {
                    Service: [
                        "scheduler.amazonaws.com"
                    ]
                },
                Action: "sts:AssumeRole"
            }   
        ]
    }),
    description: "Role for EC2 start/stop scheduler",
});
new aws.iam.RolePolicy("ec2SchedulerSchedulerPolicy", {
    name: `ec2SchedulerSchedulerPolicy-${environment}`,
    role: schedulerRole.id,
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [
            {
                Effect: "Allow",
                Action: "lambda:InvokeFunction",
                Resource: "*"
            }
        ]
    }),
});

 

다음은 EventBridge Scheduler를 생성하는 Pulumi 함수이다. 이 함수를 index.ts에서 import하여 사용하면 된다.

import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

/**
 * EventBridge Scheduler를 생성하는 함수
 * @param name 스케줄러 리소스 이름
 * @param scheduleExpression cron 등 스케줄 표현식
 * @param targetArn 트리거할 Lambda 함수의 ARN
 * @param roleArn EventBridge가 사용할 IAM Role의 ARN
 * @param description 스케줄러 설명(영문)
 */
export function createEventBridgeScheduler(
  name: string,
  scheduleExpression: string,
  targetArn: pulumi.Output<string>,
  roleArn: pulumi.Output<string>,
  description: string
) {
  return new aws.scheduler.Schedule(name, {
    name: name,
    flexibleTimeWindow: { mode: "OFF" },
    scheduleExpression,
    target: {
      arn: targetArn,
      roleArn: roleArn,
    },
    description,
  });
}

 

startInstancesScheduler의 경우 stopped → running 상태를 만드는 Lambda 함수를 호출하는 EventBridge Scheduler이고, stopInstancesScheduler는 running → stopped 상태로 만드는 Scheduler이다. 이 Scheduler가 Lambda 함수를 호출하기 위해서는 lambda:InvokeFunction 권한이 필요하므로 Scheduler 생성 시점에 schedulerRole.arn을 넘겨준다.

// EventBridge Scheduler로 Lambda 트리거
const startInstancesScheduler = createEventBridgeScheduler(
  `startInstancesScheduler-${environment}`,
  startInstancesCron,
  startInstancesLambda.arn,
  schedulerRole.arn,
  "Start EC2 instances with Environment tag at scheduled time"
);

const stopInstancesScheduler = createEventBridgeScheduler(
  `stopInstancesScheduler-${environment}`,
  stopInstancesCron,
  stopInstancesLambda.arn,
  schedulerRole.arn,
  "Stop EC2 instances with Environment tag at scheduled time"
);

 

EventBridge Scheduler를 생성할 때, Schduler가 동작해야 하는 주기를 등록해야 한다. 나는 업무 시간을 8시부터 23시까지 하고 나머지는 업무 외 시간으로 결정하였다. Scheduler에 cron을 등록할 때 UTC 기준으로 등록해야 한다. 이 cron 값은 위에서 Scheduler를 생성할 때 주입하였다.

startInstancesCron: "cron(0 23 * * ? *)" # KST 오전 8시, UTC 오후 11시(23시)
stopInstancesCron: "cron(0 13 * * ? *)"  # KST 오후 10시, UTC 오후 1시(13시)

 

3. stopped → running 상태가될 때 UserData 스크립트 실행하기

EC2 인스턴스의 UserData 스크립트는 기본적으로 인스턴스 최초 부팅 시에만 실행된다. 그러나 나는 인스턴스가 매번 stopped → running 상태로 변경될 때마다 UserData 스크립트가 동작하여 API 요청을 받을 수 있는 상태로 만들고자 한다.

 

이를 위해 https://kim-dragon.tistory.com/222 방법을 참고했다. UserData 스크립트가 매번 부팅 시 실행되도록 하려면 스크립트를 /var/lib/cloud/scripts/per-boot/ 또는 /etc/rc.local에 복사하거나, cloud-init 설정을 조정하면 된다.

 

다음은 매번 부팅 시 실행되는 UserData 스크립트 템플릿이다.

아래 템플릿에서 ⓷ 부분에 UserData 스트립트를 작성하면 된다.

Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0

--//                                       ##--------------- ①
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"

#cloud-config
cloud_final_modules:
- [scripts-user, always]

--//                                       ##--------------- ②                                     
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"

#!/bin/bash
/bin/echo "Hello World" >> /tmp/testfile.txt  // --------------- ⓷

 

여기까지 설정하면 다음과 같은 자동화 flow가 구현된다.

flowchart TD
  A1[EC2 인스턴스 준비 완료] --> A2[UserData 스크립트 적용 완료]
  A2 --> A3[stopped → running 시 UserData로 앱 자동 실행]

  subgraph 자동화 프로세스
    B1[EventBridge Scheduler]
    B2[AWS Lambda 함수]
    B3[EC2 인스턴스 Start API 호출]
    B1 --> B2 --> B3
  end


  A3 --> B1

  B3 --> D1[EC2 인스턴스 running]

  D1 --> D2[UserData 스크립트 재실행 → 앱 정상 구동]

  subgraph 야간/주말 처리
    E1[EventBridge Scheduler]
    E2[AWS Lambda 함수]
    E3[EC2 인스턴스 Stop API 호출]
    E1 --> E2 --> E3
    E3 --> E4[EC2 인스턴스 stopped]
  end

  %% 시간 설정 추가 섹션
  subgraph 시간 설정
    S1[인스턴스 시작 시간: 08:00]
    S2[인스턴스 종료 시간: 22:00]
  end

 

4. 수동으로 인스턴스를 실행할 수 있도록 하기

업무 시간 외에도 필요시 인스턴스를 수동으로 실행할 수 있어야 한가. 특히 AWS 콘솔에 직접 접근할 수 없는 클라이언트 팀을 위해 별도의 제어 방법이 필요하다.

이를 위해 다음과 같은 제한된 권한을 가진 IAM 역할을 생성했다.

  • EC2 인스턴스 목록 조회
  • Environment=dev 태그가 있는 인스턴스만 시작/종료
  • MFA 설정 및 Access Key 관리
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ec2:DescribeInstances"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "DescribeAccess"
        },
        {
            "Condition": {
                "StringEquals": {
                    "ec2:ResourceTag/Environment": "dev"
                }
            },
            "Action": [
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "StartStopDevInstancesOnly"
        },
        {
            "Action": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:DeactivateMFADevice",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "iam:ListUsers",
                "iam:ListAccessKeys",
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:UpdateAccessKey",
                "iam:GetUser"
            ],
            "Resource": "arn:aws:iam::037684266277:user/*",
            "Effect": "Allow",
            "Sid": "AllowUserMFAAndAccessKeySelfManagement"
        }
    ]
}

 

AWS CLI 설정 방법은 다음과 같다.

// AWS CLI 설치
$ brew install awscli

// AWS Profile 등록
$ aws configure --profile {profile-name}

AWS Access Key ID [None]: (여기에 Access Key 입력)
AWS Secret Access Key [None]: (여기에 Secret Key 입력)
Default region name [None]: ap-northeast-2
Default output format [None]: json

// AWS Profile 지정
$ export AWS_PROFILE={profile-name}

 

발급된 IAM User 계정으로 쉘 스크립트를 통해 인스턴스 조회 및 시작/중지할 수 있다.

  • 인스턴스 목록 조회: ./scripts/dev-instance-manager.sh list
  • 인스턴스 시작: ./scripts/dev-instance-manager.sh start {instance_id}
  • 인스턴스 종료: ./scripts/dev-instance-manager.sh stop {instance_id}
#!/usr/bin/env bash
set -euo pipefail

function fetch_instances() {
  local aws_output
  if ! aws_output=$(aws ec2 describe-instances \
    --region ap-northeast-2 \
    --filters "Name=tag:Environment,Values=dev" "Name=instance-state-name,Values=running,stopped" \
    --query 'Reservations[].Instances[].[Tags[?Key==`Name`]|[0].Value, InstanceId, State.Name]' \
    --output json 2>&1); then
    print_error "EC2 인스턴스 목록을 조회할 수 없습니다. AWS 권한, region, profile 설정을 확인해 주세요."
    print_error "AWS CLI 오류 메시지: $aws_output"
    exit 1
  fi
  echo "$aws_output"
}

function print_instances() {
  local instances_json="$1"

  if ! command -v jq &> /dev/null; then
    echo "[ERROR] jq 명령어가 설치되지 않았습니다. brew install jq 명령으로 설치해주세요."
    exit 1
  fi

  local count=1
  local instances_count=$(echo "$instances_json" | jq '. | length')

  printf "%-3s %-60s %-35s %-20s\n" "No." "인스턴스 이름" "인스턴스 ID" "상태"
  printf "%s\n" "--------------------------------------------------------------------------------------------------------"

  for ((i=0; i<$instances_count; i++)); do
    local name=$(echo "$instances_json" | jq -r ".[$i][0]")
    local id=$(echo "$instances_json" | jq -r ".[$i][1]")
    local state=$(echo "$instances_json" | jq -r ".[$i][2]")

    if [[ "$name" == "null" || -z "$name" ]]; then
      name="$id"
    fi

    printf "%-3s %-60s %-25s %s\n" "$count." "$name" "$id" "$state"
    ((count++))
  done
}

function find_instance() {
  local instances_json="$1"
  local search_id="$2"
  local instances_count=$(echo "$instances_json" | jq '. | length')

  for ((i=0; i<$instances_count; i++)); do
    local id=$(echo "$instances_json" | jq -r ".[$i][1]")
    if [[ "$id" == "$search_id" ]]; then
      local name=$(echo "$instances_json" | jq -r ".[$i][0]")
      if [[ "$name" == "null" || -z "$name" ]]; then
        name="$id"
      fi
      local state=$(echo "$instances_json" | jq -r ".[$i][2]")
      echo "$name|$id|$state"
      return 0
    fi
  done
  return 1
}

function print_info() {
  printf "%s\n" "$1"
}

function print_error() {
  printf "[ERROR] %s\n" "$1"
}

function print_success() {
  printf "%s\n" "$1"
}

if [[ $# -ge 1 ]]; then
  CMD="$1"
  INSTANCES_JSON=$(fetch_instances)

  instances_count=$(echo "$INSTANCES_JSON" | jq '. | length')
  if [[ $instances_count -eq 0 ]]; then
    print_error "'Environment:dev' 태그가 붙은 EC2 인스턴스가 존재하지 않습니다."
    exit 1
  fi

  case "$CMD" in
    list)
      print_instances "$INSTANCES_JSON"
      exit 0
      ;;
    start|stop)
      if [[ $# -ne 2 ]]; then
        print_error "사용법: $0 $CMD {instance_id}"
        exit 1
      fi
      TARGET_ID="$2"

      INSTANCE_INFO=$(find_instance "$INSTANCES_JSON" "$TARGET_ID")
      if [[ $? -ne 0 ]]; then
        print_error "입력하신 인스턴스 ID가 dev 인스턴스 목록에 존재하지 않습니다."
        exit 1
      fi

      IFS='|' read -r TARGET_NAME TARGET_ID STATE <<< "$INSTANCE_INFO"

      if [[ "$CMD" == "start" ]]; then
        if [[ "$STATE" == "running" ]]; then
          printf "%s [%s]는 이미 실행 중입니다.\n" "$TARGET_NAME" "$TARGET_ID"
        else
          aws ec2 start-instances --instance-ids "$TARGET_ID"
          print_success "$TARGET_NAME [$TARGET_ID] 인스턴스를 실행하였습니다."
        fi
      else
        if [[ "$STATE" == "stopped" ]]; then
          printf "%s [%s]는 이미 중지 상태입니다.\n" "$TARGET_NAME" "$TARGET_ID"
        else
          aws ec2 stop-instances --instance-ids "$TARGET_ID"
          print_success "$TARGET_NAME [$TARGET_ID] 인스턴스를 종료하였습니다."
        fi
      fi
      exit 0
      ;;
    *)
      print_error "지원하지 않는 명령입니다. 사용법:"
      echo "  $0 list"
      echo "  $0 start {instance_id}"
      echo "  $0 stop {instance_id}"
      exit 1
      ;;
  esac
else
  print_error "명령형 모드만 지원됩니다. 사용법:"
  echo "  $0 list"
  echo "  $0 start {instance_id}"
  echo "  $0 stop {instance_id}"
  exit 1
fi

 

이로써 자동화 프로세스(On/Off)와 수동 제어 방식을 모두 구현한 전체 시스템 흐름은 다음과 같다.

flowchart TD
  A1[EC2 인스턴스 준비 완료] --> A2[UserData 스크립트 적용 완료]
  A2 --> A3[stopped → running 시 UserData로 앱 자동 실행]

  subgraph 자동화 프로세스
    B1[EventBridge Scheduler]
    B2[AWS Lambda 함수]
    B3[EC2 인스턴스 Start API 호출]
    B1 --> B2 --> B3
  end

  subgraph 수동 프로세스
    C1[Bash 스크립트 실행]
    C2[AWS CLI로 EC2 인스턴스 Start 요청]
    C1 --> C2
  end

  A3 --> B1
  A3 --> C1

  B3 --> D1[EC2 인스턴스 running]
  C2 --> D1

  D1 --> D2[UserData 스크립트 재실행 → 앱 정상 구동]

  subgraph 야간/주말 처리
    E1[EventBridge Scheduler]
    E2[AWS Lambda 함수]
    E3[EC2 인스턴스 Stop API 호출]
    E1 --> E2 --> E3
    E3 --> E4[EC2 인스턴스 stopped]
  end

  %% 시간 설정 추가 섹션
  subgraph 시간 설정
    S1[인스턴스 시작 시간: 08:00]
    S2[인스턴스 종료 시간: 22:00]
  end

 

결론

개발 서버의 유휴 시간 동안 EC2 인스턴스를 자동으로 종료하는 스케줄러를 생성함으로써 약 58%의 AWS 비용을 절감할 수 있었다. 이는 큰 돈이든 작은 돈이든, 장기적으로 볼 때 상당한 비용 절감 효과를 가져온다.

이번 작업을 통해 새로운 지식을 습득하고 이전에 애매하게 이해하고 있던 개념들을 명확히 할 수 있었다. 또한 AWS 웹 콘솔을 통한 수동 작업보다 IaC 방식이 Git처럼 변경 사항 추적이 가능하고 휴먼 에러를 줄일 수 있다는 점에서 매력을 느꼈다.

 

본 글은 온디맨드 방식으로 EC2 인스턴스를 사용할 때의 비용 절감 방법이었다. 다음 글에서는 온디맨드 방식이 아닌, 스팟 인스턴스를 사용하여 비용을 줄이고 안정적으로 운영하는 방법에 대해 소개하겠다.