CNI
컨테이너 네트워크 서비스(CNI)
볼륨을 통한 데이터 공유
SDN(Software-Defined Networking: 소프트웨어 정의 네트워크)
- 네트워크 리소스를 최적화하고 변화하는 비지니스 요구, 애플리케이션 및 트래픽에 신속하게 네트워크를 채택하는데 도움이 되는 네트워크 가상화 및 컨테이너화에 대한 접근 방식
- 네트워크 제어와 데이터 평면을 분리하여 소프트웨어 프로그래밍 가능 인프라를 만드는 방식
쿠버네티스에서 SDN이 필요한 이유
- 쿠버네티스 네트워킹에는 수백 개의 파드가 있고 그 중 일부가 같은 서비스에 대응하는 경우 파드가 이동해서 모든 트래픽이 항상 올바른 위치로 갈 수 있도록 클러스터에 들어오고 나가는 트래픽을 계속 라우팅 할 수 있어야 함
- 위의 목적을 달성하기 위한 쿠버네티스에서 제공하는 2가지 도구
- Service Proxy: 정적 IP를 갖는 서비스 뒤에 파드가 로드 밸런싱되고 쿠버네티스 서비스 객체가 라우팅 되는 것을 보장해줌
- CNI: 파드가 클러스터의 일정하면서도 쉽게 액세스 할 수 있는 네트워크 내부에서 계속해서 다시 재생될 수 있음을 보장해 줌
- 이를 위해서 ClusterIP 타입을 갖는 쿠버네티스 서비스 객체가 생성됨
- ClusterIP 서비스는 쿠버네티스 서비스로 쿠버네티스 클러스터에서 라우팅이 가능하지만 클러스터 외부에서는 액세스 할 수 없음
- ClusterIP 서비스는 다른 서비스의 상단에 만들어질 수 있는 기본적인 요소
- 클러스터에서 애플리케이션이 직접 라우팅 할 필요없이 서로 액세스 할 수 있는 가장 간단한 방법은 파드의 IP주소를 활용하는 것
- 서비스도 생성될 때 별도의 IP 주소를 가지고 생성되는데 이 때 각 서비스는 별도의 서브넷을 가지고 만들어짐
서비스에 파드를 등록하면 파드 레플리카들에 별도의 이름을 할당하고 그 이름에 IP를 매핑하는 DNS를 소유하게 됨
일종의 NAT 와 유사한 형태의 방식을 사용
- NodePort 서비스 와 ClusterIP 서비스
- NodePort 서비스는 내부 파트 네트워크의 모든 포트를 외부에 노출시키는데 이를 이용해서 로드밸런서를 만들 수 있게 함
- NodePort를 설정하게 되면 동일한 성격의 파드는 NodePort가 전부 개방됨
- 클러스터 파드 내부에서 실행되는 CoreDNS 컨테이너를 가리키는 NodePort 서비스 생성
my-nodeport.yml
apiVersion: v1
kind: Service
metadata:
annotation:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: CoreDNS
name: kube-dns-2
namespace: kube-system
spec:
ipFamlies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: dns
port: 53
protocol: UDP
targetPort: 53
- name: dns-tcp
port: 53
protocol: TCP
targetPort: 53
selector:
k8s-app: kube-dns
sessionAffinity: None
type: NodePort
- 서비스 생성
kubectl apply -f my-nodeport.yml
- 서비스 확인
kubectl get svc
Spring Boot Application을 NodePort를 이용해서 모든 Node에서 포트를 개방해서 서비스 할 수 있도록 하기
1. 애플리케이션 생성
- Intellij에서 Spring Boot Application 생성(Lombok 과 Web, devtools 에 대한 의존성만 추가)
- 기본 패키지 안에 요청을 처리하는 클래스를 추가하고 작성(FrontController)
FrontController.java
package com.gmail.dragonhailstone.apiserver;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class FrontController {
@GetMapping("/")
public Map<String, Object> index() {
Map<String, Object> data = new HashMap<>();
data.put("result", "success");
List<Map> list = new ArrayList<>();
Map<String, String> map1 = new HashMap<>();
map1.put("id", "itstudy");
map1.put("name", "아담");
list.add(map1);
data.put("list", list);
return data;
}
}
- 실행 한 후 브라우저에서 8080 포트로 접속해서 에러가 없는지 확인
2. Dockerfile 파일 만들어서 이미지 생성
- 프로젝트의 루트 디렉토리에 Dockerfile을 생성하고 작성
FROM amazoncorretto:17
CMD ["./mvnw", "clean", "package"]
ARG JAR_FILE=target/*.jar
COPY ./build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
=>이미지 생성
docker build -f Dockerfile -t apiserver:0.0.1 .
=>이미지 확인
docker images
=>컨테이너로 실행
docker run -d --name apiserver -p 80:8080 apiserver:0.0.1
3.Github이용해서 DockerHub에 배포
- git hub에 레포지토리 생성: apiserver =>git hub에 push
git init
git add .
git commit -m "init"
한번도 사용하지 않아서 이메일 과 이름을 등록하라고 메시지 출력
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
브랜치 확인(현재 git hub는 main이 기본 브랜치)
git branch
브랜치 변경
git checkout -b main
원격 저장소 등록
git remote add 저장소이름 url
git remote add origin https://github.com/itstudy001/apiserver.git
push: windows 나 mac에서는 브라우저에서 로그인하고 linux의 경우는 콘솔에서 로그인하라는 메시지가 출력됨
git push origin main
- push가 발생할 때 Dockerhub에 이미지 배포
- Docker Hub에 Repository를 생성: apiserver
- Docker Hub에서 토큰 발급
- 프로젝트 안에 .github/workflows/upload.yml
upload.yml
name: Java CI with Gradle
on:
push:
branches: ["main"]
jobs:
build-docker-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# JDK 17 설치
- name: Set Up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Run chmod to make gradlew executable
run: chmod +x ./gradlew
# 빌드
- name: Build andGradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: clean bootJar
# 도커 허브 로그인
- name: Login To DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# 이미지 빌드 및 업로드
- name: Image Build and Release to Dockerhub
env:
NAME: ${{ secrets.DOCKERHUB_USERNAME }}
REPO: ${{ secrets.DOCKERHUB_REPOSITORY }}
run: |
docker build -t $REPO .
docker tag $REPO:latest $NAME/$REPO:latest
docker push $NAME/$REPO:latest
push
git add .
git commit -m 메시지
git push 저장소이름 브랜치이름
- 배포가 제대로 되었는지 도커 허브에서 확인
- 이미지가 제대로 동작하는지 확인
4. 쿠버네티스 Deployment 이용해서 파드 생성
- pod를 생성할 yml 파일을 생성
springbootdeployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: devops-spring-deployment
spec:
selector:
matchLabels:
app: devops-spring-app
replicas: 2
template:
metadata:
labels:
app: devops-spring-app
spec:
containers:
- name: core
image: dragonhailstone/apiserver
imagePullPolicy: Always # 컨테이너 재실행 시 image pull
ports:
- containerPort: 8080
protocol: TCP
resources:
requests:
cpu: 500m
memory: 1000Mi
5.쿠버네티스 Service 이용해서 NodePort 설정
- nodeport를 이용할 서비스 파일을 생성: service.yml
service.yml
apiVersion: v1
kind: Service
metadata:
name: devops-spring-service
spec:
type: NodePort
ports:
- port: 80 # Service 포트, 노드포트는 자동할당
protocol: TCP
targetPort: 8080 # Pod 포트
selector:
app: devops-spring-app
- 서비스 생성
kubectl apply -f service.yml
- 결과 확인
dh@swarm-manager:~/1029$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
devops-spring-service NodePort 10.96.134.15 <none> 80:31901/TCP 2m46s app=devops-spring-app
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28h <none>
Cluster안에서 NodePort의 Cluster-IP(PrivateIP)의 31901포트로 들어갈 수 있음
- endpoint 확인
dh@swarm-manager:~/1029$ kubectl get ep
NAME ENDPOINTS AGE
devops-spring-service 10.244.1.2:8080,10.244.2.2:8080 2m56s
kubernetes 172.18.0.2:6443 28h
엔드포인트 IP(Pod의 IP) 2개를 볼 수 있음
노드포트로 들어가게 되면 두개 파드의 8080포드 중 하나로 들어가게 됨
- 서비스 와 동일한 이름의 endpoint가 있으면 service에 요청을 하면 endpoint에 요청을 한다는 의미