8/12 진행사항
✅ 알고리즘 2문제
✅ 프로젝트 관리 심화 강의
✅ 과제 해설 강의 듣기 + 피드백 반영
Docker란?
애플리케이션을 쉽게 만들고 테스트하고 배포할 수 있게 도와주는 소프트웨어 플랫폼으로
애플리케이션을 컨테이너라는 가볍고 이식성 있는 패키지로 실행할 수 있다.
특징
- 컨테이너화 - 하나의 패키지로 묶어 어디서든 실행 가능
- 경량 - 운영체제의 커널을 공유하므로 가상 머신보다 훨씬 빠름
- 이식성 - 어디서든 동일하게 동작
- 확장성 - 여러 개의 컨테이너를 효율적으로 관리하고 쉽게 확장
주요 키워드
- 이미지 - 애플리케이션과 모든 실행에 필요한 파일을 포함한 읽기 전용 템플릿, 정적인 템플릿
- 컨테이너 - 이미지를 실행하여 동작하는 애플리케이션 인스턴스, 동적인 환경
- Dockerfile - 이미지를 생성하기 위한 명령어가 담긴 스크립트 파일, 이미지 빌드 명령어 포함
- Docker Hub - 이미지를 저장하고 공유하는 중앙 저장소
- 볼륨 - 컨테이너 데이터를 지속적으로 저장하는 메커니즘
- 네트워크 - 컨테이너 간의 통신을 관리하는 방식
- Bridge Network (브리지 네트워크) - Docker가 컨테이너를 실행할 때 사용하는 네트워크로 단일 호스트에서 여러 컨테이너를 연결할 때 사용된다.
Docker vs 가상머신
Docker
애플리케이션만 실행하고, 운영 체제의 핵심부분은 공유하므로 가상 머신보다 훨씬 빠르게 시작할 수 있다.
보안 격리가 가상 머신보다 약함, 도커는 리눅스 커널을 사용하여 작동, 호환성 문제가 있을 수 있음, 리눅스 이외의 OS에서는 성능저하 우려
가상머신
물리적 하드웨어를 가상화하는 소프트웨어
격리된 환경 제공하고 다양한 운영 체제 실행 가능
오버헤드가 크고 느린 부팅시간, 실제 하드웨어에서 컴퓨터를 켜는 것과 비슷함, 높은 리소스 소비 (여러 가상 머신 실행 시 컴퓨터 자원을 많이 소모하게 됨)
Docker의 사용
- 일관된 개발환경이 필요할 때
- 애플리케이션을 빠르게 배포하고 싶을 때
- 마이크로 서비스 아키텍처를 도입할 때
- CI/CD 파이프라인 구축할 때
- 리소스 효율성을 높이고 싶을 때
- 애플리케이션 격리가 필요할 때
- 쿠버네티스와 함께 사용하고자 할 때
Docker Compose란?
다중 컨테이너 도커 애플리케이션을 정의하고 실행하기 위한 도구
도커 내부의 컨테이너들끼리는 포트가 같아도 상관 없음 -> 독립적인 환경이라 ok. 다만, 호스트의 포트는 달라야함
도커 컨테이너가 많아지면 관리가 힘들어지기 때문에 Docker Compose를 사용한다.
도커 컴포즈를 사용하면 새 브리지 네트워크를 생성하여 각 서비스 컨테이너를 그 네트워크에 연결한다.
이 네트워크는 docker-compose.yml 파일에 정의된 모든 서비스가 서로 통신할 수 있도록 한다.
도커 네트워크 안에 있어야 컨테이너 끼리 호출할 수 있음.
docker-compose.yml
version: '3.8'
services:
service-a:
image: img-service-a
ports:
- "18080:8080"
environment:
- SERVICE_B_URL=http://service-b:8080
depends_on:
- service-b
service-b:
image: img-service-b
ports:
- "18081:8080"
networks:
default:
driver: bridge
실행
docker compose up -d
Docker 실습
프로젝트 빌드
./gradlew clean bootJar
이미지 생성 (마지막 .은 도커 파일 위치)
docker build -t img-service-a .
컨테이너 생성 (외부포트:18080, 내부포트:8080)
docker run -d --name service-a \
--network my-network \
-p 18080:8080 \
-e SERVICE_B_URL=http://service-b:8080 \
img-service-a
과제 Feedback
client 폴더에 DIP 적용하기
// 응용 계층 DIP 적용을 위한 상품 서비스 인터페이스
public interface ProductService {
ProductResponseDto getProductById(Long id);
}
// 상품 서비스 호출 클라이언트가 응용 계층의 인터페이스인 ProductService를 상속받아 DIP를 적용
@FeignClient(name = "product-service", configuration = FeignConfiguration.class)
public interface ProductClient extends ProductService {
@GetMapping("/products/{id}")
ProductResponseDto getProductById(@PathVariable("id") Long id);
}
외부 서비스가 변경 되었을 때 내부 서비스에도 영향이 없도록 설계하기 위해 DIP를 적용한다.
=> MSA 환경에서 외부서비스의 격리가 목적
DIP란? 객체지향의 5대 원칙 중 의존성 역전 원칙으로
고수준 모듈(interface, 추상클래스)이 저수준 모듈(메인 클래스, 객체)에 의존하지 않도록 하는 설계 원칙이다.
=> 구체화에 의존하는 것이 아닌 추상화에 의존하라
=> 자신보다 변화하기 쉬운 것을 의존하는 것이 아닌 거의 변화가 없는 개념에 의존하라
예시) 고수준 모듈 interface를 선언하여 저수준 모듈에 할당
List<String> strList = new ArrayList()<>;
Gateway 내 마이크로 서비스 호출 방식 변경
WebClient -> FeignClient
GateWay에서 FeignClient 호출 시
> FeignClient 와 Global Filter 의 순환참조 문제가 발생
> Bean 초기 로딩 시 순환을 막기 위해 @Lazy 어노테이션을 추가
public LocalJwtAuthenticationFilter(@Value("${service.jwt.secret-key}") String secretKey, @Lazy AuthService authService) {
this.secretKey = secretKey;
this.authService = authService;
}
FeignClient를 Lazy Load 할 경우 HttpMessageConverters 문제 발생
> config 설정
@Configuration
public class FeignConfig {
@Bean
public Decoder feignDecoder() {
ObjectFactory<HttpMessageConverters> messageConverters = () -> {
HttpMessageConverters converters = new HttpMessageConverters();
return converters;
};
return new SpringDecoder(messageConverters);
}
}
폴더 정리
service → application
dto ⊂ application
entity → domain
repository ⊂ domain
client → infrastructure
'TIL' 카테고리의 다른 글
[TIL] Kafka란? (0) | 2024.08.15 |
---|---|
[TIL] RabbitMQ 이해하기 (0) | 2024.08.13 |
[TIL] Spring Boot 로컬과 서버 환경 분리 | 정적 팩토리 메서드 (0) | 2024.08.10 |
[TIL] Spring Boot 프로젝트에 캐싱 적용하기 (0) | 2024.08.09 |
[TIL] 양방향 연관관계 매핑 | Feign client Error Decoder (0) | 2024.08.08 |