간단한 자바 스프링부트 앱을 컨테이너화 하여 이를 Azure의 컨테이너 서비스인 Azure Container Instances, Web App for Container, Azure Container Apps 그리고 Azure Kubernetes Service를 대상으로 배포하는 내용을 다루고자 한다.
참고로 이 실습을 따라 하기 위해서는 지난Part1,Part2에서 다룬 Azure SA 업무 환경 구성이 필요하다.
컨테이너는 클라우드 애플리케이션을 패키지, 배포 및 관리하기 위한 기본 방법으로 도입되고 있다. Azure Container Instances는 어떠한 가상 머신도 관리하지 않고 또 더 높은 수준의 서비스를 채택하지 않고도 Azure에서 컨테이너를 실행하는 가장 빠르고 간단한 방법을 제공한다.
Azure Container Instances는 VM을 프로비전 및 관리할 필요 없이 Azure에서 몇 초 안에 컨테이너를 시작할 수 있다.
Azure Container Instances는 간단한 애플리케이션, 작업 자동화 및 빌드 작업 등 격리된 컨테이너에서 작동할 수 있는 모든 시나리오에 적합한 솔루션이다. 여러 컨테이너 간 서비스 검색, 자동 크기 조정 및 조정된 애플리케이션 업그레이드를 포함하여 전체 컨테이너 오케스트레이션이 필요한 시나리오에는 AKS(Azure Kubernetes Service)를 사용하는 것이 좋다. 컨테이너 인스턴스를 배포할 때 모범 사례를 이해하려면 고려 사항 및 제한 사항과 FAQ를 읽어보는 것이 좋다.
Linux 및 Windows 컨테이너 : Windows의 경우 볼륨 탑재 및 GPU 리소스 등에 대한 제약 존재
Virtual network 배포 : Azure Container Instances를 사용하면 컨테이너 인스턴스를 Azure 가상 네트워크에 배포할 수 있다. 가상 네트워크 내의 서브넷에 배포되는 경우 컨테이너 인스턴스는 온-프레미스(VPN Gateway 또는 ExpressRoute를 통해)를 포함하여 가상 네트워크의 다른 리소스와 안전하게 통신할 수 있다.
스폿 컨테이너 배포 : 일반 우선 순위 ACI 컨테이너에 비해 최대 70% 할인된 가격으로 사용하지 않는 Azure 용량에서 중단 가능한 컨테이너화된 워크로드를 실행할 수 있다. ACI 스폿 컨테이너는 Azure에서 잉여 용량 부족이 발생할 때 선점될 수 있으며 엄격한 가용성 요구 사항이 없는 워크로드에 적합하다.
1.2 Web App for Container
Azure App Service를 사용하면 인프라를 관리할 필요 없이 선택한 프로그래밍 언어로 웹앱, 모바일 백 엔드 및 RESTful API를 빌드하고 호스트할 수 있다. 여기서는 자동 크기 조정 및 고가용성을 제공하고, Windows 및 Linux를 모두 지원하며, GitHub, Azure DevOps 또는 Git 리포지토리에서 자동화된 배포를 사용한다.
Azure App Service는 Docker 컨테이너 기술을 사용하여 기본 제공 이미지와 사용자 지정 이미지를 모두 호스팅한다.
Azure App Service on Linux는 .NET, PHP, Node.js 등과 같은 언어 지원을 통해 Linux에서 미리 정의된 애플리케이션 스택을 제공한다. Docker 컨테이너 기술을 사용하여 기본 제공 이미지와 사용자 지정 이미지를 모두 호스팅한다.
주요 기능 (Web App)
여러 언어 및 프레임워크 - App Service는 ASP.NET Core | Open-source web framework for .NET , ASP.NET Core | Open-source web framework for .NET Core, Java, Node.js, PHP 또는 Python에 대한 최고 수준의 지원을 제공한다. PowerShell 및 기타 스크립트 또는 실행 파일을 백그라운드 서비스로 실행할 수도 있다.
관리되는 프로덕션 환경 - App Service는 OS 및 언어 프레임워크를 자동으로 패치하고 유지 관리한다.
컨테이너화 및 Docker - 앱을 Docker화하고 App Service에서 사용자 지정 Windows 또는 Linux 컨테이너를 호스팅한다. Docker Compose를 사용하여 다중 컨테이너 앱을 실행한다. Docker 기술을 App Service로 직접 마이그레이션하라.
DevOps 최적화 - Azure DevOps, GitHub, BitBucket, Docker Hub 또는 Azure Container Registry를 사용하여 지속적인 통합 및 배포를 설정한다. 테스트 및 스테이징 환경을 통해 업데이트를 승격한다.
고가용성을 갖춘 글로벌 확장 - 수동 또는 자동으로 확장 또는 확장한다. Microsoft의 글로벌 데이터 센터 인프라 어디에서나 앱을 호스팅하세요. App Service SLA는 고가용성을 보장한다.
SaaS 플랫폼 및 온-프레미스 데이터에 대한 커넥트 - 엔터프라이즈 시스템(예: SAP), SaaS 서비스(예: Salesforce), 인터넷 서비스(예: Facebook)를 위한 수백 개의 커넥터 중에서 선택하세요. 하이브리드 연결 및 Azure 가상 네트워크를 사용하여 온-프레미스 데이터에 액세스한다.
보안 및 규정 준수 - App Service는 ISO, SOC 및 PCI 규격을 준수한다. IP 주소 제한 및 관리형 서비스 ID를 만든다. 하위 도메인 인수를 방지한다.
인증 - 내장된 인증 구성 요소를 사용하여 사용자를 인증한다. Microsoft Entra ID, Google, Facebook, Twitter 또는 Microsoft 계정으로 사용자를 인증한다.
애플리케이션 템플릿 - WordPress, Joomla, Drupal 등 Azure Marketplace의 광범위한 애플리케이션 템플릿 목록에서 선택하세요.
Visual Studio와 Visual Studio Code 통합 - Visual Studio 및 Visual Studio Code의 전용 도구는 생성, 배포, 디버깅 작업을 간소화한다.
Java 도구 통합 - Maven, Gradle, Visual Studio Code, IntelliJ, Eclipse와 같은 선호하는 개발 도구를 남기지 않고 Azure에 개발하고 배포한다.
API 및 모바일 기능 - App Service는 RESTful API 시나리오에 대한 턴키 CORS 지원을 제공하고 인증, 오프라인 데이터 동기화, 푸시 알림 등을 사용하도록 설정하여 모바일 앱 시나리오를 간소화한다.
서버리스 코드 - 인프라를 명시적으로 프로비전하거나 관리할 필요 없이 주문형 코드 조각 또는 스크립트를 실행하고 코드가 실제로 사용하는 컴퓨팅 시간에 대해서만 비용을 지불합니다(Azure Functions 참조).
1.3 Azure Container Apps
Azure Container Apps는 복잡한 인프라를 오케스트레이션하지 않고 코드 또는 컨테이너에서 앱을 배포하는 데 도움이 되는 완전 관리형 Kubernetes 기반 애플리케이션 플랫폼이다. 생산성을 높이기 위해 통합된 중앙 집중식 네트워킹, 통합 가시성, 동적 확장 및 구성을 사용하여 이기종 최신 앱 또는 마이크로서비스를 빌드하세요. Dapr을 완벽하게 지원하는 탄력적인 마이크로서비스와 KEDA 기반의 동적 확장을 통해 탄력적인 마이크로서비스를 설계한다.
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows
PS C:\Users\zerobig> wsl
zerobig@win11vm-sa:/mnt/c/Users/zerobig$
별도의 bash 창에서 mvn clean package 명령으로 컴파일을 수행하고 컴파일 한다. 참고로 clean 옵션은 maven build 시 생성된 모든 것들을 삭제한다.
mvn clean package -DskipTests
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$ mvn clean package -DskipTests
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Scanning for projects...
[INFO]
[INFO] -------------< org.springframework:gs-spring-boot-docker >--------------
[INFO] Building Spring Boot Docker 0.1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ gs-spring-boot-docker ---
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ gs-spring-boot-docker ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ gs-spring-boot-docker ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /mnt/c/Users/youngdae.kim/gs-spring-boot-aks/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ gs-spring-boot-docker ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /mnt/c/Users/youngdae.kim/gs-spring-boot-aks/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ gs-spring-boot-docker ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /mnt/c/Users/youngdae.kim/gs-spring-boot-aks/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ gs-spring-boot-docker ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ gs-spring-boot-docker ---
[INFO] Building jar: /mnt/c/Users/youngdae.kim/gs-spring-boot-aks/target/gs-spring-boot-docker-0.1.0.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.3.0.RELEASE:repackage (repackage) @ gs-spring-boot-docker ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.557 s
[INFO] Finished at: 2022-04-19T15:00:31+09:00
[INFO] ------------------------------------------------------------------------
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$
target 디렉토리 생성을 확인하고 해당 디렉토리로 이동하여 gs-spring-boot-docker-0.1.0.jar 파일 생성을 확인한다.
cd target ls -rlth
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$ cd target/
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks/target$ ls -rlht
total 16M
drwxrwxrwx 1 zerobig zerobig 4.0K Mar 17 22:52 generated-sources
drwxrwxrwx 1 zerobig zerobig 4.0K Mar 17 22:52 maven-status
drwxrwxrwx 1 zerobig zerobig 4.0K Mar 17 22:52 classes
drwxrwxrwx 1 zerobig zerobig 4.0K Mar 17 22:52 generated-test-sources
drwxrwxrwx 1 zerobig zerobig 4.0K Mar 17 22:52 test-classes
drwxrwxrwx 1 zerobig zerobig 4.0K Mar 17 22:52 maven-archiver
-rwxrwxrwx 1 zerobig zerobig 2.8K Mar 17 22:52 gs-spring-boot-docker-0.1.0.jar.original
-rwxrwxrwx 1 zerobig zerobig 16M Mar 17 22:52 gs-spring-boot-docker-0.1.0.jar
java -jar 명령을 통해 로컬에서 샘플 소스의 유효성을 검증한다.
java -jar gs-spring-boot-docker-0.1.0.jar
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks/target$ java -jar gs-spring-boot-docker-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.0.RELEASE)
2024-03-17 22:53:21.447 INFO 4723 --- [ main] hello.Application : Starting Application v0.1.0 on win11vm-sa with PID 4723 (/mnt/c/Users/zerobig/gs-spring-boot-aks/target/gs-spring-boot-docker-0.1.0.jar started by zerobig in /mnt/c/Users/zerobig/gs-spring-boot-aks/target)
2024-03-17 22:53:21.457 INFO 4723 --- [ main] hello.Application : No active profile set, falling back to default profiles: default
2024-03-17 22:53:25.759 INFO 4723 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2024-03-17 22:53:25.803 INFO 4723 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2024-03-17 22:53:25.804 INFO 4723 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.35]
2024-03-17 22:53:26.071 INFO 4723 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2024-03-17 22:53:26.072 INFO 4723 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 4375 ms
2024-03-17 22:53:26.750 INFO 4723 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2024-03-17 22:53:27.375 INFO 4723 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2024-03-17 22:53:27.410 INFO 4723 --- [ main] hello.Application : Started Application in 8.601 seconds (JVM running for 9.819)
별도의 Windows 터미널 탭을 띄워서 다음 명령을 실행한다.
start http://localhost:8080
결과 확인 후 Ctrl + C를 눌러 실행된 자바 프로세스를 종료한다.
다음 명령을 시행하여 gs-spring-boot-aks 디렉토리로 이동한다.
cd ..
컨테이너 노출 포트 변경 : 8080 --> 80
현재 노출되고 있는 컨테이너의 8080 포트를 80으로 변경하도록 하겠다. VS Code 및 VI를 이용하여 src - main - resources 이하에 application.yml 파일을 열어 port를 80으로 변경하고 저장한다. 이후 VS Code 창을 닫고 다시 App 빌드를 수행한다.
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$ mvn clean package -DskipTests
[INFO] Scanning for projects...
[INFO]
[INFO] -------------< org.springframework:gs-spring-boot-docker >--------------
[INFO] Building Spring Boot Docker 0.1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ gs-spring-boot-docker ---
[INFO] Deleting /mnt/c/Users/zerobig/gs-spring-boot-aks/target
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ gs-spring-boot-docker ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ gs-spring-boot-docker ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /mnt/c/Users/zerobig/gs-spring-boot-aks/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ gs-spring-boot-docker ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /mnt/c/Users/zerobig/gs-spring-boot-aks/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ gs-spring-boot-docker ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /mnt/c/Users/zerobig/gs-spring-boot-aks/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ gs-spring-boot-docker ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ gs-spring-boot-docker ---
[INFO] Building jar: /mnt/c/Users/zerobig/gs-spring-boot-aks/target/gs-spring-boot-docker-0.1.0.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.3.0.RELEASE:repackage (repackage) @ gs-spring-boot-docker ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.307 s
[INFO] Finished at: 2024-03-17T23:02:24Z
[INFO] ------------------------------------------------------------------------
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$
2.3 Docker Build 및 검증하기
Docker Build
다시 gs-spring-boot-aks 디렉토리로 이동하여 샘플 소스 내 준비된 Dockerfile을 열고 EXPOSE 포트 정보를 80으로 변경하고 저장한다.
FROM openjdk:8-jdk-alpine EXPOSE 80 // 변경 필요 ARG JAR_FILE=target/*.jar ADD ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar"]
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$ vi Dockerfile
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$ cat Dockerfile
FROM openjdk:8-jdk-alpine
EXPOSE 80
ARG JAR_FILE=target/*.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
이제 Docker를 빌드하고 로컬환경에서 그 결과를 검증한다.
# Docker 빌드 docker build -t appmod-demo-cicd .
# 빌드 결과 확인 docker images
# Docker 실행 docker run -d -p 8888:80 appmod-demo-cicd
주의 : WSL에서 docker 명령 실행 시 다음과 같은 에러가 발생한다면, Docker Desktop을 실행하고 다시 시도한다.
zerobig@win11vm-sa:/mnt/c/Users/zerobig/gs-spring-boot-aks$ docker ps
The command 'docker' could not be found in this WSL 2 distro.
We recommend to activate the WSL integration in Docker Desktop settings.
For details about using Docker Desktop with WSL 2, visit:
https://docs.docker.com/go/wsl2/