<개요>

- 기존에 사용하던 Aurora DB의 성능 문제로 AWS Redshift 로 제품군 변경

- EBS 를 사용하던 부분을 ECS로 변경하기 위해서 Docker 작업

 

<내용>

- ANSI SQL을 주로 사용하였다면 쿼리상의 큰 변화는 없다.

- MySQL driver를 pom.xml 에서 제거하고 Redshift driver를 추가한다.

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
 <dependency>
            <groupId>com.amazon.redshift</groupId>
            <artifactId>redshift-jdbc42-no-awssdk</artifactId>
            <version>1.2.10.1009</version>
        </dependency>

- application.yml 파일에서도 DB url을 변경한다.

spring:
  profiles: dev
  datasource:
    driver-class-name: com.amazon.redshift.jdbc42.Driver
    url: jdbc:redshift://localhost:5439/dev
    username: 
    password: 

- docker file을 작성한다.[Dockerfile]

FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

- maven package를 수행하고 다음과 같이 Docker 이미지를 build한다.

docker build -t {service-name} .

- docker 를 실행한다.

docker run -p 8200:8200 {service-name}

 

<오류상황>

- Local에서는 잘 동작하던 Application이 Docker 이미지에서는 잘 되지 않는 현상이 발생한다.

- 오류로그를 보면 다음과 같다.

Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
	at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:100) ~[hibernate-core-5.3.7.Final.jar!/:5.3.7.Final]
	at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:54) ~[hibernate-core-5.3.7.Final.jar!/:5.3.7.Final]
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:137) ~[hibernate-core-5.3.7.Final.jar!/:5.3.7.Final]
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35) ~[hibernate-core-5.3.7.Final.jar!/:5.3.7.Final]
	at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:94) ~[hibernate-core-5.3.7.Final.jar!/:5.3.7.Final]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) ~[hibernate-core-5.3.7.Final.jar!/:5.3.7.Final]
	... 41 common frames omitted

hibernate dialect 가 설정되지 않았다는 내용이다.  

일반적으로는 명시하지 않아도 추천되어서 hibernate가 자동으로 지정하는데 docker내에서는 오류가 발생하는 것을 확인할 수 있다.

(자세한 원인파악이 필요하다. 우선은 해당 dialect를 명시하여 오류를 수정한다.)

- AWS Redshift는 PostgreSQL 8.0.2. 을 기반으로 수행되기 때문에 다음과 같이 Spring JPA를 세팅한다.

  jpa:
    database-platform: org.hibernate.dialect.PostgreSQL9Dialect
    generate-ddl: false
    hibernate:
      ddl-auto: none

 

<해결>

- docker 상에서도 정상적으로 서비스가 동작하는 것을 확인할 수 있다.

- 다음에는 AWS ECS 에서 해당 서비스를 기동한다.

 

<참고>

PostgreSQL을 기반으로 만들어졌으나 지원하지 않는 기능들이 있으니 확인해야 한다.

<AWS Redshift에서 제공하지 않는 PostgreSQL features>

  • Table partitioning (range and list partitioning)
  • Tablespaces
  • Constraints
    • Unique
    • Foreign key
    • Primary key
    • Check constraints
    • Exclusion constraints

<AWS Redshift에서 제공하지 않는 PostgreSQL data types>

  • Arrays
  • BIT, BIT VARYING
  • BYTEA
  • Composite Types
  • Date/Time Types
    • INTERVAL
    • TIME
  • Enumerated Types
  • Geometric Types
  • HSTORE
  • JSON
  • Network Address Types
  • Numeric Types
    • SERIAL, BIGSERIAL, SMALLSERIAL
    • MONEY
  • Object Identifier Types
  • Pseudo-Types
  • Range Types
  • Special Character Types
    • "char" – A single-byte internal type (where the data type named char is enclosed in quotation marks).
    • name – An internal type for object names.
    For more information about these types, see Special Character Types in the PostgreSQL documentation.
  • Text Search Types
  • TXID_SNAPSHOT
  • UUID
  • XML

