μ•ˆλ…•ν•˜μ„Έμš” Mambo μž…λ‹ˆλ‹€. 이번 글은 AWS Elastic Beanstalk의 Java SE ν”Œλž«νΌ ν™˜κ²½μ„ μ‚¬μš©ν•˜μ—¬ 개발된 μŠ€ν”„λ§ λΆ€νŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λŠ” 과정에 λŒ€ν•΄μ„œ ν•™μŠ΅ν•©λ‹ˆλ‹€. AWSλ₯Ό μ‚¬μš©μ€‘μ΄λΌλ©΄ Elastic Beanstalk μ„œλΉ„μŠ€λ₯Ό 톡해 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ‹€ν–‰λ˜λŠ” ν™˜κ²½μ„ λΉ λ₯΄κ³  μ‰½κ²Œ ꡬ성할 수 있으며 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜κ³  ν™˜κ²½ μ„±λŠ₯ μ§€ν‘œμ— 따라 ν”„λ‘œλΉ„μ €λ‹ν•  수 μžˆλŠ” κΈ°λŠ₯을 μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

저와 같이 Elastic Beanstalk κ²½ν—˜μ΄ λΆ€μ‘±ν•œ 초보 개발자 λ˜λŠ” νšŒμ‚¬μ—μ„œ μ„œλΉ„μŠ€ 배포λ₯Ό λ‹΄λ‹Ήν•˜μ§€ μ•ŠλŠ” κ°œλ°œμžλΆ„λ“€μ€ 이 글을 톡해 Elastic Beanstlak둜 μžλ°” μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 μžˆλŠ” ν™˜κ²½μ„ κ΅¬μ„±ν•˜κ³  μ–΄λ–€ λ°©μ‹μœΌλ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 μžˆλŠ”μ§€λ₯Ό 이해할 수 μžˆμ„ 것 μž…λ‹ˆλ‹€.

개발 κ²½λ ₯은 4년이 μ§€λ‚¬μœΌλ‚˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 배포λ₯Ό λ‹΄λ‹Ήν•˜λŠ” 것은 μ–Όλ§ˆλ˜μ§€ μ•Šμ•˜μ–΄μš”β€¦

AWS Elastic Beanstalk

AWSμ—μ„œ μ œκ³΅ν•˜λŠ” Elastic Beanstalkμ„œλΉ„μŠ€λŠ” AWS ν΄λΌμš°λ“œ ν™˜κ²½μ—μ„œ λΉ λ₯΄κ²Œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜κ³  μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ κ΅¬λ™λ˜λŠ” ν™˜κ²½μ„ μ‰½κ²Œ 관리할 수 μžˆλŠ” AWS의 μ£Όμš” μ„œλΉ„μŠ€μž…λ‹ˆλ‹€. Beanstalk을 μ‚¬μš©ν•˜λ©΄ Go, Java, Node.js, Python λ“± λ‹€μ–‘ν•œ μ–Έμ–΄λ‘œ 개발된 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 μžˆμŠ΅λ‹ˆλ‹€.

이 κΈ€μ—μ„œ 배포할 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ†ŒμŠ€ μ½”λ“œλŠ” κΉƒν—ˆλΈŒ beanstalk-deploy-sample에 κ³΅μœ λ˜μ–΄μžˆμœΌλ‹ˆ μ°Έκ³ ν•˜μ‹œκΈ°λ₯Ό λ°”λžλ‹ˆλ‹€.

μ—„μ²­ κ°„λ‹¨ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄μ—μš”!

Java SE ν”Œλž«νΌ

Elastic BeanstalkλŠ” λ‹€μ–‘ν•œ μ–Έμ–΄λ‘œ μž‘μ„±λœ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 있게 μ—¬λŸ¬ ν”Œλž«νΌ ν™˜κ²½μ„ μ§€μ›ν•©λ‹ˆλ‹€. 이 μ€‘μ—μ„œ μ œκ°€ μ€€λΉ„ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 μžˆλŠ” ν™˜κ²½μ€ Java SE ν”Œλž«νΌμž…λ‹ˆλ‹€.

λ³„λ„λ‘œ Tomcat ν”Œλž«νΌμ΄ μ‘΄μž¬ν•˜κΈ°λŠ” ν•˜μ§€λ§Œ 이 κΈ€μ—μ„œμ˜ μ£Όμš” κ΄€μ‹¬μ‚¬λŠ” μ•„λ‹ˆμ—μš”.

Elastic Beanstalk Java SE ν”Œλž«νΌμ€ 자체적으둜 μ‹€ν–‰κ°€λŠ₯ν•˜λ„λ‘ 컴파일된 JAR 파일으둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰ν•  수 μžˆλŠ” ν™˜κ²½μœΌλ‘œ μ œκ°€ μ€€λΉ„ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μŠ€ν”„λ§ λΆ€νŠΈ 기반으둜 μž‘μ„±λ˜μ–΄μžˆμœΌλ―€λ‘œ 이 ν™˜κ²½μ΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 μžˆλŠ” κ°€μž₯ μ ν•©ν•œ ν™˜κ²½μž…λ‹ˆλ‹€. μŠ€ν”„λ§ λΆ€νŠΈμ—μ„œ μ œκ³΅ν•˜λŠ” 지원 쀑 ν•˜λ‚˜λŠ” μ‹€ν–‰κ°€λŠ₯ν•œ JAR λ˜λŠ” WAR 파일으둜 νŒ¨ν‚€μ§•ν•  수 μžˆλŠ” λΆ€λΆ„μž…λ‹ˆλ‹€.

κΈ°λ³Έ 배포 μœ ν˜•

Elastic Beanstalk의 Java SE ν”Œλž«νΌ ν™˜κ²½μ€ 두가지 λ°©μ‹μœΌλ‘œ ν™˜κ²½μ— μ—…λ‘œλ“œλœ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ νŒŒμΌμ„ 배포할 수 있게 μ§€μ›ν•©λ‹ˆλ‹€. 기본적으둜 λ°°ν¬λ˜λŠ” 방식은 단일 파일둜 자체적으둜 μ‹€ν–‰κ°€λŠ₯ν•˜λ„λ‘ 컴파일된 JAR νŒŒμΌμ„ μ΄μš©ν•˜λŠ” κ²ƒμœΌλ‘œ λ‹€μŒμ˜ λͺ…λ Ήμ–΄λ₯Ό μˆ˜ν–‰ν•˜μ—¬ Java SE ν”Œλž«νΌ ν™˜κ²½μ—μ„œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ΅¬λ™ν•©λ‹ˆλ‹€.

κΈ°λ³Έ μ‹€ν–‰ λͺ…λ Ήμ–΄
java -jar application.jar

μœ„ λͺ…λ Ήμ—μ„œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 파일λͺ…이 application.jar인 μ΄μœ λŠ” μš°λ¦¬κ°€ ν™˜κ²½μ— μ—…λ‘œλ“œν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ νŒŒμΌμ„ application.jarλΌλŠ” μ΄λ¦„μœΌλ‘œ λ³€κ²½ν•˜κΈ° λ•Œλ¬Έμ΄λ©° μ—¬λŸ¬λΆ„μ΄ μ–΄λ–€ 파일의 μ΄λ¦„μœΌλ‘œ μ œκ³΅ν•˜λ“  상관이 μ—†μŠ΅λ‹ˆλ‹€.

μ‚¬μš©μž μ •μ˜ 배포

λ‘λ²ˆμ§ΈλŠ” λ‹€μˆ˜μ˜ 컴파일된 JAR λ˜λŠ” Procfileμ΄λΌλŠ” λͺ…λ Ήμ–΄ νŒŒμΌμ„ 톡해 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰ν•  수 μžˆλŠ” λ°©μ‹μž…λ‹ˆλ‹€. 이 방식을 μ‚¬μš©ν•˜λ©΄ ν•˜λ‚˜μ˜ ν™˜κ²½μ—μ„œ μ—¬λŸ¬κ°œμ˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰ν•  수 μžˆκ±°λ‚˜ 직접 μ‹€ν–‰ν•  수 μžˆλŠ” λͺ…λ Ήμ–΄λ₯Ό μ •μ˜ν•˜λ―€λ‘œ JVM μ˜΅μ…˜ λ˜λŠ” μ»€λ§¨λ“œ 라인 νŒŒλΌλ―Έν„°λ₯Ό μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Procfile
web: java -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Xmx1g -jar app.jar

