Gom3rye 2025. 8. 4. 18:34
728x90
반응형

EKS(Elastic Kubernetes Service)

1.EKS 구축

  1. EKS 구축에 사용하는 도구 설치

=>AWS CLI 설치(eksctl 사용을 하기 위해서 설치)

  • 설치 확인: aws --version
  • 설치를 하고자 하는 경우는 access-key 와 secret access-key를 발급 받아야 함

사용자 생성할 때 모든 권한을 전부 부여해서 생성

  • aws configure --profile 명령을 이용해서 키를 등록
  • 프로필 확인: aws configure list-profiles

=>eksctl: EKS 명령을 수행하기 위한 CLI

Windows: https://github.com/eksctl-io/eksctl/releases

Mac: brew install weaveworks/tap/eksctl

Linux 에서는 직접 바이너리 파일을 다운로드 받아서 설치

  • 확인: eksctl version

=>kubectl 설치: 쿠버네티스 명령을 사용하기 위해서 사용

  • 확인: kubectl version --client
  1. 구축

=>CloudFormation을 이용해서 VPC를 구축

  • 리전 과 가용 용역 및 IP 대역을 설정
  • VPC를 생성하고 나면 출력 부분에서 WorkerSubnets 부분을 복사해 둡니다.

subnet-01dd5500f85f1f613,subnet-0df7857a5ca453f37,subnet-0b6d2ba8821995507

=>eks 클러스터 구축(eksctl 명령으로 구축)

eksctl create cluster --vpc-public-subnets WorkerSubnets 값 --name 클러스터이름 --region 리전이름 --version 쿠버네티스버전 --nodegroup-name 노드그룹이름(워커노드그룹) --node-type 생성되는 인스턴스의 타입 --nodes 워커노드개수 --nodes-min 워커노드의최소개수 --nodes-max 워커노드의최대개수

  • 정상적으로 생성되면 Stack 이 2개 추가되고 EC2 인스턴스가 워커노드의 개수만큼 생성됩니다.
  • 자동으로 control-plane을 생성해주고 현재 로컬 컴퓨터에 설치된 kubectl에 이 control-plane을 사용할 수 있는 context를 설정합니다.

kubectl config get-contexts 명령으로 확인

내용 확인: ~/kube/config(윈도우즈이면 사용자 디렉토리)

  1. 외부 접속이 가능한지 확인
  • nginx 파드 배포
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx-app
spec:
  containers:
  - name: nginx-container
    image: nginx
    ports:
    - containerPort: 80
    
# 리소스 생성
kubectl apply -f 야믈파일경로
# nginx 파드에 포트포워딩을 수행
kubectl port-forward nginx-pod 8080:80
  • 외부에서 접속
    • EKS는 로컬 컴퓨터와 연결이 되면 로컬 컴퓨터가 Control-Plane으로 인식한다.

Database를 연동하는 BackEnd Application 배포

  1. 테스트를 위해 로컬에 데이터베이스를 생성
  • 도커에 postgresql을 설치
    • docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=관리자비밀번호 --name 컨테이너이름 postgres
  • 샘플 데이터 생성
  1. Spring Boot Application 생성
  • Spring Boot Application 생성(의존성: Spring Dev tools, Lombok, Data JPA, Spring PostgreSQL, Spring Web)
  • build.gradle 파일의 dependencies를 수정
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'org.postgresql:postgresql'
    annotationProcessor 'org.projectlombok:lombok'
    implementation 'org.apache.commons:commons-lang3:3.9'
    testImplementation ('org.springframework.boot:spring-boot-starter-test'){
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'com.ninja-squad:DbSetup:2.1.0'
    testImplementation 'org.assertj:assertj-db:1.3.0'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
  • application.properties를 삭제하고 application.yaml 파일을 추가하고 정적인 데이터를 설정
server:
  port: 9000
spring:
  application:
    name: eks_backend
  datasource:
    url: jdbc:postgresql://localhost:5432/myworkdb
    username: mywork
    password: wnddkd
    driver-class-name: org.postgresql.Driver
    type: com.zaxxer.hikari.HikariDataSource
    jpa:
      database-platform= org.hibernate.dialect.PostgreSQLDialect
  • src/main/resources 디렉토리에 로깅을 위한 logback.xml 파일을 생성해서 작성
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss:SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
  • 모든 Entity(데이터베이스 테이블 과 매핑되는 클래스)가 공통으로 가져야 하는 메서드를 소유한 추상 클래스 생성
public abstract class AbstractEntity {
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
    @Override
    public boolean equals(Object obj) {
        if(obj == null) {
            return false;
        }
        if(obj == this) {
            return true;
        }
        if(obj.getClass() != this.getClass()) {
            return false;
        }
        return EqualsBuilder.reflectionEquals(this, obj);
    }
    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }
}
  • Region 테이블과 연동되는 RegionEntity 클래스 생성
