K8s & Mvc 환경에서 서비스 간 호출을 위한 OpenFeign 사용

soominJung·2021년 5월 14일
0

샘플

목록 보기
1/1

궁금해서 시작..

0. 목표

  • WebFlux 사양이 아니므로 WebClient & Resilience4j 대신 Mvc 사양에서 서비스간 통신에 대해서 OpenFeign & Hystrix 를 사용해보기로 함.
  • K8s 의 IpTables 사양으로 인해 Eureka 를 사용하지 않으므로, Feign 의 url 속성 사용
  • Edge Server 는 s.c.Gateway

"Turbine 은 반드시 Eureka 서비스와 같이 돌아가기 때문에, 사용하지 않는다."
"Turbine 없이 DashBoard 를 사용하는 것은 불편함을 야기하므로 K8s 내부에 올리지 않는다."

간단한 샘플링 정도만 진행한다.

1. 멀티 모듈 빌딩

Gradle 프로젝트의 상 하 관계를 이용한다.
이 부분은 호불호가 많이 갈리는 영역이므로, 개별적으로 틀거나 멀티모듈로 하거나 둘 다 선택사항이다.

프로젝트 구조

  • dashboard > Hystrix DashBoard
  • service-a > service-b 에게 호출 됨
  • service-b > service-a 를 호출
  • service-c > service-a 에게 호출 됨

최상위 모듈의 build.gradle

#Build.gradle

buildscript {
    ext {
        springBootVersion = '2.3.10.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath "io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE"
    }
}

subprojects {
    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    group = 'com.study.sample'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = 1.8

    repositories {
        mavenCentral()
    }

    configurations {
        compileOnly {
            extendsFrom annotationProcessor
        }
    }
    ext {
        set('springCloudVersion', "Hoxton.SR11")
    }
    dependencyManagement {
        imports {
            mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
        }
    }

    dependencies {
        implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

        implementation 'org.springframework.boot:spring-boot-starter-actuator'
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        compileOnly 'org.projectlombok:lombok'
        runtimeOnly 'com.h2database:h2'
        runtimeOnly 'org.postgresql:postgresql'
        annotationProcessor 'org.projectlombok:lombok'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }

    test {
        useJUnitPlatform()
    }
}

최상위 모듈의 setting.gradle

rootProject.name = 'openfegin'
include ':service-a'
include ':service-b'
include ':service-c'

Service-A build.gradle

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
}

bootJar{
    enabled(true)
}

Service-B build.gradle

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
}

bootJar{
    enabled(true)
}

service-c build.gradle

bootJar{
    enabled(true)
}

dashboard build.gradle

plugins {
    id 'org.springframework.boot' version '2.3.10.RELEASE'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.study.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "Hoxton.SR11")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

test {
    useJUnitPlatform()
}

2. Service-B 의 소스

  • 처음으로 호출되는 곳이 B 이므로, 헷갈릴 수 있어 B 부터 기록
  • 연결 상 필요한 소스만 기록

2-1. main 함수

@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class ServiceBApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceBApplication.class, args);
    }

}

2-2. application.yaml

feign:
  client:
    config:
      default:
        connectionTimeout: 3000
        readTimeout: 3000
        loggerLevel: FULL
  hystrix:
    enabled: true
  url:
    service-a: http://localhost:8080

#Circuit Breaker
#Hystrix 는 Thread Pool 에서 관리 된다.
hystrix:
  threadpool:
    default:
      maximum-size: 20 # Thread Poll Size => default 10
  command:
    defualt:
      execution:
        isolation:
          thread:
            timeout-in-milliseconds: 3000 # 3초 지연시 fallback, default 1000
      metrics:
        rooling-stats:
          time-in-milliseconds: 100000 # 오류 감시 시간 default 10000
      circuit-breaker:
        request-volume-threshold: 5 # 감시 시간내의 요청 수 default 20
        error-threshold-percentage: 50 #  감시 시간내의 요청 수가 해당 % 를 넘으면 OPEN default 50
        sleep-window-in-milliseconds: 5000 # open 서킷 유지시간

#Actuator
management:
  endpoints:
    web:
      exposure:
        include: ["health", "info", "hystrix.stream"]
  endpoint:
    health:
      show-details: always

server:
  port: 8081

logging:
  level:
    com:
      study:
        sample:
          serviceb:
            feign:
              ServiceAClient: debug
ribbon:
  http:
    client:
      enabled: false
---
spring:
  profiles:
    active: k8s

server:
  port: 8080
feign:
  url:
    service-a: http://service-a:8080