μ΄λ ‡κ²Œ νŒ¨ν‚€μ§•λœ JAR 파일과 Procfile을 λ¬Άμ–΄μ„œ μ œκ³΅ν•˜λŠ” νŒŒμΌμ„ Elastic Beanstalkμ—μ„œλŠ” μ†ŒμŠ€ λ²ˆλ“€(Source Bundle)이라고 λ§ν•©λ‹ˆλ‹€. μ†ŒμŠ€ λ²ˆλ“€ νŒŒμΌμ„ Java SE ν”Œλž«νΌ ν™˜κ²½μ— μ—…λ‘œλ“œν•˜μ—¬ λ°°ν¬ν•˜λŠ” 방식은 μ•„λ§ˆμ‘΄ μ›Ή μ„œλΉ„μŠ€μ—μ„œλ„ μΆ”μ²œν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.

Linux ν”Œλž«νΌ ν™•μž₯

μ†ŒμŠ€ λ²ˆλ“€ 파일둜 μ‚¬μš©μž μ •μ˜ 배포 방식을 μ‚¬μš©ν•˜λŠ” 경우 .ebextensions, .platformμ΄λΌλŠ” 폴더λ₯Ό ν¬ν•¨μ‹œμΌœ Java SE ν”Œλž«νΌ ν™˜κ²½μ— λŒ€ν•œ ν™•μž₯을 μˆ˜ν–‰ν•  수 μžˆλ„λ‘ μ§€μ›ν•©λ‹ˆλ‹€. ν”Œλž«νΌ ν™•μž₯에 λŒ€ν•œ μžμ„Έν•œ λ‚΄μš©μ€ AWS Elastic Beanstalk 개발자 κ°€μ΄λ“œμ˜ Elastic Beanstalk Linux ν”Œλž«νΌ ν™•μž₯λ₯Ό 톡해 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μΈμŠ€ν„΄μŠ€ 배포 μ›Œν¬ν”Œλ‘œμš°λ₯Ό μ°Έκ³ ν•˜λ©΄ μ—…λ‘œλ“œλœ μ†ŒμŠ€ λ²ˆλ“€ νŒŒμΌμ„ 톡해 Elastic Beanstalk이 μ–΄λ–»κ²Œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. ν”Œλž«νΌ ν™•μž₯을 톡해 λ‹€μŒμ˜ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ν•˜κΈ° μ „ λͺ…λ Ήμ–΄ μˆ˜ν–‰
  • μ—­λ°©ν–₯ ν”„λ‘μ‹œ ꡬ성

μš°μ•„ν•œ ν˜•μ œλ“€ 기술 λΈ”λ‘œκ·Έμ˜ Elastic Beanstalk Configuration files(.ebextensions)μ—μ„œμ²˜λŸΌ ν”Œλž«νΌ ν™•μž₯을 톡해 λͺ¨λ‹ˆν„°λ§ νŒ¨ν‚€μ§€λ₯Ό μ„€μΉ˜ν•˜κ±°λ‚˜ 둜컬 νƒ€μž„μ„ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λΉŒλ“œλΆ€ν„° λ°°ν¬κΉŒμ§€

Elastic Beanstalk의 Java SE ν”Œλž«νΌ ν™˜κ²½μ— λŒ€ν•΄μ„œ μ•Œκ²Œλ˜μ—ˆμœΌλ‹ˆ μ΄μ œλΆ€ν„° μ œκ°€ μ€€λΉ„ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ Java SE ν”Œλž«νΌ ν™˜κ²½μ— λ°°ν¬ν•˜λŠ” 과정을 ν™•μΈν•˜λ©΄μ„œ λΉŒλ“œλΆ€ν„° λ°°ν¬κΉŒμ§€μ˜ ν”„λ‘œμ„ΈμŠ€λ₯Ό μˆ™μ§€ν•΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

μ—¬λŸ¬λΆ„λ“€λ„ λ”°λΌν•˜κ³  μ‹Άλ‹€λ©΄ kdevkr/beanstalk-deploy-sample을 μ°Έκ³ ν•˜μ‹œκΈ° λ°”λžλ‹ˆλ‹€.

μ‹€ν–‰κ°€λŠ₯ν•œ JAR 파일 μ€€λΉ„ν•˜κΈ°

μŠ€ν”„λ§ λΆ€νŠΈλŠ” κ·Έλž˜λ“€(Gradle) λ˜λŠ” 메이븐(Maven) 도ꡬλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‹€ν–‰κ°€λŠ₯ν•œ JAR λ˜λŠ” WAR νŒŒμΌμ„ νŒ¨ν‚€μ§•ν•  수 μžˆλŠ” 방법을 μ œκ³΅ν•©λ‹ˆλ‹€. μ €λŠ” κ·Έλž˜λ“€μ„ λΉŒλ“œ λ„κ΅¬λ‘œ μ‚¬μš©ν•˜λŠ” μŠ€ν”„λ§ λΆ€νŠΈ ν”„λ‘œμ νŠΈλ₯Ό κ΅¬μ„±ν•˜μ—¬ λ‹€μŒκ³Ό 같이 λΉŒλ“œ νƒœμŠ€ν¬μ— bootJar와 bootWarκ°€ ν¬ν•¨λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€.

μ‹€ν–‰κ°€λŠ₯ν•œ JAR 파일둜만 배포할 수 μžˆλŠ” 것이 μ•„λ‹˜μ„ ν™•μΈν•˜κΈ° μœ„ν•΄ μ €λŠ” bootWar νƒœμŠ€ν¬λ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹€ν–‰κ°€λŠ₯ν•œ WAR 파일둜 νŒ¨ν‚€μ§•ν•˜κ² μŠ΅λ‹ˆλ‹€. 이 νƒœμŠ€ν¬λ‘œ νŒ¨ν‚€μ§•λœ WAR νŒŒμΌμ„ build/libs 폴더에 μƒμ„±λ©λ‹ˆλ‹€.

μ΄λ ‡κ²Œ νŒ¨ν‚€μ§•λœ WAR νŒŒμΌμ„ 확인할 수 μžˆμœΌλ‚˜ Java SE ν”Œλž«νΌμ— μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λŠ” 방식 쀑 μ‚¬μš©μž μ •μ˜ 배포 방식을 μœ„ν•΄ μƒμ„±λœ WAR 파일과 ν•¨κ»˜ 이 νŒŒμΌμ„ μ‹€ν–‰ν•˜λŠ” λͺ…λ Ήμ–΄λ₯Ό μ •μ˜ν•œ Procfile을 λ§Œλ“€μ–΄ ν•˜λ‚˜μ˜ μ†ŒμŠ€ λ²ˆλ“€λ‘œ λ§Œλ“œλŠ” 과정을 μ§„ν–‰ν•˜κ² μŠ΅λ‹ˆλ‹€.

μ†ŒμŠ€ λ²ˆλ“€ νŒŒμΌμ„ λ§Œλ“€κΈ° μœ„ν•΄μ„œ build/libs 폴더에 직접 Procfile을 μƒμ„±ν•˜μ—¬ μ‹€ν–‰ λͺ…λ Ήμ–΄λ₯Ό μ •μ˜ν•˜λ©΄ μ•ˆλ©λ‹ˆλ‹€. build ν΄λ”λŠ” Gitμ—μ„œ λ¬΄μ‹œλ˜λŠ” 경둜이며 clean νƒœμŠ€ν¬μ— μ˜ν•΄μ„œ μ‰½κ²Œ μ‚­μ œλ  수 μžˆμŠ΅λ‹ˆλ‹€. κ°€μž₯ μ‰½κ²Œ λ– μ˜€λ₯΄λŠ” 방법은 ν”„λ‘œμ νŠΈ 루트 κ²½λ‘œμ— Procfile을 λ§Œλ“€μ–΄μ„œ build/libs에 νŒ¨ν‚€μ§•λœ WAR파일과 μ••μΆ•ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