@Entity
@Table(name="REGION")
@Getter
@Setter
public class RegionEntity extends AbstractEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="REGION_ID")
    private Integer regionId;
    @Column(name="REGION_NAME")
    private String regionName;
    @Column(name="CREATION_TIMESTAMP")
    private LocalDateTime creationTimestamp;
}
  • Location 테이블과 연동되는 LocationEntity 클래스 생성
@Entity
@Table(name = "LOCATION")
public class LocationEntity extends AbstractEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "LOCATION_ID")
    private Long locationId;
    @Column(name="LOCATION_NAME")
    private String locationName;
    @ManyToOne
    @JoinColumn(name = "REGION_ID")
    private RegionEntity region;
    @Column(name = "NOTE")
    private String note;
}
  • BatchProcessing 테이블 과 연동하는 Entity 클래스
@Entity
@Table(name="BATCH_PROCESSING")
@Data
public class BatchProcessingEntity extends AbstractEntity{
    @Id
    @Column(name = "BATCH_NAME", length=20, nullable=false)
    private String batchName;
    @Column(name="LAST_EXECUTION_DATE_TIME")
    private LocalDateTime lastExecutionDateTime;
    @OneToMany(mappedBy="batchProcessing", cascade= CascadeType.ALL)
    private List<BatchProcessingFileEntity> fileList;
}
  • BATCH_PROCESSING_FILE 테이블 과 연동할 Entity
@Entity
@Table(name="BATCH_PROCESING_FILE")
@Data
public class BatchProcessingFileEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long batchProcessingFileId;
    @ManyToOne
    @JoinColumn(name = "BATCH_NAME", nullable = false)
    private BatchProcessingEntity batchProcessing;
    @Column(name = "FILE_NAME", length=300, nullable = false)
    private String fileName;
}
  • Region 테이블의 CRUD 작업을 위한 Repository 생성
@Repository
public interface RegionRepository extends JpaRepository<RegionEntity, Integer> {
    //메서드 이름으로 sql을 생성
    //regionName으로 데이터를 조회해주는 메서드
    Optional<RegionEntity> findByRegionName(String regionName);
}
  • Location 테이블의 CRUD 작업을 위한 Repository 생성
@Repository
public interface LocationRepository extends JpaRepository<LocationEntity,Long> {
    //RegionEntity를 대입하면 동일한 값을 갖는 Location을 전부 조회
    List<LocationEntity> findByRegion(RegionEntity region);
}
  • BatchProcessing 테이블의 CRUD 작업을 위한 Repository 생성
@Repository
public interface BatchProcessingRepository extends JpaRepository<BatchProcessingEntity,Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @QueryHints(@QueryHint(name = "javax.persistence.lock.timeout", value = "0"))
    @Query("Select bp from BatchProcessingEntity bp where bp.batchName = :batchName")
    Optional<BatchProcessingEntity> fineByIdWithLock(String batchName);
}
  • BatchProcessingFile 테이블을 연동할 Repositoy 생성
public interface BatchProcessingFileRepository extends JpaRepository<BatchProcessingFileEntity, Long>{
    public void deleteByFileName(String fileName);
}
  • RegionRepository를 테스트하기 위한 클래스를 test 디렉토리 패키지에 생성