https://docs.aws.amazon.com/redshift/latest/dg/c_redshift-and-postgres-sql.html 

<개요>

Azure WebApp을 다음과 같은이유로 잘 사용해왔다.

- Azure Devops와 연계하여 배포가 쉽다.

- 다양한 Runtime환경을 기본으로 제공한다. (node, java, C# 등)

- 통합 로깅, 모니터링, 대시보드 환경을 제공한다.

- Auto Scaling이 편리하다. (VM 단위보다 더욱 유동적이다.)

- 접속을 위한 싱글포인트를 제공한다.

 

- 이번에 AWS에서 시스템을 구축하게 되어서 비슷한 PaaS를 찾던 중 Elastic Beanstalk를 발견하고 사용하던 중 발생한 내용이다.

 

<현상>

- Elastic Beanstalk는 내부적으로 nginx 를 사용하고 있다.

- Spring Boot배포 후 접속하면 502 Bad gateway 발생

 

<상세오류 - 로그확인>

2020/03/12 06:12:07 [error] 3114#0: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 100.10.0.208, server: , request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:5000/", host: "userservice-env.eba-yqakdkhm.ap-northeast-2.elasticbeanstalk.com"


<해결과정>

- 구글링을 통해서 나오는 대표적인 해결방법은 포트변경이다. (AWS Beanstalk가 기본적으로 5000포트를 사용한다고 한다.)

Beanstalk내의 환경설정에서 SERVER_PORT를 5000으로 변경

하지만 나에게는 해당하지 않는 내용이었다. 과거 connection reset을 해결할때가 떠오르기 시작한다;;

 

조금 더 관련내용들을 찾아보았다.

https://stackoverflow.com/questions/52912663/aws-elastic-beanstalk-nginx-connect-failed-111-connection-refused

 

AWS elastic Beanstalk / nginx : connect() failed (111: Connection refused

I got this message connect() failed (111: Connection refused Here is my log: ------------------------------------- /var/log/nginx/error.log ------------------------------------- 2018/10/21 06:16...

stackoverflow.com

내용을 종합해보면 결국 application이 적절하게 기동되지 않거나 하여 nginx 가 해석할 수 없는 메시지를 돌려받았을 경우 발생한다.

- Beanstalk의 설정을 천천히 검토해보았다.

 구성, 환경변수, 네트워크, 로드 밸런서 등 이상이 없음을 확인하였다.

- 그렇다면 결국 application이 제대로 기동되지 않았을 확률이 높으나 해당 로그를 확인할 수 없는 것이 아쉽다.  (로컬에서는 정상적으로 기동하니까)

 

- AWS Beanstalk의 spring-boot application 기동방식이 궁금해지기 시작한다.

샘플어플리케이션으로 사용되는 예제는 다음과 같다.

https://github.com/spring-guides/gs-accessing-data-rest.git

 

해당 프로젝트의 pom.xml을 살펴보고 문제점을 찾았다. 아주 사소한....

<build> 태그의 <spring-boot-maven-plugin> 을 빼먹고 작성하지 않았다. 

 

 

<정리>

- Azure WebApp을 사용할때는 azure-webapp-maven-plugin을 사용했었다.

- 신규 프로젝트 구성시 initializer없이 구성하다 Public Cloud관련요소 작업 중 Build plugin실수로 삭제;;

- 정확한 로그를 확인할 수 없어서 약간 힘들었음

 

<Azure WebApp과 비교>

- Azure WebApp이 생성시 매우 간단하다. 클릭 몇번으로 끝

- 배포 역시 azure-webapp-maven-plugin을 사용하여 IDE에서 바로 배포하는 것도 쉽다.

  다만 version에 따른 사용법이 매우 다르기 때문에 주의해서 사용해야 한다. 

  예전에 사용했던 버전이 1.4.0 과 1.5.3 이었는데 세부속성과 사용법이 다르고, 설정 오류시 상세한 메시지를 확인하기가 어렵다.)

 

- AWS Beanstalk역시 기본설정은 간단하지만 상세화면으로 들아가면 항목이 매우 많다. 

( Azure의 경우 후발주자이다보니 PaaS를 중심으로 발전시켜 나갔고, 

  AWS의 경우 VM기반의 선발주자이다보니 제품 여기저기에서 VM중심의 구성이 많이 보인다.)

 

 

<참고>

https://aws.amazon.com/ko/blogs/devops/deploying-a-spring-boot-application-on-aws-using-aws-elastic-beanstalk/

https://github.com/spring-guides/gs-accessing-data-rest/

https://docs.microsoft.com/ko-kr/azure/java/spring-framework/deploy-containerized-spring-boot-java-app-with-maven-plugin

 

Maven을 사용하여 Azure에 Spring Boot 앱 배포

Azure Web Apps의 Maven 플러그 인을 사용하여 Spring Boot 앱을 Azure에 배포하는 방법을 알아봅니다.

docs.microsoft.com

 

다음에는 Azure Devops와 AWS Code Pipeline의 비교사용기를 ...

 가장 많이 사용하고 있는 Spring Boot + JPA 로  Azure Cosmos DB를 연결하는 작업을 진행해보았다.

https://docs.microsoft.com/ko-kr/java/azure/spring-framework/configure-spring-boot-starter-java-app-with-cosmos-db?view=azure-java-stable


1. maven pom.xml에 아래와 같이 추가하고.

<dependency>

   <groupId>com.microsoft.azure</groupId>

   <artifactId>azure-documentdb-spring-boot-starter</artifactId>

   <version>2.0.4</version>

</dependency>


2. JPA에서 제공하는 CRUD Repository를 이용해서 다음과 같이 작업했다.

public interface DeviceTelemetryRepository extends DocumentDbRepository<DeviceTelemetry, String>{

    List<DeviceTelemetry> findAll();

    @Query(value="SELECT * FROM DeviceTelemetry where deviceId= ?1 and date >= ?2 and date <= ?3", nativeQuery = true)
    List<DeviceTelemetry> findDeviceTelemetryByDeviceIdAndDateGreaterThanAndDateLessThan(@Param("deviceId") int deviceId,
                                                               @Param("from") long from,
                                                               @Param("to") long to    );

}


3. Entity클래스의 경우 테이블에 따라서 잦은 변경이 일어나기 때문에 Controller, Service Layer에서는 DTO클래스를 별도 로 사용하는 것을 권장한다. (디자인 패턴 참고)

@Service
public class DeviceTelemetryService {
@Autowired
private DeviceTelemetryRepository deviceTelemetryRepository;

@Autowired
private ModelMapper modelMapper;

public List<DeviceTelemetryDto> getDeviceTelemetry(int deviceId, long from, long to){
return deviceTelemetryRepository.findDeviceTelemetryByDeviceIdAndDateBetween(deviceId, from, to).stream()
.map(deviceTelemetry -> modelMapper.map(deviceTelemetry, DeviceTelemetryDto.class))
.collect(Collectors.toList());
}

}

4. Controller 까지 완성하여. 테스트하였는데 아래와 같은 오류가 발생한다.

@RequestMapping(path="/api/")
public class DeviceTelemetryController {

@Autowired
private DeviceTelemetryService deviceTelemetryService;


@RequestMapping(value="/{serviceId}/{deviceId}/telemetry", method= RequestMethod.GET)
public @ResponseBody
List<DeviceTelemetryDto> getAllDeviceTelemetry(@PathVariable("deviceId") int deviceId,
@RequestParam("from") long from,
@RequestParam("to") long to) {

return deviceTelemetryService.getDeviceTelemetry(deviceId,from,to);
}
}

java.lang.IllegalArgumentException: unsupported keyword: GREATER_THAN (1): [IsGreaterThan, GreaterThan]
	at com.microsoft.azure.spring.data.documentdb.repository.query.DocumentDbQueryCreator.from(DocumentDbQueryCreator.java:82) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at com.microsoft.azure.spring.data.documentdb.repository.query.DocumentDbQueryCreator.and(DocumentDbQueryCreator.java:56) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at com.microsoft.azure.spring.data.documentdb.repository.query.DocumentDbQueryCreator.and(DocumentDbQueryCreator.java:25) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:122) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:95) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:81) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
	at com.microsoft.azure.spring.data.documentdb.repository.query.PartTreeDocumentDbQuery.createQuery(PartTreeDocumentDbQuery.java:38) ~[spring-data-cosmosdb-2.0.3.jar:na]
	at com.microsoft.azure.spring.data.documentdb.repository.query.AbstractDocumentDbQuery.execute(AbstractDocumentDbQuery.java:25) ~[spring-data-cosmosdb-2.0.3.jar:na]


