| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
- 백준
- 오픈패스
- JPA
- Java
- baekjoon
- 국비지원취업
- 백엔드개발자
- UXUI챌린지
- 백엔드 부트캠프
- OPENPATH
- 디자인교육
- UXUIPrimary
- 내일배움카드
- 패스트캠퍼스
- mysql
- 오픈챌린지
- 환급챌린지
- 국비지원
- 티스토리챌린지
- UXUI기초정복
- KDT
- Spring
- 디자인챌린지
- 객체지향
- 시스템설계
- Be
- 디자인강의
- 국비지원교육
- 부트캠프
- 오블완
- Today
- Total
군만두의 IT 개발 일지
[스터디13] 09. 웹서비스 배포하기 본문
목차
9장. 웹서비스 배포하기
9.1 컨테이너화란 무엇일까?
서로 다른 시스템 간 종속성(Java 버전, 웹 서버, OS 등), 구성 또는 파일의 불일치로 인해 발생하는 문제를 해결하기 위해 애플리케이션을 컨테이너화한다. 컨테이너화를 하면 애플리케이션은 필요한 모든 의존성 및 파일과 함께 번들링되어 모든 환경에서 동일하게 동작한다.
컨테이너는 호스트 운영체제의 라이브러리, 바이너리뿐만 아니라 커널도 공유하므로 아주 가볍다. 관련 개념인 가상화(virtualization)는 하드웨어를 분할하여 가상 머신(virtual machine)을 만드는 프로세스로, 컨테이너와는 다르다.
- 가상머신: 호스트 시스템 위에서 생성, 무겁고 수 GB에 달함, 이식성 낮음
- 컨테이너: 하드웨어와 OS 위에서 격리된 프로세스로 실행, 몇 MB ~ 1GB, 이식성 높음
9.2 도커(Docker) 이미지 빌드하기
도커란 무엇인가?
2013년에 런칭한 도커는 시장을 리딩하는 컨테이너 플랫폼이자 오픈 소스 프로젝트다. 도커는 리눅스 커널의 cgroup과 네임스페이스를 사용해 의존성을 애플리케이션과 함께 패키징할 수 있도록 제공한다. 각 컨테이너는 고유한 사용자 네임스페이스를 가진다.
- 프로세스 식별자(PID): 프로세스 격리
- 네트워크(NET): 네트워크 인터페이스 관리
- 프로세스 간 통신(IPC): IPC 리소스 접근 관리
- 마운트 포인트(MNT): 파일시스템 마운트 포인트 관리
- 유닉스 시간 공유(UTS): 커널과 버전 식별자 격리
도커 아키텍처에 대한 이해
도커는 클라이언트-서버 아키텍처를 채택한다. 도커 클라이언트(CLI)는 사용자가 명령을 입력하는 인터페이스이며, 도커 데몬은 컨테이너의 빌드, 실행, 배포를 처리한다. 클라이언트와 데몬은 소켓이나 RESTful API를 통해 통신한다.
도커의 기본 구성 요소
- 도커 이미지: 읽기 전용 템플릿. 도커 컨테이너를 만드는 데 사용됨
- 도커 컨테이너: 도커 이미지로부터 생성되며 자체 프로세스, 파일시스템, 네트워킹 스택을 가짐
- 도커 레지스트리: 도커 이미지를 업로드하거나 다운로드할 수 있는 이미지 리포지토리 (예: Docker Hub)
도커 컨테이너의 생명주기
- 컨테이너 생성:
docker create - 컨테이너 실행:
docker run - 컨테이너 일시 중지(선택사항):
docker pause - 컨테이너 일시 중지 해제(선택사항):
docker unpause - 컨테이너 시작:
docker start - 컨테이너 중지:
docker stop - 컨테이너 재시작:
docker restart - 컨테이너 강제 종료:
docker kill - 컨테이너 제거:
docker rm— 중지된 컨테이너를 대상으로 실행해야 함
9.3 액추에이터(Actuator) 의존성 추가를 통해 이미지 빌드하기
프로덕션 수준의 기능을 제공하는 스프링 부트 액추에이터 의존성을 추가한다. 이 장에서는 /actuator/health 엔드포인트를 사용해 도커 컨테이너 내부 서비스의 상태를 파악한다.
1. build.gradle에 의존성 추가
runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator'
2. Constants.java에 URL 상수 추가
public static final String ACTUATOR_URL_PREFIX = "/actuator/**";
3. SecurityConfig.java 보안 설정 업데이트
// 앞부분 생략
req.requestMatchers(toH2Console()).permitAll()
.requestMatchers(new AntPathRequestMatcher(
ACTUATOR_URL_PREFIX)).permitAll()
.requestMatchers(new AntPathRequestMatcher(
TOKEN_URL, HttpMethod.POST.name())).permitAll()
// 뒷부분 생략
위 설정으로 인증 여부와 관계없이 액추에이터 엔드포인트에 접근할 수 있게 됐다.
9.4 스프링 부트 플러그인 태스크 설정하기
스프링 부트 그래들 플러그인은 도커 이미지 빌드를 위해 bootBuildImage 명령어를 제공한다. .jar 파일 빌드에만 사용할 수 있으며, 스프링 부트의 bootBuildImage는 Paketo 빌드팩을 사용한다. Paketo는 LTS 버전의 자바 릴리스만 지원하며, Java 17은 LTS이므로 지원이 중단되지 않는다.
// build.gradle
bootBuildImage {
imageName = "192.168.1.2:5000/${project.name}:${project.version}"
}
environment = ["BP_JVM_VERSION" : "17"]
// settings.gradle
rootProject.name = 'packt-modern-api-development-chapter09'
9.5 도커 레지스트리의 설정
개발 단계에서는 로컬 도커 레지스트리를 구성하여 쿠버네티스 환경에 배포하는 것이 이상적이다. 로컬 레지스트리는 TLS를 구성하지 않아 안전하지 않으므로 도커 설정을 변경해야 한다.
daemon.json에 안전하지 않은 레지스트리 추가하기
Docker app > Settings > Docker Engine메뉴를 찾는다.- JSON 파일에
insecure-registries항목을 추가한다.
{
"features": { "buildkit": true },
"insecure-registries": [ "192.168.1.2:5000" ]
}
- 도커를 재실행한다.
9.6 이미지를 빌드하는 그래들 태스크 실행
# 로컬 도커 레지스트리 구동
$ docker run -d -p 5000:5000 -e REGISTRY_STORAGE_DELETE_ENABLED=true \
--restart=always --name registry registry:2
# 이미지 빌드
$ ./gradlew clean build
$ ./gradlew bootBuildImage
# 로컬 컨테이너 실행 (테스트)
$ docker run -p 8080:8080 192.168.1.2:5000/packt-modern-api-development-chapter09:0.0.1-SNAPSHOT
# 이미지 태그 설정 및 레지스트리 푸시
$ docker tag 192.168.1.2:5000/packt-modern-api-development-chapter09:0.0.1-SNAPSHOT \
192.168.1.2:5000/packt-modern-api-development-chapter09:0.0.1-SNAPSHOT
$ docker push 192.168.1.2:5000/packt-modern-api-development-chapter09:0.0.1-SNAPSHOT
# 레지스트리 이미지 확인
$ curl -X GET http://192.168.80.1:5000/v2/_catalog
{"repositories":["packt-modern-api-development-chapter09"]}
$ curl -X GET http://192.168.80.1:5000/v2/packt-modern-api-development-chapter09/tags/list
{"name":"packt-modern-api-development-chapter09","tags":["0.0.1-SNAPSHOT"]}
9.7 쿠버네티스에 애플리케이션 배포하기
도커 컴포즈도 여러 컨테이너를 관리하는 기능을 제공하지만, 쿠버네티스를 사용하는 것이 좋다. 쿠버네티스를 사용하면 컨테이너 관리뿐만 아니라 배포된 컨테이너를 동적으로 확장할 수도 있다. 미니큐브(Minikube)는 로컬에서 학습 및 개발 목적으로 단일 노드의 쿠버네티스 클러스터를 실행하는 도구다.
미니큐브 설정 (로컬 레지스트리 연동)
미니큐브는 기본적으로 원격 도커 허브를 사용하기 때문에 로컬 레지스트리를 사용하려면 ~/.minikube/machines/minikube/config.json의 InsecureRegistry 항목에 로컬 레지스트리 정보를 추가해야 한다.
"InsecureRegistry": [
"10.96.0.0/12",
"192.168.1.2:5000"
],
# 미니큐브 재시작
$ minikube start --insecure-registry="192.168.80.1:5000"
# 쿠버네티스 동작 확인
$ kubectl get po -A
# 미니큐브 대시보드 실행
$ minikube dashboard
네임스페이스(namespace)는 쿠버네티스 클러스터의 리소스를 사용자나 프로젝트 간에 나눌 수 있게 해주는 쿠버네티스의 특수 객체다. 쿠버네티스 클러스터는 기본적으로 "default" 네임스페이스를 사용한다.
deployment.yaml 생성
쿠버네티스는 YAML 설정을 통해 객체를 생성한다. 샘플 앱을 배포하려면 deployment(파드 생성) 및 service(서비스 노출) 객체가 필요하다.
$ kubectl create deployment chapter09 \
--image=192.168.1.2:5000/packt-modern-api-development-chapter09:0.0.1-SNAPSHOT \
--dry-run=client -o=yaml > deployment.yaml
$ echo --- >> deployment.yaml
$ kubectl create service clusterip chapter09 --tcp=8080:8080 \
--dry-run=client -o=yaml >> deployment.yaml
코드: /Chapter09/k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: chapter09
name: chapter09
spec:
replicas: 1
selector:
matchLabels:
app: chapter09
strategy: {}
template:
metadata:
labels:
app: chapter09
spec:
containers:
- image: 192.168.1.2:5000/packt-modern-api-development-chapter09:0.0.1-SNAPSHOT
name: packt-modern-api-development-chapter09
resources: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
app: chapter09
name: chapter09
spec:
ports:
- name: 8080-8080
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: chapter09
type: ClusterIP
status:
loadBalancer: {}
쿠버네티스에 배포 및 확인
# 배포 명령 실행
$ kubectl apply -f k8s/deployment.yaml
deployment.apps/chapter09 created
service/chapter09 created
# 파드와 서비스 상태 확인
$ kubectl get all
파드(Pod)는 쿠버네티스의 가장 작은 배포 단위로 하나 이상의 컨테이너가 포함된다. 쿠버네티스가 파드를 관리하다가 파드가 다운되면 다른 파드로 대체하기 때문에 IP 등의 구성 정보는 언제든 변경될 수 있다. Service 객체는 파드의 IP 주소를 외부에 노출하거나 파드에 대한 맵핑 정보를 관리하는 추상화 계층을 제공한다.
SSH 터널링으로 외부에서 접근하기
쿠버네티스 내에서 실행 중인 애플리케이션에 직접 접근할 방법은 없으므로 포트 포워딩(SSH 터널링)을 사용해야 한다.
$ kubectl port-forward service/chapter09 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
# 새 터미널에서 확인
$ curl localhost:8080/actuator/health
{"status":"UP","groups":["liveness","readiness"]}
쿠버네티스 클러스터에 애플리케이션이 성공적으로 배포된 것을 확인했다. 이제 포스트맨을 사용해 모든 REST 엔드포인트에 쿼리를 해 볼 수 있다.

이 글은 『스프링 6와 스프링 부트 3로 배우는 모던 API 개발』 책의 내용을 바탕으로 작성되었습니다.
'학습일지' 카테고리의 다른 글
| [스터디13] 07. 사용자 인터페이스 설계하기 (0) | 2026.02.20 |
|---|---|
| [스터디13] 06. 권한 부여와 인증을 통해 REST 엔드포인트 보호하기 (0) | 2026.02.12 |
| [스터디13] 04. API를 위한 비즈니스 로직 작성 (0) | 2026.01.21 |
| [스터디13] 03. API 명세 및 구현 (0) | 2026.01.02 |
| [스터디13] 02. 스프링의 개념과 REST API (0) | 2025.12.22 |