λ‹€ν–‰νžˆλ„ κ·Έλž˜λ“€ νƒœμŠ€νŠΈ μœ ν˜• μ€‘μ—μ„œ μ•„μΉ΄μ΄λΈŒ(μ••μΆ• 파일)을 λ§Œλ“€ 수 μžˆλŠ” Zip νƒœμŠ€ν¬ μœ ν˜•μ΄ μžˆμœΌλ―€λ‘œ 이 μœ ν˜•μ— λŒ€ν•œ νƒœμŠ€ν¬λ₯Ό μž‘μ„±ν•˜μ—¬ μ†ŒμŠ€ λ²ˆλ“€μ„ λ§Œλ“œλŠ” νƒœμŠ€ν¬λ₯Ό μˆ˜ν–‰ν•˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

λ¨Όμ €, ν”„λ‘œμ νŠΈ 루트 κ²½λ‘œμ— Procfile을 λ§Œλ“€μ–΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ λͺ…λ Ήμ–΄λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

Procfile
web: java -Xmx1g -jar demo.war

그리고 νŒ¨ν‚€μ§•λœ WAR 파일과 Procfile을 μ••μΆ•ν•œ νŒŒμΌμ„ μƒμ„±ν•˜λŠ” νƒœμŠ€ν¬λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

build.gradle
task zipSourceBundle(type: Zip, dependsOn: 'bootWar') { from ('Procfile') { include('Procfile') } from ('build/libs') { println bootWar.archiveName include(bootWar.archiveName) } baseName = 'beanstalk' }

μž‘μ„±λœ zipSourceBundle νƒœμŠ€ν¬λ₯Ό μˆ˜ν–‰ν•˜λ©΄ build/distributions 폴더에 beanstalk.zipμ΄λΌλŠ” μ†ŒμŠ€ λ²ˆλ“€ 파일이 생성됨을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

그런데 λ§μž…λ‹ˆλ‹€. λ§Œμ•½, bootWar νƒœμŠ€ν¬μ— μ˜ν•΄ λ§Œλ“€μ–΄μ§€λŠ” 파일λͺ…이 demo.warκ°€ μ•„λ‹ˆλΌλ©΄ μ–΄λ–»κ²Œ λ κΉŒμš”? Procfile에 μ •μ˜ν•œ λͺ…령어와 μΌμΉ˜ν•˜μ§€ μ•Šμ•„ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λŠ” κ³Όμ •μ—μ„œ 였λ₯˜κ°€ λ°œμƒν•  κ²ƒμž…λ‹ˆλ‹€. 이 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλŠ” 쒋은 방법은 κ·Έλž˜λ“€ λΉŒλ“œ κ³Όμ •μ—μ„œ Procfile을 μƒμ„±ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

build.gradle
task procfile(dependsOn: 'bootWar') { doFirst { new File("build/libs", "Procfile").text = "web: java -Xmx1g -jar ${bootWar.archiveName}" } }

μž‘μ„±λœ procfile νƒœμŠ€ν¬λŠ” bootWar νƒœμŠ€ν¬μ— μ˜ν•΄ λ§Œλ“€μ–΄μ§€λŠ” WAR 파일λͺ…을 μ£Όμž…ν•˜μ—¬ Procfile을 build/libs μœ„μΉ˜μ— μƒμ„±ν•©λ‹ˆλ‹€. 그리고 μ•žμ„œ μž‘μ„±ν•œ zipSourceBundleλ₯Ό λ³΄μ™„ν•œ zipBeanstalk νƒœμŠ€ν¬λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

build.gradle
task zipBeanstalk(type: Zip, dependsOn: 'procfile') { from ('build/libs') { println bootWar.archiveName include(bootWar.archiveName) include("Procfile") } baseName = 'beanstalk' }

이제 zipBeanstalk νƒœμŠ€ν¬λ₯Ό μˆ˜ν–‰ν•˜μ˜€λ”λ‹ˆ λ‹€μŒκ³Ό 같이 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ†ŒμŠ€ λ²ˆλ“€ 파일이 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

κ·Έλž˜λ“€ λΉŒλ“œ μ‹œ ν”„λ‘œμ νŠΈ 버전(project.version)이 λͺ…μ‹œλ˜μ–΄λ„ Procfileκ³Ό νŒ¨ν‚€μ§•λœ WAR 파일λͺ…이 λ‹€λ₯Έ κ²½μš°κ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈ 버전을 λͺ…μ‹œν•˜λ©΄ μ•„μΉ΄μ΄λΈŒ 파일λͺ…에 ν”„λ‘œμ νŠΈ 버전이 ν¬ν•¨λ˜μš”!

-Pversion=1.0.0

였옷! μ’‹μ•„μš”.

Java SE ν”Œλž«νΌ ν™˜κ²½ μ‹œμž‘ν•˜κΈ°

μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ†ŒμŠ€ λ²ˆλ“€ 파일이 μ€€λΉ„λ˜μ—ˆμœΌλ―€λ‘œ Elastic Beanstalk μ„œλΉ„μŠ€μ—μ„œ μžλ°” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 μžˆλŠ” ν™˜κ²½μ„ μƒμ„±ν•©λ‹ˆλ‹€. ν™˜κ²½μ— λŒ€ν•œ 이름, 도메인을 μ„€μ •ν•œ ν›„ μš°λ¦¬κ°€ μ€€λΉ„ν•œ μŠ€ν”„λ§ λΆ€νŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ 자체적으둜 μ‹€ν–‰λ˜λŠ” JAR둜 컴파일된 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄λ―€λ‘œ Java SE ν”Œλž«νΌ ν™˜κ²½μ„ μ„ νƒν•©λ‹ˆλ‹€.

μ›Ή μ„œλ²„ ν™˜κ²½ 선택

ν™˜κ²½ 및 도메인 μ„€μ •

Java SE ν”Œλž«νΌ 선택

μ•žμ„œ μ€€λΉ„ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ†ŒμŠ€ λ²ˆλ“€νŒŒμΌμ„ μ„ νƒν•˜μ—¬ μ—…λ‘œλ“œν•©λ‹ˆλ‹€.

주의!

μœ„ ν™”λ©΄μ—μ„œ ν™˜κ²½ 생성 λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ 기본으둜 μ •μ˜λ˜μ–΄μžˆλŠ” μΈμŠ€ν„΄μŠ€ ν”„λ‘œνŒŒμΌμ΄ κ΅¬μ„±λœ Java SE ν”Œλž«νΌ ν™˜κ²½μ„ κ΅¬μ„±ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 배포할 수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ£Όμ˜ν•΄μ•Όλ  사항은 λ‹€μŒ 처럼 ν™˜κ²½μ„ μƒμ„±ν•˜κ³ λ‚˜μ„œ λ³€κ²½ν•  수 μ—†λŠ” 뢀뢄듀이 μžˆλ‹€λŠ” μ μž…λ‹ˆλ‹€.

κΈ°λ³Έ Java SE ν”Œλž«νΌμ€ CLB(Classic Load Balancer) λ₯Ό λ‘œλ“œλ°ΈλŸ°μ„œλ‘œ μ‚¬μš©ν•˜λŠ”λ° ν™˜κ²½μ΄ μƒμ„±λœ 이후에 λ‘œλ“œ λ°ΈλŸ°μ„œ μœ ν˜•μ„ λ³€κ²½ν•˜κ³ μž ꡬ성 νŽΈμ§‘μ„ μ‹œλ„ν•˜μ˜€μ§€λ§Œ μœ ν˜•μ„ λ³€κ²½ν•˜λŠ” 것을 μ œκ³΅ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ, L7 레벨의 λ‘œλ“œλ°ΈλŸ°μ„œμΈ ALB(Application Load Balancer) λ˜λŠ” L4 레벨의 NLB(Network Load Balancer) λ₯Ό ν™˜κ²½μ— λŒ€ν•œ λ‘œλ“œλ°ΈλŸ°μ„œλ‘œ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” μΆ”κ°€ μ˜΅μ…˜ ꡬ성 κΈ°λŠ₯으둜 μ‚¬μš©μž μ •μ˜ ν™˜κ²½μ„ μƒμ„±ν•΄μ•Όν•©λ‹ˆλ‹€.