@SpringBootTest
public class RegionRepositoryTest {
    @Autowired
    private RegionRepository regionRepository;
    @Autowired
    private DataSource dataSource;
    @Test
    //테스트 메서드
    public void testFindAll() {
        prepareDatabase();
        //테이블의 데이터 전체를 가져오는 메서드를 호출해서 그 개수를 리턴
        var result = regionRepository.findAll();
        assertThat(result).hasSize(4);
    }
    @Test
    public void testFindByRegionName(){
        prepareDatabase();
        var result = regionRepository.findByRegionName("지역1");
        assertThat(result.get().getRegionId()).isEqualTo(1);
    }
    public void prepareDatabase(){
        var oprations = sequenceOf(
                deleteAllFrom("location"),
                deleteAllFrom("region"),
                insertInto("region")
                        .columns("region_id", "region_name", "creation_timestamp")
                        .values(1, "지역1", LocalDateTime.now())
                        .values(2, "지역2", LocalDateTime.now())
                        .values(3, "지역3", LocalDateTime.now())
                        .values(4, "지역4", LocalDateTime.now())
                        .build()
        );
        var dbSetup = new DbSetup(new DataSourceDestination(dataSource), oprations);
        dbSetup.launch();
    }
}
  • LocationRepository를 테스트하기 위한 클래스를 test 디렉토리 패키지에 생성
@SpringBootTest
public class LocationRepositoryTest {
    @Autowired
    private LocationRepository locationRepository;
    @Autowired
    private RegionRepository regionRepository;
    @Autowired
    private DataSource dataSource;
    @Test
    public void testFindByRegion() {
        //prepare_Database();
        var region = regionRepository.findByRegionName("지역1").get();
        var result = locationRepository.findByRegion(region);
        assertThat(result).hasSize(4);
    }
    //샘플 데이터베이스 생성해주는 메서드
    //테스트하기 전에 무조건 수행하는 메서드
    @BeforeEach
    public void prepare_Database(){
        var oprations = sequenceOf(
                deleteAllFrom("location"),
                deleteAllFrom("region"),
                insertInto("region")
                        .columns("region_id", "region_name", "creation_timestamp")
                        .values(1, "지역1", LocalDateTime.now())
                        .values(2, "지역2", LocalDateTime.now())
                        .values(3, "지역3", LocalDateTime.now())
                        .values(4, "지역4", LocalDateTime.now())
                        .build(),
                insertInto("location")
                        .columns("location_id", "location_name", "region_id", "note")
                        .values(1, "명소1", 1, "명소1의 상세정보 입니다.")
                        .values(2, "명소2", 1, "명소2의 상세정보 입니다.")
                        .values(3, "명소3", 1, "명소3의 상세정보 입니다.")
                        .values(4, "명소4", 1, "명소4의 상세정보 입니다.")
                        .build()
        );
        var dbSetup = new DbSetup(new DataSourceDestination(dataSource), oprations);
        dbSetup.launch();
    }
}
  • 서비스 계층에서 사용할 Region 테이블에 대한 도메인 클래스를 생성
@Getter
@Setter
public class Region {
    private Integer regionId;
    private String regionName;
    private LocalDateTime creationTimestamp;
    //파라미터를 받아서 생성
    public Region(Integer regionId, String regionName, LocalDateTime creationTimestamp) {
        if(regionName == null){
            throw new IllegalArgumentException("regionName cannot be null");
        }
        this.regionId = regionId;
        this.regionName = regionName;
        this.creationTimestamp = creationTimestamp;
    }
    public Region(RegionEntity regionEntity) {
        this(regionEntity.getRegionId(), regionEntity.getRegionName(),
                regionEntity.getCreationTimestamp());
    }
}
  • 서비스 계층에서 사용할 Location 테이블에 대한 도메인 클래스를 생성
public class Location {
	private Long locationId;
	private String locationName;
	private Region region;
	private String note;
	public Location(Long locationId, String locationName, Region region, String note) {

		if (locationName == null) {
			throw new IllegalArgumentException("locationName cannot be null.");
		}
			
		if (region == null) {
			throw new IllegalArgumentException("region cannot be null.");
		}
			 
		this.locationId = locationId;
		this.locationName = locationName;
		this.region = region;
		this.note = note;		 
	}

