Java 8에서 최신 Java 런타임으로 마이그레이션

이 페이지에서는 1세대에서 2세대 Java 런타임으로 마이그레이션하는 방법을 안내합니다. 지원되는 최신 Java 버전을 사용하도록 2세대 앱을 업그레이드하는 방법은 기존 애플리케이션 업그레이드를 참조하세요.

Java 8 지원은 2024년 1월 31일에 종료되었습니다. 기존 Java 8 애플리케이션은 계속 실행되고 트래픽을 수신합니다. 그러나 App Engine에서 지원 종료 날짜 이후에 런타임을 사용하는 애플리케이션의 재배포를 차단할 수 있습니다. 이 페이지의 가이드라인을 수행하여 지원되는 최신 Java 버전으로 마이그레이션하는 것이 좋습니다.

2세대 Java 런타임으로 마이그레이션하면 최신 언어 기능을 사용하고 관용구 코드를 통해 포팅 가능한 앱을 빌드할 수 있습니다.

마이그레이션 옵션 이해

런타임 마이그레이션 효과와 복잡성이 줄어들도록 App Engine 표준 환경에서는 2세대 Java 런타임에서 Memcache와 같은 여러 기존 번들 서비스와 API에 액세스할 수 있습니다. Java 앱은 App Engine API JAR을 통해 번들 서비스 API를 호출하고 Java 8 런타임에서와 같이 대부분의 동일한 기능에 액세스할 수 있습니다.

기존 번들 서비스와 비슷한 기능을 제공하는 Google Cloud 제품을 사용할 수도 있습니다. 이러한 Google Cloud 제품은 관용적인 Java용 Cloud 클라이언트 라이브러리를 제공합니다. 이미지 처리, 검색, 메시징과 같은 Google Cloud에서 별도의 제품으로 제공되지 않는 번들 서비스의 경우 타사 제공업체 또는 다른 해결 방법을 사용할 수 있습니다.

번들되지 않은 서비스로 마이그레이션하는 방법에 대한 자세한 내용은 번들 서비스에서 마이그레이션을 참조하세요.

기존 번들 서비스를 사용할지 여부에 따라 런타임 마이그레이션을 수행하는 방식에 몇 가지 차이점이 있습니다.

번들 서비스를 사용하여 2세대 Java 런타임으로 마이그레이션 번들 서비스를 사용하지 않고 2세대 Java 런타임으로 마이그레이션
App Engine API JAR을 사용하여 번들 서비스에 액세스합니다. 필요한 경우 권장 Google Cloud 제품 또는 타사 서비스를 사용합니다.

앱 구성에는 appengine-web.xmlweb.xml을 사용합니다.

앱에서 사용하는 기능에 따라 추가 YAML 파일을 구성해야 할 수도 있습니다.

앱 구성에 app.yaml을 사용합니다.

앱에서 사용하는 기능에 따라 추가 YAML 파일을 구성해야 할 수도 있습니다.

앱은 Jetty를 통해 배포됩니다. WAR 형식을 사용하여 앱을 패키징합니다. 앱은 자체 서버를 사용하여 배포됩니다. JAR 형식을 사용하여 앱을 패키징합니다. 기존 WAR 파일을 실행 가능한 JAR로 변환하는 방법에 대한 자세한 내용은 WAR 파일 다시 패키징을 참조하세요.

마이그레이션 프로세스 개요

다음은 App Engine Java 런타임을 사용하려면 기존 2세대 Java 8 앱과 배포 프로세스에 적용해야 할 수도 있는 몇 가지 변경사항입니다.

Java 8과 2세대 Java 런타임 간의 주요 차이점

다음은 App Engine 표준 환경에서 Java 8과 2세대 Java 런타임 간의 차이점을 요약한 내용입니다.

Java 8 런타임 2세대 Java 런타임
서버 배포 Jetty를 사용하여 배포된 서버 앱에서 기존 번들 서비스를 사용하지 않는 경우 직접 서버를 배포해야 합니다.1
App Engine 기존 번들 서비스 제공된 전화번호 제공된 전화번호
Java용 Cloud 클라이언트 라이브러리를 사용하는 기능
언어 확장 프로그램 및 시스템 라이브러리 지원
외부 네트워크 액세스
파일 시스템 액세스 /tmp에 대한 읽기/쓰기 액세스 /tmp에 대한 읽기/쓰기 액세스
언어 런타임 App Engine용으로 수정됨 수정되지 않은 오픈소스 런타임
격리 메커니즘 gVisor 기반 컨테이너 샌드박스 gVisor 기반 컨테이너 샌드박스
로컬 개발 서버에서 테스트 지원됨 지원됨
스레드 안전 구성 appengine-web.xml 파일에서 지정할 수 있습니다. 구성 파일에서 지정할 수 없습니다. 모든 앱이 스레드 안전성을 갖는 것으로 간주됩니다.3
로깅 stderr에 쓰고
각 레코드 후에
스트림을 플러시하는 java.util.logging.
ConsoleHandler를 사용합니다.
표준 Cloud Logging 2
DataNucleus 플러그인 2.x 지원 지원됨 지원되지 않음4

