Introduction to Pulumi With Java
1. 서론
Pulumi는 개발자들이 자바와 같은 범용 프로그래밍 언어를 사용하여 클라우드 리소스를 정의하고 관리할 수 있게 해주는 현대적인 인프라스트럭처 코드(IaC) 도구입니다. 전통적인 IaC 도구가 도메인 특화 언어(DSL)를 사용하는 것과는 달리, Pulumi는 자바의 강력한 기능을 활용하여 재사용 가능하고 모듈화된 타입 안전 인프라스트럭처 코드를 생성합니다.
이 기사는 Pulumi를 자바와 함께 사용하여 S3 버킷, EC2 인스턴스 및 보안 그룹 등 AWS 리소스를 생성하고 관리하는 방법을 설명합니다. 또한 EC2 인스턴스를 인터넷에 노출시키고, 인프라를 업데이트하며, 모든 리소스를 삭제하는 방법도 다룹니다.
2. 자바를 사용한 Pulumi 설정하기
Pulumi CLI가 설치되어 있는지 확인해야 합니다. 공식 웹사이트에서 설치할 수 있습니다.
설치 후, AWS 자바 템플릿을 사용하여 새로운 Pulumi 프로젝트를 생성합니다:
$ pulumi new aws-java
이 명령은 기본 프로젝트 구조를 생성합니다. 이후 프로젝트 디렉토리로 이동하여 종속성을 설치합니다:
$ cd my-pulumi-project
$ mvn install
이제 코드를 작성할 준비가 완료되었습니다.
3. 간단한 AWS 리소스 생성하기
이 섹션에서는 AWS에서 간단한 독립형 리소스를 생성하는 방법을 살펴봅니다. 이후에는 인터넷에서 웹 서비스를 노출하기 위해 약간 복잡한 아키텍처를 탐구합니다.
아마존 S3 (Simple Storage Service)는 파일, 백업 및 미디어를 저장하는 데 널리 사용되는 AWS의 고도로 확장 가능한 객체 저장 솔루션입니다. 이를 통해 Pulumi의 기능을 간단하면서도 영향력 있게 보여줄 수 있습니다. Pulumi를 사용하여 S3 버킷을 정의하는 것은 버킷 리소스를 선언하는 것만큼 간단합니다:
var bucket = new Bucket("pulumi-bucket", BucketArgs.builder().build());
Pulumi에서 S3 버킷을 정의한 후, 이를 AWS에 배포할 수 있습니다. Pulumi는 이를 위한 간단한 명령을 제공합니다:
$ pulumi up
pulumi up를 실행하면 Pulumi는 생성될 리소스를 보여줍니다.
Pulumi는 코드를 분석하여 생성, 수정 또는 삭제될 리소스를 보여줍니다. 또한, 진행하기 전에 계획된 변경 사항을 검토할 수 있도록 확인을 요청합니다.
4. 웹 서버 생성하기
이제 S3 버킷으로 시작했으니, 웹 서비스를 위한 인프라를 구축하도록 하겠습니다. 웹 요청을 서비스하기 위해 EC2 인스턴스를 배포하려면 VPC, 서브넷, 인터넷 게이트웨이 및 기타 필수 네트워크 구성 요소를 설정해야 합니다.
4.1. 네트워크 인프라 생성하기
VPC (Virtual Private Cloud)는 AWS 내에서 리소스를 시작할 수 있는 격리된 네트워크입니다. 이를 통해 IP 주소 범위를 정의하고, 서브넷을 만들며, 리소스 간 통신을 제어할 수 있는 라우팅 규칙을 구성할 수 있습니다.
CIDR 블록이 10.0.0.0/16인 VPC를 생성해 보겠습니다:
var vpc = new Vpc("vpc", VpcArgs.builder().cidrBlock("10.0.0.0/16").build());
서브넷은 VPC의 하위 분할로, 작은 네트워크 세그먼트 내에서 리소스를 조직할 수 있게 해줍니다. EC2 인스턴스가 외부에서 접근 가능하려면 퍼블릭 서브넷에 있어야 하며, 이는 인스턴스에 퍼블릭 IP 주소를 자동으로 할당합니다:
var subnet = new Subnet("subnet", SubnetArgs.builder()
.vpcId(vpc.id())
.cidrBlock("10.0.1.0/24")
.mapPublicIpOnLaunch(true)
.build());
기본적으로 VPC 내의 리소스는 인터넷에 접근할 수 없습니다. 인터넷으로의 트래픽 흐름을 허용하려면 인터넷 게이트웨이(IGW)가 필요합니다. 이 AWS 관리 서비스는 우리 VPC를 공용 인터넷에 연결합니다:
var gateway = new InternetGateway("gateway", InternetGatewayArgs.builder()
.vpcId(vpc.id())
.build());
인터넷 게이트웨이가 있어도 서브넷 내의 인스턴스는 인터넷에 접근하는 방법을 자동으로 알지 못합니다. 네트워크 트래픽 흐름을 정의하는 라우트 테이블을 설정해야 합니다. 기본 라우트(0.0.0.0/0)를 설정하여 모든 아웃바운드 트래픽이 게이트웨이를 통해 인터넷으로 나가도록 합니다:
var routes = new RouteTable("routes", RouteTableArgs.builder()
.vpcId(vpc.id())
.routes(RouteTableRouteArgs.builder()
.cidrBlock("0.0.0.0/0")
.gatewayId(gateway.id())
.build())
.build());
var routeTableAssociation = new RouteTableAssociation("route-table-association", RouteTableAssociationArgs.builder()
.subnetId(subnet.id())
.routeTableId(routes.id())
.build());
또한, EC2 인스턴스가 인터넷 게이트웨이를 사용할 수 있도록 퍼블릭 서브넷에 라우트 테이블을 연결합니다.
4.2. 보안 그룹: 트래픽 제어하기
보안 그룹은 EC2 인스턴스에 도달할 수 있는 네트워크 트래픽 유형을 제어하는 가상의 방화벽입니다. 이를 생성하고 VPC에 연결하겠습니다:
var securityGroup = new SecurityGroup("security-group", SecurityGroupArgs.builder()
.name("allow_public")
.vpcId(vpc.id())
.build());
이 시점에서 보안 그룹은 존재하지만 아직 규칙이 없습니다. AWS의 보안 그룹은 기본적으로 모든 트래픽을 거부하며, 규칙을 명시적으로 정의하기 전까지 트래픽이 허용되지 않습니다. 서버에 접근할 수 있도록 HTTP 인바운드 트래픽을 허용해야 합니다:
var allowPort80Ipv4 = new SecurityGroupIngressRule("allowPort80Ingress", SecurityGroupIngressRuleArgs.builder()
.securityGroupId(securityGroup.id())
.cidrIpv4("0.0.0.0/0")
.fromPort(80)
.ipProtocol("tcp")
.toPort(80)
.build());
이 규칙을 이전에 생성한 보안 그룹에 첨부하고 포트 80 (HTTP 웹 트래픽에 사용됨)에 대한 모든 트래픽을 허용합니다. 또한 서비스가 인터넷에 접근할 수 있도록 아웃바운드 규칙도 추가해야 합니다:
var allowAllTrafficIpv4 = new SecurityGroupEgressRule("allowAllTrafficEgress", SecurityGroupEgressRuleArgs.builder()
.securityGroupId(securityGroup.id())
.cidrIpv4("0.0.0.0/0")
.ipProtocol("-1")
.build());
이렇게 하면 EC2 인스턴스가 설정 시 업데이트 및 종속성을 다운로드 할 수 있습니다.
4.3. 사용자 데이터: 서버 설정 자동화하기
EC2 인스턴스를 시작할 때 자동으로 실행되는 사용자 데이터 스크립트를 전달할 수 있습니다:
String userData = """
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello World, welcome to our Pulumi deployed infrastructure.</h1>" > /var/www/html/index.html
""";
이 스크립트는 다음을 수행합니다:
- 시스템 업데이트
- Apache HTTP 서버 설치
- 웹 서버 시작
- 기본 HTML 페이지 생성
이를 통해 EC2 인스턴스가 시작되면 즉시 웹 페이지를 제공할 수 있습니다.
4.4. EC2 인스턴스 시작하기
보안과 자동화가 구축되었으니, 이제 EC2 인스턴스를 정의하고 배포할 수 있습니다:
var instance = new Instance("my-instance", InstanceArgs.builder()
.instanceType(InstanceType.T2_Micro)
.ami("ami-0e320147c22e46f12")
.subnetId(subnet.id())
.vpcSecurityGroupIds(Output.all(securityGroup.id()))
.associatePublicIpAddress(true)
.userData(userData) // 사용자 데이터를 인스턴스에 추가
.build());
인스턴스 유형은 t2.micro로 설정되어 있으며, 이는 작고 비용 효율적인 옵션입니다. 또한 이 인스턴스 유형은 AWS 무료 사용 한도에 포함됩니다. 사용되는 Amazon 머신 이미지(AMI)는 Amazon Linux 2로, 경량화되고 AWS에 최적화된 운영 체제입니다. 인스턴스가 외부에서 접근 가능해야 하므로 퍼블릭 IP 주소가 할당됩니다. 이를 통해 나중에 웹 서버를 테스트할 수 있습니다.
우리 EC2 인스턴스에 할당된 퍼블릭 IP 주소를 콘솔에 출력할 수 있습니다:
ctx.export("publicIp", instance.publicIp());
이렇게 하면 AWS 콘솔을 수동으로 확인하지 않고도 인스턴스의 퍼블릭 IP를 가져올 수 있습니다. pulumi up를 실행하면 퍼블릭 IP가 명령 출력에 표시됩니다.
4.5. 웹 서버 배포 확인하기
EC2 인스턴스가 가동되면 웹 서버에 접근할 수 있는지 확인합니다. 그러나 테스트하기 전에 인스턴스가 초기화를 완료할 수 있도록 몇 분 정도 기다려야 합니다. 특히, 웹 서버가 시작되고 있는 동안 업데이트 및 패키지가 설치되고 있을 수 있습니다. 배포를 확인하는 가장 간단한 방법은 인스턴스의 퍼블릭 IP를 웹 브라우저에 입력하는 것입니다:
이제 사용자 데이터 스크립트의 메시지를 볼 수 있습니다.
명령 줄에서 빠른 확인을 위해 curl을 사용하여 인스턴스에 HTTP 요청을 보낼 수 있습니다:
$ curl http://<퍼블릭_IP_주소>/
5. 인프라 삭제하기
웹 서버를 테스트한 후에는 불필요한 AWS 비용을 방지하기 위해 배포된 리소스를 제거해야 합니다. EC2 인스턴스와 같은 클라우드 리소스는 활성 사용 여부와 관계없이 존재하는 한 요금이 부과됩니다.
Pulumi는 단일 명령으로 인프라를 쉽게 철거할 수 있도록 해줍니다:
$ pulumi destroy
이 명령은:
- EC2 인스턴스를 종료합니다.
- 보안 그룹, VPC, 서브넷 및 라우트 테이블을 삭제합니다.
- S3 버킷을 제거합니다(버킷에 파일이 포함되어 있는 경우 일단 수동으로 비워야 합니다).
Pulumi는 삭제를 진행하기 전 확인을 요청합니다. 확인 후에는 이전에 생성된 모든 리소스를 순차적으로 제거합니다.
6. 결론
이 튜토리얼에서는 Pulumi를 사용하여 AWS 인프라를 생성하고 관리하는 방법, 즉 S3 버킷, VPC, EC2 인스턴스 및 보안 그룹 및 라우트 테이블과 같은 관련 리소스를 포함하여 설명했습니다. 각 코드 부분을 분해하여 Pulumi가 자바와 같은 친숙한 프로그래밍 언어를 사용하여 클라우드 인프라를 쉽게 프로비저닝하는 방법을 이해했습니다.
pulumi up를 실행함으로써 자원을 배포하는 것이 얼마나 쉬운지 확인했고, pulumi destroy를 사용하여 리소스를 정리하여 AWS에서 원하지 않는 비용을 피할 수 있음을 알게 되었습니다. S3와 같은 간단한 저장소를 프로비저닝하거나 EC2 인스턴스와 같은 보다 복잡한 네트워킹 및 컴퓨팅 리소스를 관리하는 데 있어 Pulumi는 클라우드 인프라를 프로그래밍 방식으로 관리할 수 있는 유연하고 강력한 방법을 제공합니다.
이 기사에 대한 모든 예제는 GitHub에서 확인할 수 있습니다.