κΈ°λ³Έ VPCλ₯Ό μ§€μ›Œμ„œ VPC ν™˜κ²½μ΄ μ—†λŠ” μ±„λ‘œ λ§Œλ“€μ–΄μ§„...

aws ec2 create-default-vpc λͺ…λ ΉμœΌλ‘œ κΈ°λ³Έ VPCλ₯Ό λ‹€μ‹œ λ§Œλ“€μˆ˜λŠ” μžˆλ‹€β€¦

Java SE ν”Œλž«νΌμ—μ„œ 기본적으둜 μ œκ³΅ν•˜λŠ” Nginx ν”„λ‘μ‹œ ꡬ성은 HTTP(80) νŠΈλž˜ν”½μ— λŒ€ν•΄ 5000 포트λ₯Ό μ‚¬μš©ν•˜λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ λΌμš°νŒ…λ˜λ„λ‘ μ„€μ •λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€. Beanstalk에 μ˜ν•΄ μƒμ„±λœ EC2 μΈμŠ€ν„΄μŠ€μ— μ ‘μ†ν•΄μ„œ Nginx μ„€μ • 정보λ₯Ό 찾아보면 λ‹€μŒκ³Ό 같이 00_application.conf νŒŒμΌμ— ν”„λ‘μ‹œ ꡬ성이 μ •μ˜λ˜μ–΄ μžˆμŒμ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

/etc/nginx/conf.d/elasticbeanstalk/00_application.conf
[root@ip-172-31-40-185 ~]# cat /etc/nginx/conf.d/elasticbeanstalk/00_application.conf location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }

그런데 μ œκ°€ λ°°ν¬ν•œ μŠ€ν”„λ§ λΆ€νŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ λ³„λ„λ‘œ μ‹€ν–‰λ˜λŠ” 포트λ₯Ό μ§€μ •ν•˜μ§€ μ•Šμ•„ 8080 포트λ₯Ό ν• λ‹Ήν•©λ‹ˆλ‹€.