	public Location(String locationName, Region region, String note) {
		this(null, locationName, region, note);
	}
	
	public Location(LocationEntity entity) {	 
		this(entity.getLocationId(), entity.getLocationName(), new Region(entity.getRegion()), entity.getNote());
	}

	public Long getLocationId() {
		
		return locationId;
	}
		 
	public void setLocationId(Long locationId) {
		this.locationId = locationId;
	}
		 
	public String getLocationName() {
		return locationName;
	}
	
	public void setLocationName(String locationName) {
		this.locationName = locationName;	 
	}

	public Region getRegion() {
		return region;
	}

	public void setRegion(Region region) {
		this.region = region;
	}
	
	public String getNote() {
		return note;
	}
	
	public void setNote(String note) {
		this.note = note;
	}
}
  • Region 관련 서비스를 위한 인터페이스 생성
public interface RegionService {
    //모든 Region을 리턴하는 메서드
    public List<Region> getAllRegions();
}
  • Region 관련 서비스 구현을 위한 클래스 생성
@Service
@RequiredArgsConstructor
public class RegionServiceImpl implements RegionService {
    private final RegionRepository regionRepository;
    @Override
    public List<Region> getAllRegions() {
        //Region 테이블의 모든 데이터를 가져옵니다.
        var regionEntities = regionRepository.findAll();
        var regionList = new ArrayList<Region>();
        //가져온 RegionEntity List를 Region List로 수정
        //각각의 데이터에 new Region을 적용해서 List에 추가
        regionEntities.forEach(entity -> regionList.add(new Region(entity)));
        return regionList;
    }
}
  • Location 관련 서비스를 위한 인터페이스 생성
public interface LocationService {
    //regionId를 가지고 Location을 전부 찾아오는 메서드
    public List<Location> getLocationListByRegionId(Integer regionId);
    //Location의 List를 받아서 등록하는 메서드
    public void registerLocations(List<Location> locationList);
}
  • Location 관련 서비스 구현을 위한 클래스 생성
@Service
@RequiredArgsConstructor
public class LocationServiceImpl implements LocationService{
    private final LocationRepository locationRepository;
    private final RegionRepository regionRepository;
    private static final Logger logger = LoggerFactory.getLogger(LocationServiceImpl.class);
    @Override
    public List<Location> getLocationListByRegionId(Integer regionId) {
        var region = regionRepository.findById(regionId);
        var locationList = new ArrayList<Location>();
        region.ifPresent(r -> {
            var locationEntityList = locationRepository.findByRegion(r);
            locationEntityList.forEach(entity ->
                locationList.add(new Location(entity)));
            });
        return locationList;
    }
    @Override
    @Transactional
    public void registerLocations(List<Location> locationList) {
        var regionMap = new HashMap<Integer, RegionEntity>();
        locationList.forEach(location -> {
            try{
                var entity = new LocationEntity();
                entity.setLocationName(location.getLocationName());
                entity.setRegion(getRegionEntity(location.getRegion(), regionMap));
                entity.setNote(location.getNote());
                locationRepository.save(entity);
            }catch(Exception e){
                logger.warn("Skipped data. Error occurred in :" + location, e);
            }
        });
    }
    //내부에서 사용할 메서드
    private RegionEntity getRegionEntity(
            Region region, Map<Integer, RegionEntity> regionMap){
        if(regionMap.get(region.getRegionId()) == null){
            regionRepository.findById(region.getRegionId()).ifPresent(
                    entity -> regionMap.put(region.getRegionId(), entity ));
        }
        return regionMap.get(region.getRegionId());
    }
}
  • LocationService를 테스트할 클래스를 만들어서 메서드를 만든 후 테스트 수행