2-3. 호출 Controller

@RestController
@RequestMapping("/api/v1")
@Slf4j
public class TestController {

    private final ServiceAClient client;

    public TestController(ServiceAClient client) {
        this.client = client;
    }

    @GetMapping("/success")
    public ResponseEntity<?> feignCallSuccess(){
        List<?> objects = client.success();
        return ResponseEntity.ok(objects);
    }

    @GetMapping("/time-out")
    public ResponseEntity<?> feignCallTimeout() throws InterruptedException {
        List<?> objects = client.timeOut();
        return ResponseEntity.badRequest().body(objects);
    }

    @GetMapping("/exception")
    public ResponseEntity<?> feignCallException() throws InterruptedException {
        List<?> objects = client.exception();
        return ResponseEntity.ok(objects);
    }

    @GetMapping("/access/success")
    public ResponseEntity<?>  feignCallAntherServiceSuccess() {
        List<?> objects = client.feignCallSuccess();
        return ResponseEntity.ok(objects);
    }
    @GetMapping("/access/time-out")
    public ResponseEntity<?>  feignCallAntherServiceTimeout() throws InterruptedException {
        List<?> objects = client.feignCallTimeOut();
        return ResponseEntity.badRequest().body(objects);
    }
    @GetMapping("/access/exception")
    public ResponseEntity<?>  feignCallAntherServiceException() {
        List<?> objects = client.feignCallException();
        return ResponseEntity.badRequest().body(objects);
    }

}

2-4. 호출되는 Controller

@FeignClient(url = "${feign.url.service-a}",decode404 = false,value = "service-a",fallbackFactory = ServiceAClientFallbackFactory.class)
@RequestMapping("/api/v1")
public interface ServiceAClient {

    @GetMapping("/service-a/success")
    List<?> success();

    @GetMapping("/service-a/time-out")
    List<?> timeOut() throws InterruptedException;

    @GetMapping("/service-a/exception")
    List<?> exception();

    @GetMapping("/service-a/access/success")
    List<?> feignCallSuccess();

    @GetMapping("/service-a/access/time-out")
    List<?> feignCallTimeOut() throws InterruptedException;

    @GetMapping("/service-a/access/exception")
    List<?> feignCallException();
}

2-5. FallBackFactory

@Component
@Slf4j
public class ServiceAClientFallbackFactory implements FallbackFactory<ServiceAClient> {
    @Override
    public ServiceAClient create(Throwable cause) {
        return new ServiceAClient() {

            @Override
            public List<?> success() {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceA > Fail");
            }

            @Override
            public List<?> timeOut() throws InterruptedException {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceA > Time Out");
            }

            @Override
            public List<?> exception() {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceA > Exception Catch");
            }

            @Override
            public List<?> feignCallSuccess() {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceA > Exception Catch");
            }

            @Override
            public List<?> feignCallTimeOut() throws InterruptedException {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceA > Exception Catch");
            }

            @Override
            public List<?> feignCallException() {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceA > Exception Catch");
            }
        };
    }
}

3. Service-A 의 소스

  • 연결에 필요한 소스 만을 기록

3-1. main 함수

@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class ServiceAApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }

}

3-2. application.yaml

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/postgres?currentSchema=public
    username: postgres
    password: tnals12@
    schema: classpath*:schema.sql
    initialization-mode: always
    data: classpath*:data.sql

  jpa:
    properties:
      hibernate:
      show_sql: true
      format_sql: true
    #    generate-ddl: true
    hibernate:
      ddl-auto: none
    open-in-view: true

logging:
  level:
    org.hibernate.type.descriptor.sql: trace
    com:
      study:
        sample:
          servicea:
            feign:
              ServiceCClient: debug
feign:
  url:
    service-c: http://localhost:8082
  client:
    config:
      default:
        connectionTimeout: 2000
        readTimeout: 2000
        loggerLevel: FULL
  hystrix:
    enabled: true

#Actuator
management:
  endpoints:
    web:
      exposure:
        include: ["health", "info", "hystrix.stream"]

#Circuit Breaker
#Hystrix 는 Thread Pool 에서 관리 된다.
hystrix:
  threadpool:
    default:
      maximum-size: 100 # Thread Poll Size => default 10
  command:
    defualt:
      execution:
        isolation:
          thread:
          timeout-in-milliseconds: 3000 # 3초 지연시 fallback, default 1000
      metrics:
        rooling-stats:
          time-in-milliseconds: 100000 # 오류 감시 시간 default 10000
      circuit-breaker:
        request-volume-threshold: 5 # 감시 시간내의 요청 수 default 20
        error-threshold-percentage: 50 #  감시 시간내의 요청 수가 해당 % 를 넘으면 OPEN default 50
        sleep-window-in-milliseconds: 5000 # open 서킷 유지시간