참고:

  1. 앱에서 기존 번들 서비스를 사용하지 않는 경우 PORT 환경 변수(권장)나 포트 8080에서 지정한 포트에서 HTTP 요청에 응답하도록 구성된 웹 서버를 패키지하는 한 2세대 Java 런타임은 어떤 Java 프레임워크라도 실행할 수 있습니다. 예를 들어 2세대 Java 런타임은 Spring Boot Uber JAR을 있는 그대로 실행할 수 있습니다. 더 많은 예시는 프레임워크 유연성 섹션을 참조하세요.

    앱이 기존 번들 서비스를 사용하는 경우 App Engine은 Java 8 런타임과 동일한 방식으로 Jetty를 사용하여 배포합니다.

  2. 2세대 Java 런타임의 로깅은 Cloud Logging의 로깅 표준을 따릅니다. 2세대 Java 런타임에서 앱 로그는 더 이상 요청 로그와 번들로 묶이지 않고 다른 레코드로 분리됩니다. 2세대 Java 런타임에서 로그를 읽고 쓰는 방법에 대한 자세한 내용은 로깅 가이드를 참조하세요.

  3. 2세대 Java 런타임에서 비 스레드 안전 앱을 구성하려면 Java 8에서 <threadsafe>false</threadsafe>를 설정하는 것과 마찬가지로 app.yaml 파일 또는 appengine-web.xml 파일에서 최대 동시 실행을 1로 설정하세요(기존 번들 서비스를 사용하는 경우).

  4. Google은 2세대 런타임에서 DataNucleus 라이브러리를 지원하지 않습니다. 최신 버전의 DataNucleus는 Java 8에 사용된 이전 버전과 호환되지 않습니다. Datastore에 액세스하려면 Datastore 모드 클라이언트 라이브러리 또는 Java Objectify(버전 6 이상) 솔루션을 사용하는 것이 좋습니다. Objectify는 Datastore용 오픈소스 API로, 더 높은 수준의 추상화를 제공합니다.

메모리 사용량 차이

2세대 런타임은 1세대 런타임에 비해 메모리 사용량 기준이 더 높습니다. 이는 여러 다른 기본 이미지 버전, 두 세대 간의 메모리 사용량 계산 방법의 차이와 같은 여러 요인 때문에 발생합니다.

2세대 런타임은 애플리케이션 프로세스가 사용하는 항목의 합계 및 메모리에 동적으로 캐시된 애플리케이션 파일 수에 따라 인스턴스 메모리 사용량을 계산합니다. 메모리 사용량이 높은 애플리케이션에서 메모리 한도 초과로 인한 인스턴스 종료가 발생하지 않도록 방지하려면 메모리가 많은 더 큰 인스턴스 클래스로 업그레이드하세요.

CPU 사용량 차이

2세대 런타임은 인스턴스 콜드 스타트 시 CPU 사용량 기준이 더 높을 수 있습니다. 그 결과 애플리케이션의 확장 구성에 따라 애플리케이션이 CPU 사용률을 기준으로 확장되도록 구성된 경우 예상한 것보다 높은 인스턴스 수와 같은 의도치 않은 부작용이 발생할 수 있습니다. 이러한 문제를 방지하기 위해서는 애플리케이션 확장 구성을 검토 및 테스트해서 인스턴스 수를 적정 수준으로 유지해야 합니다.

요청 헤더 차이

1세대 런타임에서는 밑줄이 사용된 요청 헤더(예: X-Test-Foo_bar)를 애플리케이션에 전달할 수 있습니다. 2세대 런타임에는 호스트 아키텍처에 Nginx가 도입되었습니다. 이러한 변경사항으로 인해 2세대 런타임은 밑줄(_)이 있는 헤더를 자동으로 삭제하도록 구성됩니다. 애플리케이션 문제를 방지하기 위해서는 애플리케이션 요청 헤더에 밑줄을 사용하지 않아야 합니다.

프레임워크 유연성

기존 번들 서비스를 사용하지 않는 경우 2세대 Java 런타임에 웹 제공 프레임워크가 포함되지 않습니다. 즉, 서블릿 기반 프레임워크 이외의 프레임워크를 사용할 수 있습니다. 기존 번들 서비스를 사용하는 경우 2세대 Java 런타임에서 Jetty 웹 제공 프레임워크를 제공합니다.

Google Cloud GitHub 저장소에 인기 있는 Java 웹 프레임워크를 사용하는 hello world 샘플이 있습니다.

XML 파일 형식을 YAML 파일 형식으로 마이그레이션

gcloud CLI는 다음 파일 형식을 지원하지 않습니다.

  • cron.xml
  • datastore-index.xml
  • dispatch.xml
  • queue.xml

다음 예시는 xml 파일을 yaml 파일로 마이그레이션하는 방법을 보여줍니다.