5. Azure Cosmos DB에 문의하니 다음과 같은 답변을 받았다.

spring-data-cosmosdb 2.0.6을 사용해야 하는데 아무리 찾아도 azure-documentdb-spring-boot-starter에 포함된 최신버전은 2.0.4가 최신버전이다!

다시 문의를 했다. 수동으로 업데이트해야하는가?

결론은 "azure-documentdb는 구 네이빙 버전입니다. 신버전을 사용하세요."


Thanks for filing issue, according to your stack trace, seems you are using version 2.0.3, if you are using Spring Boot 2.0.x, try upgrading to 2.0.6.


You can use the azure-cosmosdb-spring-boot-starter(not azure-documentdb-spring-boot-starter) version 2.0.13, which uses spring-data-cosmosdb 2.0.6. Between should have been supported in 2.0.6, check this issue. Also you can reference the integration tests in this repo for the usage.


6. pom.xml에서 azure-cosmosdb-spring-boot-starter 최신버전으로 변경하였다.

<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-cosmosdb-spring-boot-starter</artifactId>
<version>2.0.13</version>
</dependency>


7. 하지만 Spring JPA의 naming rule에 같은컬럼을 중복해서 사용하면 에러가 발생한다.

BETWEEN 쿼리로 변경!

(GREATER_THAN, BETWEEN등은 위에서 언급한 2.0.6 버전에서 정상지원한다.)