server:
  port: 8080
---
spring:
  profiles:
    active: k8s
    include: prod
feign:
  url:
    service-c: http://service-c:8080

3-3. 호출 Controller

@RestController
@RequestMapping("/api/v1/service-a")
@Slf4j
public class TestController {

    private final ServiceCClient serviceCClient;

    private final TestRepository testRepository;

    public TestController(ServiceCClient serviceCClient, TestRepository testRepository) {
        this.serviceCClient = serviceCClient;
        this.testRepository = testRepository;
    }

    @GetMapping("/success")
    public List<?> success() {
        log.warn("=================Active Netflix OpenFeign : success=================");
        return testRepository.findAll();
    }

    @GetMapping("/time-out")
    public List<?> timeOut() throws InterruptedException {
        log.warn("=================Active Netflix OpenFeign : timeOut=================");
        Thread.sleep(6000);
        return testRepository.findAll();
    }

    @GetMapping("/exception")
    public List<?> exception() {
        log.warn("=================Active Netflix OpenFeign=================");
        throw new RuntimeException("Exception from Service-A : Fallback Active");
    }

    @GetMapping("/access/success")
    public List<?> feignCallSuccess() {
        log.warn("=================Active Netflix OpenFeign : feignCallSuccess=================");
        return serviceCClient.success();
    }

    @GetMapping("/access/time-out")
    public List<?> feignCallTimeOut() throws InterruptedException {
        log.warn("=================Active Netflix OpenFeign : feignCallTimeOut=================");
        return serviceCClient.timeOut();
    }

    @GetMapping("/access/exception")
    public List<?> feignCallException() {
        log.warn("=================Active Netflix OpenFeign : feignCallException=================");
        return serviceCClient.exception();
    }
}

3-3. 호출되는 Controller

@FeignClient(url = "${feign.url.service-c}",decode404 = false,value = "service-a",fallbackFactory = ServiceCClientFallbackFactory.class)
@Component
@RequestMapping("/api/v1/service-c")
public interface ServiceCClient {
    @GetMapping("/success")
    public List<?> success();
    @GetMapping("/time-out")
    public List<?> timeOut() throws InterruptedException;
    @GetMapping("/exception")
    public List<?> exception();
}

3-4. FallbackFactory

@Component
@Slf4j
public class ServiceCClientFallbackFactory implements FallbackFactory<ServiceCClient> {
    @Override
    public ServiceCClient create(Throwable cause) {
        return new ServiceCClient() {
            @Override
            public List<?> success() {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceC > Fail");
            }

            @Override
            public List<?> timeOut() throws InterruptedException {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceC > Time Out");
            }

            @Override
            public List<?> exception() {
                log.error("Fallback Factory Log: {}",cause.getCause());
                return Collections.singletonList("ServiceC > Exception Catch");
            }
        };
    }
}

4. Service-c

4-1. 호출되는 Controller

@RestController
@RequestMapping("/api/v1/service-c")
@Slf4j
public class PrintController {

    @GetMapping("/success")
    public List<?> success() {
        log.warn("=================Active Netflix OpenFeign : success=================");
        return Collections.singletonList("Hello");
    }

    @GetMapping("/time-out")
    public List<?> timeOut() throws InterruptedException {
        log.warn("=================Active Netflix OpenFeign : timeOut=================");
        Thread.sleep(6000);
        return Collections.singletonList("Hello");
    }

    @GetMapping("/exception")
    public List<?> exception() {
        log.warn("=================Active Netflix OpenFeign : exception=================");
        throw new RuntimeException("Exception from Service-A : Fallback Active");
    }
}

5. DashBoard

5-1. main 함수

@SpringBootApplication
@EnableHystrixDashboard
public class DashboardApplication {

    public static void main(String[] args) {
        SpringApplication.run(DashboardApplication.class, args);
    }

}

5-2. application.yaml

server:
  port: 7000

hystrix.dashboard.proxy-stream-allow-list: localhost

6. 통신

  • 멋지게 curl 에 jq 사용해서 딱 보여....주면 바보같으므로 보기좋게 브라우저 확장 프로그램으로 통신

6-1. Service-B > Service-A

6-1-1. 성공

  • Service-B 의 로그
  • Service-A 의 로그