@SpringBootTest
public class LocationServiceTest {
    @Autowired
    private LocationService locationService;
    @Autowired
    private DataSource dataSource;
    @Test
    public void testRegisterLocations() {
        var locationList = List.of(
                new Location("명소5",
                        new Region(1, "지역1", LocalDateTime.now()),
                        "명소5의 상세 정보입니다."),
                new Location("명소6",
                        new Region(1, "지역1", LocalDateTime.now()),
                        "명소6의 상세 정보입니다.")
        );
        locationService.registerLocations(locationList);
        var locationTable = new Table(dataSource, "location");
        assertThat(locationTable).hasNumberOfRows(2);
    }
    @BeforeEach
    public void prepareDatabase(){
        var oprations = sequenceOf(
                deleteAllFrom("location"),
                deleteAllFrom("region"),
                insertInto("region")
                        .columns("region_id", "region_name", "creation_timestamp")
                        .values(1, "지역1", LocalDateTime.now())
                        .values(2, "지역2", LocalDateTime.now())
                        .values(3, "지역3", LocalDateTime.now())
                        .values(4, "지역4", LocalDateTime.now())
                        .build()
        );
        var dbSetup = new DbSetup(new DataSourceDestination(dataSource), oprations);
        dbSetup.launch();
    }
}
  • Controller 계층에서 HealthCheck에 사용할 클래스
//Health 체크에 사용할 클래스
@Getter
@Setter
public class HealthDto {
	private String status;
	 
	public String getStatus() {
		return status;
	}
	
	public void setStatus(String status) {
		this.status = status;
	}
}
  • Controller 계층에서 사용할 RegionDTO 클래스 생성
//DTO: Data Transfer Object
//서로 다른 레이어 사이에서 데이터를 이동할 때 사용하는 클래스
@Getter
@Setter
public class RegionDTO implements Serializable {
    private Integer regionId;
    private String regionName;
    public RegionDTO() {}
    public RegionDTO(Region region) {
        this.regionId = region.getRegionId();
        this.regionName = region.getRegionName();
    }
}
  • Controller 계층에서 사용할 RegionsDTO 클래스 생성
@Getter
@Setter
public class RegionsDTO {
    private List<RegionDTO> regionList = new ArrayList<>();
    public RegionsDTO() {}
    public RegionsDTO(List<RegionDTO> regionDTOList) {
        this.regionList.addAll(regionDTOList);
    }
}
  • LocationDTO 클래스 생성
@Setter
@Getter
public class LocationDTO {
    private Long locationId;
    private String locationName;
    private RegionDTO region;
    private String note;
    public LocationDTO(){}
    public LocationDTO(Location location){
        this.locationId = location.getLocationId();
        this.locationName = location.getLocationName();
        this.region = new RegionDTO(location.getRegion());
        this.note = location.getNote();
    }
}
  • LocationsDTO 클래스 생성
@Getter
@Setter
public class LocationsDTO {
    private List<LocationDTO> locationDTOList = new ArrayList<>();
    public LocationsDTO() {}
    public LocationsDTO(List<LocationDTO> locationDTOList) {
        this.locationDTOList.addAll(locationDTOList);
    }
}
  • Health Check를 위한 Controller 클래스
@RestController
@RequestMapping("health")
public class HealthApi {
    private static final Logger LOGGER = LoggerFactory.getLogger(HealthApi.class);
    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public HealthDTO getHealth() {
        LOGGER.info("Health API Called");
        var health = new HealthDTO();
        health.setStatus("OK");
        return health;
    }
}
  • Region Service를 위한 Controller 클래스
@RestController
@RequestMapping("region")
//CORS 설정(동일한 도메인이 아닌 곳에서 javascript api를 이용해서 호출 가능하도록 하는 설정)
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class RegionApi {
    private static final Logger LOGGER = LoggerFactory.getLogger(RegionApi.class);
    private final RegionService regionService;
    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public RegionsDTO getAllRegions() {
        LOGGER.info("REGION GET ALL API");
        var allRegions = regionService.getAllRegions();
        var dtoList = new ArrayList<RegionDTO>();
        allRegions.forEach(region -> {
            var dto = new RegionDTO(region);
            dtoList.add(dto);
        });
        return new RegionsDTO(dtoList);
    }
}
  • Location Service를 위한 Controller 클래스