8. Repository 소스 변경(@Query는 없어도 쿼리를 자동으로 생성하여 동작한다)

public interface DeviceTelemetryRepository extends DocumentDbRepository<DeviceTelemetry, String> {

List<DeviceTelemetry> findAll();

// @Query(value="SELECT * FROM DeviceTelemetry where deviceId= ?1 and date >= ?2 and date <= ?3", nativeQuery = true)
List<DeviceTelemetry> findDeviceTelemetryByDeviceIdAndDateBetween(@Param("deviceId") int deviceId,
@Param("from") long from,
@Param("to") long to );
}

9. Azure Cosmos DB에서 정상적으로 데이터를 조회하는 것을 확인할 수 있다.

[

{ "id": "3ebd07c0-0740-466f-acb4-1e04a58cdf1a", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":34.797642257199705,\"humidity\":79.18982439419167,\"illuminance\":100}", "date": 1552376519931 }, { "id": "9424f15a-e452-4dcc-8ff6-bc3707b7ec3a", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":25.964463142640522,\"humidity\":73.64654000868197,\"illuminance\":100}", "date": 1552376579937 }, { "id": "c72aed1b-b4d0-4338-a21f-ae8b7e5b4eba", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":28.32913263660401,\"humidity\":73.40374660917695,\"illuminance\":100}", "date": 1552376639938 }, { "id": "c1571a80-7eb6-49dc-be9f-32457e41f69a", "serviceId": 1, "deviceId": 1, "contents": "{\"temperature\":30.035071643495087,\"humidity\":70.52682127516005,\"illuminance\":100}", "date": 1552376699940 } ]

참고사이트)

1. 위 내용 관련하여 MS Github에서 주고 받은 내용들

https://github.com/Microsoft/spring-data-cosmosdb/issues/347


2. 현재 google 에서 조회하면 가장 상단에 Azure SDK for Java Stable 나오는 페이지

https://docs.microsoft.com/ko-kr/java/azure/spring-framework/configure-spring-boot-starter-java-app-with-cosmos-db?view=azure-java-stable




+ Recent posts