6-1-2. 타임아웃

  • Service-B 의 로그
  • Service-A 의 로그 > Thread.sleep 종료 후 질의 발생

6-1-3. 예외

  • Service-B 의 로그
  • Service-A 의 로그

6-2. Service-B > Service-A > Service-C

6-2-1. 성공

  • Service-B 의 로그
  • Service-A 의 로그 > OpenFeign
  • Service-C 의 로그

6-2-2. 타임아웃

  • 이 부분이 아주 재밌는데, Timeout 이 전파된 것을 생각해보면, C > A > B 까지 전파됬다.
  • 그렇다고 FallBack 이 안 돌았는가? 분명 A 에서 Fallback 을 받았는데도 B 의 Fallback 이 다시 돈다.
  • Service A 의 로그
  • Service C 의 로그
  • 그럼 Circuit Break 모듈에서는 어떻게 판단할까? DashBoard 로 가보자.


    여기서 전파가 된 것을 볼 수 있다, Timeout 은 어떻게 보면, B->A [타임아웃] A->C[타임아웃]의 연쇄 반응이고, 타임아웃 시간이 같다는 것도 한 몫하고 있을 것이다. 아마도 Fallback 대기 시간을 줄이면 재밌게도 C Fallback 결과가 나오지 않을까 싶다.

7. Docker 이미지 패키징

  • 간단하게, Dockerfile 을 작성하는 것인데 openjdk:12.0.2 를 사용할 것이다.
  • openjdk:12.0.2 이미지는 Docker 컨테이너의 자원 제한량을 준수한다. [--memory & --cpus..]

Kubelet 프로세스가 Master 의 Kube-Apiserver 의 요청을 받아서 [물론 RBAC 검증 후] Docker 자원량 한도를 컨테이너에 걸게 되므로 [Pod.spec.containers[] 아래에 작성하는 거 보면 분명해..]

# 이럴땐 노드로 직접 들어가서 Kubelet 프로세스가 만든 컨테이너를 까보면 되겠다.
docker inspect [id] | grep -i -A10 mem

물론, Kube-Scheduler 의 스케쥴링 filter에 Pod Profile 에도 해당 되므로 중요한 부분이라고 보면 되겠다.

FROM openjdk:12.0.2 

EXPOSE 8080