[root@ip-172-31-40-185 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name           
tcp6       0      0 :::8080                 :::*                    LISTEN      3512/java 

Elastic Beanstalk은 5000 ν¬νŠΈμ— λŒ€ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μƒνƒœλ₯Ό ν™•μΈν•˜λ―€λ‘œ Elastic Beanstalk은 ν™˜κ²½ μƒνƒœλ₯Ό μ˜¬λ°”λ₯΄μ§€ μ•Šλ‹€κ³  ν”Όλ“œλ°±ν•˜κ²Œ λ©λ‹ˆλ‹€. λ”°λΌμ„œ, λ‘œλ“œλ°ΈλŸ°μ„œκ°€ νŠΈλž˜ν”½μ„ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ λΌμš°νŒ…ν•  수 μžˆλ„λ‘ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 5000 포트둜 ν• λ‹Ήν•˜λ„λ‘ ν•΄μ•Όν•©λ‹ˆλ‹€. λ‹€λ§Œ, μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 5000 포트λ₯Ό μ‚¬μš©ν•˜λ„λ‘ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ν”„λ‘œνΌν‹° 쀑 server.port λ₯Ό 5000으둜 μ§€μ •ν•˜κ³  λ‹€μ‹œ νŒ¨ν‚€μ§•ν•˜μ—¬ λ°°ν¬ν•˜λŠ” 것은 λΆˆνŽΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ‹€μ‹œ νŒ¨ν‚€μ§•ν•˜μ§€ μ•Šλ”λΌλ„ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ κ΅¬λ™λ˜λŠ” 포트λ₯Ό λ³€κ²½ν•  수 μžˆλŠ” 방법을 고렀해보도둝 ν•˜μ£ .

  1. κΈ°λ³Έ μ—­λ°©ν–₯ ν”„λ‘μ‹œ ꡬ성 νŒŒμΌμ— μ •μ˜λœ 포트 λ³€κ²½
  • PORT ν™˜κ²½ μ†μ„±μœΌλ‘œ κΈ°λ³Έ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μˆ˜μ‹  λŒ€κΈ°ν•˜λŠ” 포트λ₯Ό μž¬μ •μ˜
  1. JVM μ˜΅μ…˜μ„ μ§€μ •ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ 포트 λ³€κ²½

1번 방법은 기본으둜 μ •μ˜λ˜λŠ” Nginx ν”„λ‘μ‹œ ꡬ성을 λ³€κ²½ν•˜λŠ” λ°©λ²•μœΌλ‘œ PORTλΌλŠ” ν™˜κ²½ 속성에 따라 00_application.conf의 λ‚΄μš©μ„ μ •μ˜ν•©λ‹ˆλ‹€.

PORT ν™˜κ²½ 속성 μ •μ˜

00_application.conf
[root@ip-172-31-40-185 ~]# cat /etc/nginx/conf.d/elasticbeanstalk/00_application.conf location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }

PORT ν™˜κ²½ 속성에 따라 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ν”„λ‘μ‹œ ν¬νŠΈκ°€ λ³€κ²½λ˜μ—ˆμ–΄μš”!

μŠ€ν”„λ§ λΆ€νŠΈ 기반의 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ°œλ°œν•˜λŠ” 개발자라면 ν™˜κ²½ μ†μ„±μ΄λΌλŠ” 이름을 보고 μŠ€ν”„λ§ λΆ€νŠΈκ°€ μ§€μ›ν•˜λŠ” μ™ΈλΆ€ν™” ꡬ성(Externalized Configuration)을 λ– μ˜¬λžμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. μ™ΈλΆ€ν™” ꡬ성 μˆœμ„œ 5번 ν•­λͺ©μ— OS ν™˜κ²½ λ³€μˆ˜κ°€ μžˆλŠ”λ° μΈν…”λ¦¬μ œμ΄λ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ ꡬ동할 λ•Œ ν™˜κ²½ λ³€μˆ˜λ₯Ό μ§€μ •ν•˜μ—¬ ν”„λ‘œνΌν‹° 값을 μ„€μ •ν•  수 μžˆλŠ” 것이 이 λΆ€λΆ„μž…λ‹ˆλ‹€.

μ΄λ²ˆμ—λŠ” PORT ν™˜κ²½ 속성이 μ•„λ‹Œ SERVER_PORT ν™˜κ²½ 속성에 5000을 μ„€μ •ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 5000 포트둜 κ΅¬λ™λ˜λŠ”μ§€λ₯Ό ν™•μΈν•΄λ΄…μ‹œλ‹€.

SERVER_PORT ν™˜κ²½ 속성

[root@ip-172-31-40-185 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name              
tcp6       0      0 :::5000                 :::*                    LISTEN      6244/java           

SERVER_PORT ν™˜κ²½ 속성에 μ •μ˜λœ 포트둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ‹€ν–‰λ˜μ—ˆμ–΄μš”!

JVM μ˜΅μ…˜ μ„€μ •

ν™˜κ²½ 속성을 톡해 SERVER_PORTλ₯Ό μ •μ˜ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ν”„λ‘œνΌν‹°λ₯Ό λ³€κ²½ν•  수 μžˆμ§€λ§Œ JVM μ˜΅μ…˜μœΌλ‘œ server.port에 λŒ€ν•œ κ°’μœΌλ‘œ 5000을 μ§€μ •ν•˜λŠ” 것을 μ‹œλ„ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. 일반적으둜 JVM μ˜΅μ…˜μ„ μ§€μ •ν•˜κΈ° μœ„ν•΄μ„œλŠ” -Dserver.port와 같이 java λͺ…λ Ήμ–΄λ₯Ό μˆ˜ν–‰ν•  λ•Œ μ§€μ •ν•΄μ•Όν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ μš°λ¦¬λŠ” Procfile에 μ‹€ν–‰ λͺ…λ Ήμ–΄λ₯Ό μ •μ˜ν•΄λ†“μ•˜μœΌλ―€λ‘œ 좔가적인 JVM μ˜΅μ…˜μ„ 지정할 수 μžˆλŠ” λ°©μ•ˆμ΄ μ—†μŠ΅λ‹ˆλ‹€.

Elastic Beanstalkμ—μ„œ JVM μ˜΅μ…˜μ„ μ§€μ •ν•˜λŠ” 방법을 검색해보면 JAVA_TOOL_OPTIONS ν™˜κ²½ λ³€μˆ˜λ‘œ 지정할 수 μžˆμŒμ„ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€. JAVA_TOOL_OPTIONS ν™˜κ²½ λ³€μˆ˜ 외에도 JVM λ§ˆλ‹€ JAVA_OPTS λ˜λŠ” _JAVA_OPTIONSλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€λ§Œ JAVA_TOOL_OPTIONS이 JVMTI ν‘œμ€€ μŠ€νŽ™μ΄λ―€λ‘œ JAVA_TOOL_OPTIONS을 μ‚¬μš©ν•˜μ—¬ JVM μ˜΅μ…˜μ„ μ„€μ •ν•˜λŠ” 게 μ˜¬λ°”λ₯Έ λ°©λ²•μž…λ‹ˆλ‹€.

Java SE ν”Œλž«νΌ ν™˜κ²½μ˜ Correcto JDKλŠ” _JAVA_OPTIONS ν™˜κ²½ 속성도 μ μš©λ˜μš”!

λ‹€μŒμ€ Elastic Beanstalk Java SE ν”Œλž«νΌ ν™˜κ²½μ—μ„œ JAVA_TOOL_OPTIONS와 _JAVA_OPTIONS ν™˜κ²½ 속성을 μ§€μ •ν–ˆμ„ λ•Œ 적용 μˆœμ„œμž…λ‹ˆλ‹€.

Mar  5 16:37:54 ip-172-31-40-185 web: Picked up JAVA_TOOL_OPTIONS: -Dserver.port=5000
Mar  5 16:37:54 ip-172-31-40-185 web: Picked up _JAVA_OPTIONS: -Dserver.port=5001

[root@ip-172-31-40-185 ~]# jps -v
3634 war -Dserver.port=5000 -Xmx1g -Dserver.port=5001
3740 Jps -Dapplication.home=/usr/lib/jvm/java-11-amazon-corretto.x86_64 -Xms8m -Djdk.module.main=jdk.jcmd

JAVA_TOOL_OPTIONS 보닀 _JAVA_OPTIONS ν™˜κ²½ 속성이 λ‚˜μ€‘μ— 적용되며 Procfile에 μ •μ˜λœ JVM μ˜΅μ…˜κ³Όμ˜ μˆœμ„œλ„ λ‹€λ₯Έ 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μΆ”κ°€ μ˜΅μ…˜ ꡬ성

기본으둜 μ •μ˜λœ ν™˜κ²½μœΌλ‘œ μ‹œμž‘ν•˜κΈ°λ³΄λ‹€λŠ” μΆ”κ°€ μ˜΅μ…˜ ꡬ성 κΈ°λŠ₯을 톡해 μ‚¬μš©μž μ •μ˜λœ Java SE ν”Œλž«νΌ ν™˜κ²½μ„ κ΅¬μ„±ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. 사전 섀정을 톡해 μΈμŠ€ν„΄μŠ€ 규λͺ¨λ₯Ό 선택할 수 μžˆμœΌλ‚˜ 직접 ν™˜κ²½ μ˜΅μ…˜μ„ μ •μ˜ν•˜κΈ° μœ„ν•΄ μ‚¬μš©μž 지정 ꡬ성 을 μ„ νƒν•©λ‹ˆλ‹€.

μ‚¬μš©μž 지정 ꡬ성 μ„ νƒμ‹œ μ œκ³΅λ˜λŠ” κΈ°λ³Έ μ˜΅μ…˜μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. λͺ‡κ°€μ§€ μ˜΅μ…˜μ— λŒ€ν•΄μ„œ μˆ˜μ •μ„ 해보도둝 ν•˜μ£ .

μ†Œν”„νŠΈμ›¨μ–΄

κ°€μž₯ λ¨Όμ €, μ†Œν”„νŠΈμ›¨μ–΄ μ˜΅μ…˜μ„ νŽΈμ§‘ν•˜μ—¬ 기본으둜 μ§€μ •λœ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ν”„λ‘μ‹œ ν¬νŠΈμ— 맞게 싀행될 수 μžˆλ„λ‘ SERVER_PORT ν™˜κ²½λ³€μˆ˜λ₯Ό λ“±λ‘ν•©λ‹ˆλ‹€.

μš©λŸ‰

μš©λŸ‰ μ˜΅μ…˜μ„ νŽΈμ§‘ν•˜μ—¬ Elastic Beanstalk이 EC2 μΈμŠ€ν„΄μŠ€λ₯Ό μ–΄λ–»κ²Œ μ‹€ν–‰ν•˜κ³  관리할지 μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κΈ°λ³Έ 단일 μΈμŠ€ν„΄μŠ€ ν™˜κ²½μ€ λ‘œλ“œλ°ΈλŸ°μ„œλ₯Ό μ‚¬μš©ν•  수 μ—†μœΌλ―€λ‘œ μ˜€ν†  μŠ€μΌ€μΌλ§ κΈ°λŠ₯을 ν™œμ„±ν™”ν•˜κΈ° μœ„ν•˜μ—¬ λ‘œλ“œ λ°ΈλŸ°μ‹± μˆ˜ν–‰ ν™˜κ²½μœΌλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€. λ‘œλ“œ λ°ΈλŸ°μ‹± μˆ˜ν–‰ ν™˜κ²½μ΄μ§€λ§Œ μ²˜μŒμ—λŠ” 단일 μΈμŠ€ν„΄μŠ€ 둜 μ‹œμž‘ν•˜κΈ° μœ„ν•˜μ—¬ μΈμŠ€ν„΄μŠ€μ˜ μ΅œλŒ€ 크기λ₯Ό 1둜 μ§€μ •ν•©λ‹ˆλ‹€.

λ§Œμ•½, μ—¬λŸ¬λΆ„μ˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μš”κ΅¬ν•˜λŠ” λ©”λͺ¨λ¦¬ 크기와 CPU μ„±λŠ₯이 μžˆλ‹€λ©΄ μΈμŠ€ν„΄μŠ€ μœ ν˜•μ„ λ³€κ²½ν•©λ‹ˆλ‹€. μ €λŠ” κ°„λ‹¨ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λ―€λ‘œ 기본으둜 μ„€μ •λœ t2.microλ₯Ό μ‚¬μš©ν•˜κ² μŠ΅λ‹ˆλ‹€.

t2.microλŠ” 1GB의 λ©”λͺ¨λ¦¬ 크기λ₯Ό κ°€μ§‘λ‹ˆλ‹€.

λ‘œλ“œλ°ΈλŸ°μ„œ

μš©λŸ‰ μ˜΅μ…˜μ—μ„œ λ‘œλ“œ λ°ΈλŸ°μ‹± ν™˜κ²½μ„ μ„ νƒν•˜λ©΄ λ‘œλ“œλ°ΈλŸ°μ„œ μ˜΅μ…˜μ„ μ„€μ •ν•  수 있게 ν™œμ„±ν™”λ©λ‹ˆλ‹€.

기본으둜 μ§€μ •λœ λ‘œλ“œλ°ΈλŸ°μ„œλŠ” L7 레벨의 ALB(Application Load Balancer)으둜 λ³€κ²½ν•  ν•„μš”λŠ” μ—†μ§€λ§Œ νšŒμ‚¬μ—μ„œλŠ” λΉ λ₯Έ νŠΈλž˜ν”½ 처리λ₯Ό μœ„ν•΄ L4 레벨의 NLB(Network Load Balancer)λ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ NLB둜 μ§€μ •ν•˜κ² μŠ΅λ‹ˆλ‹€.

각 λ‘œλ“œλ°ΈλŸ°μ„œκ°€ μ§€μ›ν•˜λŠ” κΈ°λŠ₯이 κΆκΈˆν•˜λ‹€λ©΄ Elastic Load Balancing κΈ°λŠ₯을 μ°Έκ³ ν•˜μ„Έμš”. λ‘œλ“œλ°ΈλŸ°μ„œκ°€ νŠΈλž˜ν”½μ„ μ²˜λ¦¬ν•˜λŠ” λ¦¬μŠ€λ„ˆμ™€ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ„€μ •ν•  수 μžˆμ§€λ§Œ μ—¬κΈ°μ„œλŠ” λ„˜μ–΄κ°€λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

λ³΄μ•ˆ

λ³΄μ•ˆ μ˜΅μ…˜μ„ νŽΈμ§‘ν•˜μ—¬ EC2 μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Όν•  수 μžˆλŠ” κΆŒν•œμ„ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν‚€ νŽ˜μ–΄λ₯Ό μ§€μ •ν•˜λ©΄ EC2 μ½˜μ†”μ„ 톡해 μ—°κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ„€νŠΈμ›Œν¬

λ„€νŠΈμ›Œν¬ μ˜΅μ…˜μ„ νŽΈμ§‘ν•˜μ—¬ VPCλ₯Ό μ„ νƒν•˜κ³  λ‘œλ“œλ°ΈλŸ°μ„œμ™€ μΈμŠ€ν„΄μŠ€κ°€ μ‚¬μš©ν•  μ„œλΈŒλ„· λŒ€μ—­μ„ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이제 μ˜΅μ…˜ ꡬ성이 λλ‚¬μœΌλ―€λ‘œ ν™˜κ²½ 생성 λ²„νŠΌμ„ ν΄λ¦­ν•˜μ—¬ Elastic Beanstalk이 ν™˜κ²½μ„ κ΅¬μ„±ν•˜λŠ” 것을 κΈ°λ‹€λ¦½λ‹ˆλ‹€. Elastic Beanstalkκ°€ μƒμ„±ν•œ ν™˜κ²½μ˜ μƒνƒœλ₯Ό ν™•μΈν•˜κ³  μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ νŠΈλž˜ν”½μ΄ μ²˜λ¦¬λ˜λŠ”μ§€λ₯Ό ν™•μΈν•˜λ©΄ λ©λ‹ˆλ‹€.

Elastic Beanstalk이 ν™˜κ²½μ„ μƒμ„±ν•˜λŠ” 것을 κΈ°λ‹€λ ΈμœΌλ‚˜ μ œκ°€ μ‹€μˆ˜λ‘œ μΈμŠ€ν„΄μŠ€ μ„œλΈŒλ„·μ„ 잘λͺ» μ§€μ •ν•˜μ—¬ μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ˜μ§€ λͺ»ν•˜μ˜€μœΌλ‚˜ Elastic BeanstalkλŠ” ν™˜κ²½ 생성을 μœ„ν•΄ μƒνƒœλ₯Ό 계속 ν™•μΈν•˜κ³  κΈ°λ‹€λ¦¬λŠ” λ¬΄ν•œ 루프 증상에 λΉ μ Έλ²„λ ΈμŠ΅λ‹ˆλ‹€.

μΈμŠ€ν„΄μŠ€ μ„œλΈŒλ„·μ„ μ˜¬λ°”λ₯΄κ²Œ μ§€μ •ν•˜κΈ° μœ„ν•΄μ„œ ν™˜κ²½ ꡬ성 νŽ˜μ΄μ§€λ‘œ κ°€λ³΄μ•˜μ§€λ§Œ Elastic BeanstalkλŠ” ν™˜κ²½μ— λŒ€ν•œ 이벀트λ₯Ό μˆ˜ν–‰ν•˜κ³  μžˆμ–΄ μ–΄λ– ν•œ μž‘μ—…λ„ 진행할 수 μ—†μŠ΅λ‹ˆλ‹€.

이처럼 ν™˜κ²½μ— λŒ€ν•œ μž‘μ—…μ΄ μˆ˜ν–‰λ˜κ³  μžˆμ„λ•ŒλŠ” μ–΄λ– ν•œ μš”μ²­λ„ μˆ˜ν–‰ν•  수 μ—†λŠ” 단점이…

λΉ„ν™œμ„±ν™”λœ ν™˜κ²½ μž‘μ—…

μ œλŒ€λ‘œ μƒμ„±λ˜μ§€ μ•ŠλŠ” Beanstalk ν™˜κ²½ μ‚­μ œν•˜κΈ°

Elastic Beanstalk μ„œλΉ„μŠ€μ—μ„œλŠ” μœ„ ν™˜κ²½ 생성 μž‘μ—…μ„ 쀑지할 수 μžˆλŠ” 방법이 μ—†μŠ΅λ‹ˆλ‹€. λ‹€λ§Œ, AWS μ„œλΉ„μŠ€ 쀑 CloudFormation을 κ²€μƒ‰ν•˜μ—¬ 듀어가보면 Elastic Beanstalk ν™˜κ²½μ„ κ΅¬μ„±ν•˜λŠ” μŠ€νƒμ„ 찾을 수 있고 CREATE_IN_PROGRESS μƒνƒœμ— 머물러 μžˆλŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. 이 μŠ€νƒμ„ μ„ νƒν•˜μ—¬ μ‚­μ œν•˜λ©΄ λ©λ‹ˆλ‹€.

Java SE ν”Œλž«νΌ ν™˜κ²½μ„ CloudFormation둜 μ •μ˜ν•΄μ„œ κ΅¬μ„±ν•˜λŠ”κ΅°μš”?

Beanstalk ν™˜κ²½μ„ μƒμ„±ν•˜λŠ” μŠ€νƒμ΄ μ‚­μ œλ˜μ–΄ Beanstalk μ„œλΉ„μŠ€λ‘œ λ‹€μ‹œ 가보면 생성할 λ•Œ 였λ₯˜κ°€ λ°œμƒν–ˆλ˜ ν™˜κ²½μ΄ μ‚­μ œλ˜κ³  μžˆμŒμ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. λ‹€ν–‰μž…λ‹ˆλ‹€.

단, μ‚­μ œλœ ν™˜κ²½ 이름을 λ‹€μ‹œ μ‚¬μš©ν•˜κ³  싢은 경우 1μ‹œκ°„λ™μ•ˆ κΈ°λ‹€λ €μ•Όν•΄μš”β€¦

ν™˜κ²½μ„ μ •μƒμ μœΌλ‘œ κ΅¬μ„±ν•œ 경우 λ‹€μŒκ³Ό 같이 배포가 μ™„λ£Œλ˜μ—ˆμŒμ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

νœ΄β€¦

ν”Œλž«νΌ ν™•μž₯ ꡬ성

Elastic Beanstalk은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ†ŒμŠ€ λ²ˆλ“€μ— ν¬ν•¨λœ ꡬ성 및 ν”Œλž«νΌ νŒŒμΌμ„ 톡해 ν™˜κ²½μ— λŒ€ν•œ ꡬ성을 ν™•μž₯ν•˜λŠ” 것을 μ§€μ›ν•©λ‹ˆλ‹€. ꡬ성 νŒŒμΌμ€ .ebextensions 폴더, ν”Œλž«νΌ νŒŒμΌμ€ .platform 폴더에 μœ„μΉ˜ν•˜κ²Œ λ©λ‹ˆλ‹€. μ €λŠ” 인프라에 λŒ€ν•œ νŠœλ‹ 방법에 λŒ€ν•΄μ„œλŠ” 잘 λͺ¨λ₯΄κΈ° λ•Œλ¬Έμ— 인터넷에 곡유된 λ‚΄μš©μ„ μ†Œκ°œν•˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

EC2 μΈμŠ€ν„΄μŠ€ νƒ€μž„μ‘΄ λ³€κ²½ν•˜κΈ°

AWS의 elastic-beanstalk-samples/configuration-filesμ—λŠ” EC2 μΈμŠ€ν„΄μŠ€μ˜ νƒ€μž„μ‘΄μ„ λ³€κ²½ν•  수 μžˆλŠ” ꡬ성 파일 예제(timezone-linux.config)κ°€ μžˆμŠ΅λ‹ˆλ‹€.

.ebextensions/00-timezone-linux.config
option_settings: - namespace: aws:elasticbeanstalk:application:environment option_name: TZ value: "Asia/Seoul" files: "/tmp/set_timezone.sh": mode: "000755" owner: root group: root content: | #!/bin/bash NEWTIMEZONE="$(/opt/elasticbeanstalk/bin/get-config environment -k TZ)" if [ -z $NEWTIMEZONE ] ; then echo "TZ" environment property not set exit 1 fi if [ ! -f /usr/share/zoneinfo/$NEWTIMEZONE ] ; then echo /usr/share/zoneinfo/$NEWTIMEZONE does not exist exit 1 fi echo 'ZONE="'$NEWTIMEZONE'"' > /etc/sysconfig/clock echo 'UTC=true' >> /etc/sysconfig/clock ln -f -s /usr/share/zoneinfo/$NEWTIMEZONE /etc/localtime commands: 00-custom-timezone: command: /tmp/set_timezone.sh

κΈ°μ‘΄ μ˜ˆμ œμ™€ λ‹€λ₯΄κ²Œ ntpdate둜 NTP μ„œλ²„ 동기화 λͺ…λ Ήμ–΄λ₯Ό μˆ˜ν–‰ν•˜μ§€ μ•Šκ³  Linux μΈμŠ€ν„΄μŠ€μ˜ μ‹œκ°„ 섀정에 따라 Amazon Time Sync Service와 λ™κΈ°ν™”λ˜λŠ” μ‹œκ°„μ„ κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜κ² μŠ΅λ‹ˆλ‹€.

TZ ν™˜κ²½μ†μ„±

[root@ip-10-1-0-52 ~]# date
Sun Mar  7 13:58:57 KST 2021

λͺ…λ Ήμ–΄κ°€ μˆ˜ν–‰λ˜μ–΄ ν•œκ΅­ μ‹œκ°„μœΌλ‘œ ν‘œμ‹œλœλ‹€.

Node Exporter μ—μ΄μ „νŠΈ μ„€μΉ˜

OKKYμ—μ„œ μš΄μ˜νšŒμ›μœΌλ‘œ ν™œλ™μ€‘μ΄μ‹  창천ν–₯λ‘œλ‹˜μ˜ AWS Beanstalk μ„±λŠ₯ νŠœλ‹ μ‹œλ¦¬μ¦ˆμ—μ„œμ²˜λŸΌ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„±λŠ₯ λͺ¨λ‹ˆν„°λ§μ„ μœ„ν•œ APM μ—μ΄μ „νŠΈλ₯Ό μ„€μΉ˜ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μ €λŠ” ν”„λ‘œλ©”ν…Œμš°μŠ€λ‘œ EC2 μΈμŠ€ν„΄μŠ€μ— λŒ€ν•œ λ§€νŠΈλ¦­μ„ μˆ˜μ§‘ν•  수 μžˆλ„λ‘ Beanstalk둜 μƒμ„±λ˜λŠ” EC2 μΈμŠ€ν„΄μŠ€μ— Node Exporterλ₯Ό μ„€μΉ˜ν•˜κ³  μ‹€ν–‰ν•˜λ„λ‘ ν™•μž₯ ꡬ성을 μ§„ν–‰ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

λ¨Όμ € Nginx ν”„λ‘μ‹œ ꡬ성에 Node Exporter에 λŒ€ν•œ ν”„λ‘μ‹œ ꡬ성 νŒŒμΌμ„ μΆ”κ°€ν•©λ‹ˆλ‹€.

.platform/nginx/conf.d/elasticbeanstalk/01_node_exporter.conf
location /metrics { proxy_pass http://127.0.0.1:9100/metrics; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }

μ—­λ°©ν–₯ ν”„λ‘μ‹œ ꡬ성을 톡해 /metrics에 λŒ€ν•œ νŠΈλž˜ν”½μ„ Node Exporter둜 λΌμš°νŒ…ν•  수 있게 λ©λ‹ˆλ‹€. 그럼 이제 Node Exporterλ₯Ό μ„€μΉ˜ν•˜κ³  μ‹€ν–‰ν•˜λŠ” ꡬ성 νŒŒμΌμ„ λ§Œλ“­λ‹ˆλ‹€.

.ebextensions/99-install-node-exporter.config
commands: command block: cwd: /home/webapp command: | curl -L https://github.com/prometheus/node_exporter/releases/download/v1.1.2/node_exporter-1.1.2.linux-amd64.tar.gz | tar zxv cd node_exporter-1.1.2.linux-amd64 nohup ./node_exporter > /dev/null 2>&1 &

μ—¬λŸ¬κ°€μ§€ λͺ…λ Ήμ–΄λ₯Ό ν•˜λ‚˜λ‘œ ν•©μΉ  수 μžˆλŠ” command block을 μ‚¬μš©ν–ˆμœΌλ©° Node Exporterλ₯Ό λ°±κ·ΈλΌμš΄λ“œλ‘œ μ‹€ν–‰ν•˜λ„λ‘ ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 이제 이 νŒŒμΌλ“€μ„ μ†ŒμŠ€ λ²ˆλ“€μ— ν¬ν•¨ν•˜μ—¬ ν™˜κ²½μ— λ°°ν¬ν•˜λ©΄ λ‹€μŒκ³Ό 같이 EC2 μΈμŠ€ν„΄μŠ€μ— λŒ€ν•œ μ§€ν‘œλ₯Ό μˆ˜μ§‘ν•  수 있게 λ©λ‹ˆλ‹€.

Beanstalk 배포 ν”„λ‘œμ„ΈμŠ€ 확인

Elastic Beanstalk둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜κΈ°κΉŒμ§€ λ¬Έμ œκ°€ λ°œμƒν–ˆμ„λ•Œ μ–΄λ–€ 뢀뢄을 μ²΄ν¬ν•΄μ•Όν•˜λŠ”μ§€ μ•Œμ•„λ³΄λ„λ‘ ν•˜μ£ .

둜그

κ°€μž₯ λ¨Όμ € 확인해야될 뢀뢄은 λ‘œκ·Έμž…λ‹ˆλ‹€.

/var/log
[root@ip-10-1-0-52 log]# ll -h total 736K drwx------ 3 root root 17 Mar 7 11:58 amazon -rw------- 1 root root 8.3K Mar 7 11:58 boot.log -rw------- 1 root utmp 15K Mar 7 13:36 btmp -rw-r--r-- 1 root root 6.9K Mar 7 14:45 cfn-hup.log -rw-r--r-- 1 root root 23K Mar 7 14:04 cfn-init-cmd.log -rw-r--r-- 1 root root 14K Mar 7 14:04 cfn-init.log -rw-r--r-- 1 root root 39K Mar 7 14:04 cfn-wire.log drwxr-xr-x 2 chrony chrony 6 Mar 7 11:58 chrony -rw-r--r-- 1 root root 93K Mar 7 11:58 cloud-init.log -rw-r--r-- 1 root root 2.5K Mar 7 11:58 cloud-init-output.log -rw------- 1 root root 9.4K Mar 7 14:40 cron -rw-r--r-- 1 root root 27K Mar 7 11:58 dmesg -rw-r--r-- 1 root root 0 Mar 7 11:58 eb-cfn-init-call.log -rw-r--r-- 1 root root 41K Mar 7 11:58 eb-cfn-init.log -rw-r--r-- 1 root root 137K Mar 7 14:04 eb-engine.log -rw-r--r-- 1 root root 1.3K Mar 7 14:30 eb-publish.log -rw-r--r-- 1 root root 455 Mar 7 12:30 eb-tools.log drwxr-xr-x 3 healthd healthd 39 Mar 7 12:00 healthd -rw------- 1 root root 204 Mar 7 11:58 maillog -rw------- 1 root root 205K Mar 7 14:47 messages drwxr-xr-x 4 nginx nginx 71 Mar 7 12:45 nginx drwxr-xr-x 2 root root 6 Mar 7 11:58 rotated drwxr-xr-x 2 root root 18 Mar 7 11:58 sa -rw------- 1 root root 41K Mar 7 14:32 secure -rw------- 1 root root 21K Mar 7 14:33 web.stdout.log -rw-rw-r-- 1 root utmp 7.9K Mar 7 14:32 wtmp drwxr-xr-x 2 xray xray 6 Feb 25 22:54 xray

둜그 νŒŒμΌλ“€μ€ /var/log 폴더에 μœ„μΉ˜ν•˜κ²Œ λ˜λŠ”λ° 주둜 ν™•μΈν•˜λŠ” λ‘œκ·ΈλŠ” eb-engine.log 와 web.stdout.log μž…λ‹ˆλ‹€.

  • eb-engine.log: Beanstalk ν”„λ‘œμ„ΈμŠ€ 둜그
  • web.stdout.log: ν˜„μž¬ μ‹€ν–‰λ˜κ³  μžˆλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 둜그
  • cfn-init-cmd.log: .eb-extensions ꡬ성 νŒŒμΌμ— μ •μ˜ν•œ λͺ…λ Ήμ–΄λ₯Ό μˆ˜ν–‰ν•œ 둜그

μ†ŒμŠ€ λ²ˆλ“€ 파일

μ—…λ‘œλ“œλœ μ†ŒμŠ€ λ²ˆλ“€μ— ν¬ν•¨λœ νŒŒμΌλ“€μ€ Elastic Beanstalk에 μ˜ν•΄μ„œ λ‹€μ–‘ν•œ μœ„μΉ˜λ‘œ λ³΅μ‚¬λ©λ‹ˆλ‹€.

λ¨Όμ €, ν˜„μž¬ 배포되고 μžˆλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— λŒ€ν•œ μ†ŒμŠ€ λ²ˆλ“€ 파일과 ν™˜κ²½ 속성이 μ •μ˜λœ νŒŒμΌμ€ /opt/elasticbeanstalk/deploymentμ—μ„œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

/opt/elasticbeanstalk/deployment
[root@ip-10-1-0-52 deployment]# ll -h total 18M -rw-r--r-- 1 root root 18M Mar 7 14:04 app_source_bundle -rw-r--r-- 1 root root 92 Mar 7 14:04 app_version_manifest.json -rw-r--r-- 1 root root 4.0K Mar 7 14:04 cfn-metadata-cache.json -r-------- 1 root root 211 Mar 7 14:04 env

그리고 μ†ŒμŠ€ λ²ˆλ“€(app_source_bundle)에 ν¬ν•¨λœ WAR 파일과 Procfile은 /var/app/current에 λ³΅μ‚¬λ˜μ–΄ μ‹€ν–‰ν•©λ‹ˆλ‹€. ν˜„μž¬ 배포되고 μžˆλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ œλŒ€λ‘œλœ λ²„μ „μœΌλ‘œ μ‹€ν–‰λ˜κ³  μžˆλŠ”μ§€ νŒŒμ•…ν•  수 μžˆκ² μŠ΅λ‹ˆλ‹€.

/var/app/current
[root@ip-10-1-0-52 current]# ll -h total 20M -rw-r--r-- 1 webapp webapp 20M Mar 7 14:02 demo-1.0.0.war -rw-r--r-- 1 webapp webapp 36 Mar 7 14:02 Procfile

ν”Œλž«νΌ(.platform)에 μΆ”κ°€λœ ν”„λ‘μ‹œ κ΅¬μ„±νŒŒμΌμ€ ν•΄λ‹Ή ν”„λ‘μ‹œ ν•˜μœ„λ‘œ λ³΅μ‚¬λ©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ•žμ„œ μž‘μ„±ν•΄λ³Έ 01_node_exporter.conf ν”„λ‘μ‹œ ꡬ성 νŒŒμΌμ€ /etc/nginx/conf.d/elasticbeanstalk에 λ³΅μ‚¬λ©λ‹ˆλ‹€.

/etc/nginx/conf.d/elasticbeanstalk
[root@ip-10-1-0-52 elasticbeanstalk]# ll -h total 12K -rw-r--r-- 1 root root 397 Mar 7 14:04 00_application.conf -rw-r--r-- 1 webapp webapp 420 Mar 7 00:05 01_node_exporter.conf -rw-r--r-- 1 root root 215 Mar 7 14:04 healthd.conf

/etc/nginx/conf.d/elasticbeanstalk ν΄λ”μ—λŠ” 기본으둜 μ œκ³΅λ˜λŠ” ν”„λ‘μ‹œ ꡬ성 파일과 ν•¨κ»˜ μ†ŒμŠ€ λ²ˆλ“€μ— ν¬ν•¨λœ ν”„λ‘μ‹œ ꡬ성 파일이 μ‘΄μž¬ν•˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

끝마치며

μ‹€μ œλ‘œ μ‹€λ¬΄μ—μ„œ Elastic Beanstalk둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λŠ” 것은 λ‹€μ–‘ν•œ λΆ€λΆ„μ—μ„œ κ²€ν† λ₯Ό μ§„ν–‰ν•΄μ•Όν•©λ‹ˆλ‹€. 개발된 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μš”κ΅¬ν•˜λŠ” μ΅œμ†Œ ν™˜κ²½ 사양에 따라 μΈμŠ€ν„΄μŠ€ μœ ν˜•μ„ μ„€μ •ν•΄μ•Όν•˜λ©° μ„œλΉ„μŠ€ ꡬ쑰에 따라 μœ μ—°ν•˜κ²Œ νŠΈλž˜ν”½μ„ μ²˜λ¦¬ν•  수 μžˆλŠ” ALBλ₯Ό μ μš©ν•  지 λΉ λ₯Έ νŠΈλž˜ν”½ 처리λ₯Ό μœ„ν•΄ NLB둜 λ‘œλ“œ λ°ΈλŸ°μ‹±μ„ λ‹¨μˆœν™” 해야할지λ₯Ό μ •ν•΄μ•Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Elastic Beanstalk둜 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λŠ” 것이 μ‰½λ‹€λŠ” μž₯점은 μžˆμ§€λ§Œ λ¬Έμ œκ°€ λ°œμƒν–ˆμ„λ•Œ ν•΄κ²°ν•˜λŠ” 것이 쉽지 μ•Šλ‹€λŠ” 단점 이 λΆ„λͺ…νžˆ μ‘΄μž¬ν•©λ‹ˆλ‹€. μ‹€μ œλ‘œ μ΅œκ·Όμ— μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ°°ν¬ν•˜λŠ” κ³Όμ •μ—μ„œ λ§Žμ€ λ³€κ²½μ‚¬ν•­λ“€λ‘œ μΈν•˜μ—¬ 배포 μ „ν™˜μ΄ μš©μ΄ν•˜μ§€ μ•Šμ•„ λ³„λ„λ‘œ ν™˜κ²½μ„ κ΅¬μ„±ν•˜κ³  νŠΈλž˜ν”½μ„ μ „ν™˜ν•˜λŠ” 과정을 μˆ˜ν–‰ν•˜κΈ°λ„ ν–ˆμŠ΅λ‹ˆλ‹€. μ‹€λ¬΄μ—μ„œ 예기치 λͺ»ν•˜κ²Œ λ°œμƒν•˜λŠ” λ¬Έμ œλ“€λ‘œ μΈν•˜μ—¬ μ‰½κ²Œ λ°°ν¬λ˜κ² μ§€λΌλŠ” 생각과 달리 배포가 쉽지 μ•Šμ•„ μž‘μ„±ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€. μ΄μƒμœΌλ‘œ AWS Elastic Beanstalk Java SE ν”Œλž«νΌ ν™˜κ²½μœΌλ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ°°ν¬ν•˜κΈ°λ₯Ό λ§ˆμΉ˜κ² μŠ΅λ‹ˆλ‹€.

κ°μ‚¬ν•©λ‹ˆλ‹€.