자동으로 파일 마이그레이션

xml 파일을 자동으로 마이그레이션하려면 다음을 수행합니다.

  1. gcloud CLI 버전 226.0.0 이상이 있어야 합니다. 최신 버전으로 업데이트하려면 다음을 실행합니다.

    gcloud components update
    
  2. 마이그레이션하려는 각 파일에 cron-xml-to-yaml, datastore-indexes-xml-to-yaml, dispatch-xml-to-yaml, queue-xml-to-yaml 하위 명령 중 하나와 파일 이름을 지정합니다.

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. 프로덕션에 배포하기 전에 변환된 파일을 수동으로 다시 확인합니다.

    샘플 xml 파일을 yaml 파일로 성공적으로 변환하려면 수동으로 파일 마이그레이션 탭을 참조하세요.

수동으로 파일 마이그레이션

xml 파일을 yaml 파일로 수동으로 마이그레이션하려면 다음을 수행합니다.

cron.yaml

아래 나온 것처럼 cron.yaml 파일의 각 <cron> 태그 속성에 해당하는 필드가 각각 있는 객체 목록이 포함된 cron 객체로 cron.xml 파일을 만듭니다.

전환된 cron.yaml 파일:

cron:
- url: '/recache'
  schedule: 'every 2 minutes'
  description: 'Repopulate the cache every 2 minutes'
- url: '/weeklyreport'
  schedule: 'every monday 08:30'
  target: 'version-2'
  timezone: 'America/New_York'
  description: 'Mail out a weekly report'

원본 cron.xml 파일:

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/recache</url>
    <description>Repopulate the cache every 2 minutes</description>
    <schedule>every 2 minutes</schedule>
  </cron>
  <cron>
    <url>/weeklyreport</url>
    <description>Mail out a weekly report</description>
    <schedule>every monday 08:30</schedule>
    <timezone>America/New_York</timezone>
    <target>version-2</target>
  </cron>
</cronentries>

자세한 내용은 cron.yaml 참조 문서를 확인하세요.

dispatch.yaml

아래 나온 것처럼 dispatch.yaml 파일의 각 <dispatch> 태그 속성에 해당하는 필드가 각각 있는 객체 목록이 포함된 dispatch 객체로 dispatch.xml 파일을 만듭니다.

전환된 dispatch.yaml 파일:

dispatch:
- url: '*/favicon.ico'
  module: default
- url: 'simple-sample.uc.r.appspot.com/'
  module: default
- url: '*/mobile/*'
  module: mobile-frontend

원본 dispatch.xml 파일:

<?xml version="1.0" encoding="UTF-8"?>
<dispatch-entries>
  <dispatch>
      <url>*/favicon.ico</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>simple-sample.uc.r.appspot.com/</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>*/mobile/*</url>
      <module>mobile-frontend</module>
  </dispatch>
</dispatch-entries>

자세한 내용은 dispatch.yaml 참조 문서를 확인하세요.

index.yaml

아래 나온 것처럼 index.yaml 파일의 각 <datastore-index> 태그 속성에 해당하는 필드가 각각 있는 객체 목록이 포함된 indexes 객체로 datastore-indexes.xml 파일을 만듭니다.

전환된 index.yaml 파일:

indexes:
- ancestor: false
  kind: Employee
  properties:
  - direction: asc
    name: lastName
  - direction: desc
    name: hireDate
- ancestor: false
  kind: Project
  properties:
  - direction: asc
    name: dueDate
  - direction: desc
    name: cost

원본 datastore-index.xml 파일:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
 autoGenerate="true">
   <datastore-index kind="Employee" ancestor="false">
       <property name="lastName" direction="asc" />
       <property name="hireDate" direction="desc" />
   </datastore-index>
   <datastore-index kind="Project" ancestor="false">
       <property name="dueDate" direction="asc" />
       <property name="cost" direction="desc" />
   </datastore-index>
</datastore-indexes>

자세한 내용은 index.yaml 참조 문서를 확인하세요.

queue.yaml

아래 나온 것처럼 queue.yaml 파일의 각 <queue> 태그 속성에 해당하는 필드가 각각 있는 객체 목록이 포함된 queue 객체로 queue.xml 파일을 만듭니다.

전환된 queue.yaml 파일:

queue:
- name: fooqueue
  mode: push
  rate: 1/s
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d
- name: barqueue
  mode: push
  rate: 1/s
  retry_parameters:
    min_backoff_seconds: 10
    max_backoff_seconds: 200
    max_doublings: 0

원본 queue.xml 파일:

<queue-entries>
  <queue>
    <name>fooqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <task-retry-limit>7</task-retry-limit>
      <task-age-limit>2d</task-age-limit>
    </retry-parameters>
  </queue>
  <queue>
    <name>barqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <min-backoff-seconds>10</min-backoff-seconds>
      <max-backoff-seconds>200</max-backoff-seconds>
      <max-doublings>0</max-doublings>
    </retry-parameters>
  </queue>
<queue-entries>