ADD ./build/libs/*.jar app.jar

ENTRYPOINT ["java","-jar","-Dspring.profiles.active=k8s","/app.jar"]
docker build -t [repo]/[name]:[tag] ./service-a/
docker build -t [repo]/[name]:[tag] ./service-b/
docker build -t [repo]/[name]:[tag] ./service-c/

docker push [repo]/[name]:[tag]

docker rmi ~~~ & docker image prune

8. 자원 템플릿 생성

  • 개인 클러스터를 사용해도 좋다.[싱글 노드 클러스터인 mini-kube]
    node-role.kubernetes.io/master:NoSchedule 가 Taint 로 걸려있지 않는 경우를 의미
  • Edge-Sever Svc 자원은 이미 돌아가고 있다고 가정한다.
  • Nginx 이미지는 입맛에 맞게 커스텀하면된다 [Forward , Reverse Proxy]
  • IpTables 에 Service : Pod EndPoints 가 정상적으로 등록됬다고 가정한다.
  • Svc 로 배포할 Java App 자원만 설명한다.
  • Jenkins 를 이용한 빌드과정도 따로 기록하지않는다.
  • Ingress 는 공식 문서 템플릿정도 기록한다.

보통 이런 직접 적는 템플릿을 Local 템플릿, K8s 내부 메모리 [ETCD] 에 존재하는 [kubeclt get [resource][name] -o yaml] 템플릿은 K8sCluster 내부 템플릿 ( Kubelet 이 적어주는 .status 하위필드를 의미하고는 함), .metadata.annotations.kubectl.kubernetes.io/last-applied-configuration 의 경우 마지막으로 수정된 템플릿을 JSON 형식으로 가지고 있는데, 이 3가지가 Apply , 즉 선언적 방식에 대한 자동 프로세싱을 가능케 함

8-1. Nginx 를 자원으로

  • 입 맛에 맞는 Nginx 이미지를 가졌다고 가정한다.
  • Nginx를 띄울 자원 타입은 Service 자원을 의미하며, 사실상 프로세스가 없는 메모리 내부 자원이니... Kube-Proxy 에 의해 모니터링되어 IpTables 에 등록되는 게 더 중요한 방점일 것이다.
  • Ingress.rules[].http.paths[].backend.service.name 에 서비스 이름을 넣는데 쓰겠다는 의미이다.
  • 기억에 의존해서 작성하는 것이므로 일부 필드가 틀릴 수도 있다.
  • Nginx 에서 이미 다른 네임스페이스의 Edge server 를 호출할 수 있으므로, 다른 네임스페이스는 잡지 않았다.
apiVersion: v1
kind: Service
metadata:
  name: sample-nginx
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: nginx
    proj: sample
    env: dev
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-nginx
  labels:
    app: nginx
    proj: sample
    env: dev
  namespace: default
spec:
  replicas: 1
  selector: 
    matchLabels:
      app: nginx
      proj: sample
      env: dev
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 30%
      maxUnavailable: 30%
 template:
   metadata:
     name: sample-nginx
     namespace: default
     labels:
      app: nginx
      proj: sample
      env: dev     
  spec:
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: beta.kubernetes.io/arch
              operator: In
              values:
              - amd64
              - arm64
    containers:
    - name: nginx
      image: sample/sample-nginx
      volumeMounts:
      - mountPath: /html/
        name: index
      ports:
      - containerPort: 80
    imagePullSecrets:
    - name: my-repo
    volumes:
    - hostPath:
        path: /app/sample/html/
        type: DirectoryOrCreate
      name: index

생성 및 확인
...이게 확인이 그때그때마다 달라서, 로그보고 자원정보보고 해결하는게 가장 이롭다.

kubectl apply -f [name].yaml

kubectl get svc,deploy,rs,po -n default -l "proj in (sample), app in (nginx), env in (dev)" -o wide
>> 노드 스케쥴링위치, 자원 상태, 전부 체크

kubectl describe svc sample-nginx -n default | grep -A6 -i endpoint
>> 이것만 하면 안된다. 반드시 꼼꼼히 전부 체크하자.

kubectl exec -it sample-nginx-UUID-UUID bash

# cat /etc/nginx/conf.d/default.conf
# exit

8-2. Ingress

  • K8s API 버전이 올라가면서 템플릿이 좀 바뀌었다..
  • 공식 문서 템플릿이며, 최소한이다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      host: [도메인]
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sample-nginx
            port:
              number: 80

생성 및 확인

kubectl apply -f sample-ingress.yaml

kubectl get ing -n default

kubectl describe ing [name] -n default

8-3. JavaApp

  • 중복이 많으므로 하나만 기록
apiVersion: v1
kind: Service
metadata:
  name: service-a
  namespace: sample
spec:
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080
    protocol: TCP
  selector:
    app: service-a
    proj: sample
    env: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service-a
  namespace: sample
  labels:
    app: service-a
    proj: sample
    env: dev
  annotations:
    kubernetes.io/change-cause: "Jenkins Build"
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 30%
      maxUnavailable: 30%
  replicas: 2
  selectors:
    matchLabels:
      app: service-a
      proj: sample
      env: dev
  template:
    metadata:
      name: service-a
      namespace: sample
      labels:
        app: service-a
        proj: sample
        env: dev
    spec:
      restartPolicy: Always
      terminationGracePeriodSeconds: 10
      imagePullSecrets:
      - name: my-repo
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
                - key: app
                  operator: In
                  values:
                    - "service-a"
                - key: proj
                  operator: In
                  values:
                    - "sample"
                - key: env
                  operator: In
                  values:
                    - "dev"
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: service-a
        image: [repo]/service-a:latest
        ports:
        - containerPort: 8080
        imagePullPolicy: IfNotPresent
        livenessProbe:
          httpGet:
            path: /actuator/info
            port: 8080
          failureThreshold: 4
          initialDelaySeconds: 7
          periodSeconds: 5
          timeoutSeconds: 15
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          failureThreshold: 4
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 15

자원 생성 및 확인

# cat ~/.kube/config
kubectl config view

# 필요하다면 하나 만들자
kubectl config set-context [name] --current --namespace=[sample] --cluster=[clustername] --user=[ServiceAccount 자원 이름]
# 정신건강에 이로운 컨텍스트 변경
kubectl config use-context [name]

# 그냥 하기

kubectl get svc,deploy,po -n sample -l app=service-a,proj=sample,env=dev -o -wide

kubectl describe po -n sample [name]
> 서비스 , 디플로이먼트, 레플리카 셋 모두 확인

kubectl logs -f [pod-name] -n sample

연동결과 , 모든 자원 로그 정상 & Fallback 정상작동 & OpenFeign 이 Service 를 정상적으로 찾아내는 것 확인

profile
백앤드 개발자

0개의 댓글