public class LocationApi {
	private static final Logger LOGGER = LoggerFactory.getLogger(LocationApi.class);
	private final LocationService service;
	@GetMapping(value = "/region/{regionId}", produces = MediaType.APPLICATION_JSON_VALUE)
	public LocationsDto getLocationListByRegion(@PathVariable("regionId") Integer regionId) {
		LOGGER.info("LOCATION LIST BY REGION ID API");
			 
		var locationList = service.getLocationListByRegionId(regionId);
		var dtoList = new ArrayList<LocationDto>();
		locationList.forEach(location -> {
			var dto = new LocationDto(location);
			dtoList.add(dto);
		});
		
		var locationsDto = new LocationsDto(dtoList);
			return locationsDto;
		}
}

애플리케이션 빌드

빌드 시 수행되는 작업

  • 의존성 라이브러리 다운로드
  • 프로그램 컴파일
  • 테스트 프로그램 컴파일
  • 테스트 실행
  • 프로그램 실행용 아카이브 파일(jar) 생성
  • 명령어
    • sudo chmod 755 ./gradlew(윈도우에서 실행시에는 안해도 되지만 리눅스에서는 반드시 수행)
    • ./gradlew clean build
  • 이미지로 만들 때는 위의 작업을 수행한 후 작업을 해야 하고 git action 이나 jenkins 에서 할 때는 위의 빌드 작업을 task 나 step으로 만들어서 수행합니다.
  • 이미지 생성을 위한 Dockerfile 생성
FROM amazoncorretto:17
CMD ["./mvnw", "clean", "package"]
ARG JAR_FILE=target/*.jar
COPY ./build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
  • 이미지 빌드
    • docker build -t k8s/eks_backend-app:1.0.0 --build-arg JAR_FILE=build/libs/eks_backend-0.0.1-SNAPSHOT.jar .

이미지를 ECR에 push

Cloud Formation을 이용해서 RDS를 구축

  • 데이터베이스 환경 구축
    • 서비스 환경에서는 데이터베이스는 가용 영역 여러 개로 다중화해서 구축
    • RDS는 AWS가 제공하는 관계형 데이터베이스의 관리형 서비스로 AWS 관리 콘솔이나 AWS CLI, CloudFormation 등을 이용해서 구축이 가능하다.
    • RDS의 문제점은 데이터베이스가 설치된 OS에 로그인 할 수 없는 문제 때문에 데이터베이스 환경 관련 제약 사항이 있음
    • 데이터베이스 관리용 서버로 Bastion Host를 구축하여 사용
    • Bastion Host란 외부에서 내부 네트워크에 접근할 수 있는 접근점
    • 보안성이 높은 인프라와 외부 인터넷을 연결하는 중계 서버로 작동하며 모든 인바운드 트래픽은 Bastion Host를 통과해야 내부 네트워크로 들어 갈 수 있음
    • 보통은 AWS에서는 EC2로 구축
  • 실제 환경 구축
    • CloudFormation에서 rds_ope_cfn.yaml 파일을 이용해서 스택 생성
      • EksWorkVPC 풀다운 메뉴 값 중에 eks-work-vpc가 포함된 행 선택
      • OpeServeRouteTable은 CloudFormation -> eks-work-base 스택 -> 출력 탭 -> RouteTable값 입력
  • 베스천 호스트 접속
    • 세션 서비스 관리자(Systems Manager)로 접속
    • 세션 관리자에서 세션 시작을 클릭
    • 동일한 VPC 안에 있는 어떤 EC2 인스턴스도 베스천 호스트가 될 수 있음
    • 아무 인스턴스나 선택해서 세션 시작을 클릭
    • 세션에서 명령어를 수행
    sudo yum install -y git
    sudo amazon-linux-extras install -y postgresql11
    
    • 데이터베이스 엔드포인트 확인
    • 데이터베이스 유저 와 비밀번호 확인(AWS Secrets Manager가 비밀번호를 생성)
      • 개요에서 확인
728x90
반응형