no image
[Kubernetes Service] 4. 클라이언트 외부에 서비스 노출
목차 외부 클라이언트에 서비스 노출 노드포트 노드포트 서비스 노드포트 서비스 확인 로드벨런서 외부 로드 밸런서로 서비스 노출 로드밸런서 서비스 생성 로드밸런서를 통한 서비스 연결 외부 연결 특성 불필요한 네트워크 홉 클라이언트 IP가 보존되지 않음 외부 클라이언트에 서비스 노출 이번 포스팅에서는 다음 그림과 같이 프론트엔드 웹서버와 같은 특정 서비스를 외부에 노출해 외부 클라이언트가 액세스할 수 있게 하는 방법에 대해 다뤄보도록 하겠습니다. 외부에서 서비스를 액세스할 수 있는 몇 가지 방법이 있습니다. 노드포트로 서비스 유형 설정 노드포트(NodePort) 서비스의 경우 각 클러스터 노드는 노드 자체에서 포트를 열고 해당 포트로 수신된 트래픽을 서비스로 전달합니다. 이 서비스는 내부 클러스터 IP와 포트..
2023.04.11
[Kubernetes Service] 3. 클러스터 외부에 서비스 연결
목차 클러스터 외부에 있는 서비스 연결 서비스 Endpoint 셀렉터 없이 서비스와 Endpoint 리소스 생성 외부 서비스를 위한 별칭 생성 Endpoint? 클러스터 외부에 있는 서비스 연결 이전 포스팅에서는 클러스터 내부에서 실행 중인 하나 이상의 파드와의 통신을 지원하는 서비스를 설명했습니다. 그러나 쿠버네티스 서비스 기능으로 내부에서만 통신을 하는 것이 아닌 외부 서비스를 노출하려는 경우가 있을 것입니다. 이 때 서비스가 클러스터 내에 있는 파드로 연결을 전달하는 게 아니라, 외부 IP와 포트로 연결을 전달하게 됩니다. 이 경우 서비스 로드밸런싱과 서비스 검색 모두 활용할 수 있습니다. 클러스터에서 실행 중인 클라이언트 파드는 내부 서비스에 연결하는 것처럼 외부 서비스에 연결할 수 있습니다, 서..
2023.04.10
[Kubernetes Service] 2. 서비스를 검색하는 방식 4가지
목차 서비스 검색 환경변수를 통한 서비스 검색 DNS를 통한 서비스 검색 FQDN을 통한 서비스 연결 파드의 컨테이너 내에서 셸 실행 서비스 검색 서비스를 만들면 파드에 액세스할 수 있는 안정적인 IP 주소와 포트가 생깁니다. 이 주소는 서비스각 유지되는 동안 변경되지 않습니다. 이 서비스의 파드는 생성되기도 하고 사라지기도 하고 파드 IP가 변경되거나 파드 수는 늘어나거나 줄어들 수 있지만, 항상 서비스의 IP주소로 액세스할 수 있어야 합니다. 그러나 클라이언트 파드는 서비스의 IP와 포트를 어떻게 알 수 있을까요? 다행히 쿠버네티스는 클라이언트 파드가 서비스의 IP와 포트를 검색할 수 있는 방법을 제공합니다. 이번 포스팅에서는 이 방법들에 대해 다뤄보도록 하겠습니다. 환경변수를 통한 서비스 검색 파드..
2023.04.10
[Kubernetes Service] 1. 쿠버네티스에서 서비스
목차 서비스란 서비스 생성 서비스 조회 및 테스트 kubectl exec 서비스 세션 어피니티 구성 한 서비스로 여러 포트 노출하기 이름이 지정된 포트 사용 서비스란 쿠버네티스 서비스는 웹 서버 또는 데이터베이스 실행과 같은 동일한 작업을 수행하는 Pod 집합을 함께 그룹화하는 추상화입니다. 서비스를 생성하면 클라이언트가 클러스터의 개별 위치에 관계없이 이러한 포드에 액세스할 수 있는 안정적이고 지속적인 진입점 또는 "터치포인트"를 생성할 수 있습니다. 서비스를 만들 때 Kubernetes는 서비스에 안정적인 IP 주소와 포트를 할당합니다. 이는 서비스 수명 동안 일정하게 유지되므로 클라이언트가 기본 Pod의 IP 주소 또는 포트 변경에 대해 걱정하지 않고 안정적으로 연결할 수 있습니다. 클라이언트는 안..
2023.04.10
[Kubernetes] 일회성 작업 효율적으로 관리하기 (2) CronJob
목차 잡을 지정한 시간마다 작동시키기 크론잡 YAML 작성하기 스케줄 생성하기 CronJob에 데드라인 지정하기 잡을 지정한 시간마다 작동시키기 잡 리소스를 생성하면 즉시 해당하는 파드를 실행합니다. 그러나 많은 배치 잡이 미래의 특정 시간 또는 지정된 간격으로 반복 실행해야 합니다. 예를들면 정기 업데이트 등이 있을 것 같습니다. 이러한 기능은 리눅스 명령어 중에서도 cron 명령어가 있었고 쿠버네티스에서도 이를 지원합니다. 쿠버네티스에서의 크론 작업은 크론잡 리소스를 만들어 구성합니다. 잡 실행을 위한 스케줄은 잘 알려진 크론 형식으로 지정하므로, 일반적인 크론 작업에 익숙하다면 금방 쿠버네티스의 크론잡을 이해할 수 있을 것입니다. 쿠버네티스는 설정된 시간에 잡 리소스가 생성되면 앞에서 배운 것처럼 ..
2023.04.09
[Kubernetes] 일회성 작업 효율적으로 관리하기 (1) Job 리소스
목차 잡(Job) 리소스 잡 리소스 YAML 파일 작성 잡에서 여러 파드 인스턴스 실행 잡 스케일링 잡 파드가 완료되는 데 걸리는 시간 제한하기 이전까지는 계속 지속되어 실행돼야 하는 파드에 대해 다루었습니다. 하지만 작업을 완료한 후에는 종료되는 테스크만 실행하려는 경우가 있을 것입니다. 레플리케이션 컨트롤러, 레플리카셋, 데몬셋은 완료되었다고 간주되지 않는 지속적인 테스크를 실행합니다. 그러나 완료 가능한 테스크에서는 프로세스가 종료된 후에 다시 시작되지 않습니다. 리소스 리소스(Resources)란 시스템에서 사용되는 하드웨어, 소프트웨어 및 데이터와 같은 자원들을 의미합니다. 컴퓨터와 관련된 여러 분야에서 사용되지만 쿠버네티스에서 리소스(Resource)는 컨테이너가 사용할 수 있는 CPU, 메모..
2023.04.09
반응형

목차

  1. 외부 클라이언트에 서비스 노출
  2. 노드포트
    • 노드포트 서비스
    • 노드포트 서비스 확인
  3. 로드벨런서
    • 외부 로드 밸런서로 서비스 노출
    • 로드밸런서 서비스 생성
    • 로드밸런서를 통한 서비스 연결
  4. 외부 연결 특성
    • 불필요한 네트워크 홉
    • 클라이언트 IP가 보존되지 않음

외부 클라이언트에 서비스 노출

이번 포스팅에서는 다음 그림과 같이 프론트엔드 웹서버와 같은 특정 서비스를 외부에 노출해 외부 클라이언트가 액세스할 수 있게 하는 방법에 대해 다뤄보도록 하겠습니다.

외부에서 서비스를 액세스할 수 있는 몇 가지 방법이 있습니다.

  • 노드포트로 서비스 유형 설정
    노드포트(NodePort) 서비스의 경우 각 클러스터 노드는 노드 자체에서 포트를 열고 해당 포트로 수신된 트래픽을 서비스로 전달합니다. 이 서비스는 내부 클러스터 IP와 포트로 액세스할 수 있을 뿐만 아니라 모든 노드의 전용 포트로도 액세스할 수 있습니다.

  • 서비스 유형을 로드밸런서(노드포트의 확장 버전)로 설정
    쿠버네티스가 실행 중인 클라우드 인프라에서 프로비저닝된 전용 로드밸런서(LoadBalancer)로 서비스에 액세스할 수 있습니다. 로드밸런서는 트래픽을 모든 노드의 노드포트로 전달합니다. 클라이언트는 로드밸런서의 IP로 서비스에 액세스합니다.

  • 단일 IP 주소로 여러 서비스를 노출하는 인그레스 리소스 만들기
    HTTP 레벨에서 작동하므로 4계층 서비스보다 더 많은 기능을 제공할 수 있습니다. 이 인그레스 리소스는 다음 포스팅에서 자세히 다뤄보도록 하겠습니다.

노드포트
노드포트 서비스

파드 세트를 외부 클라이언트에 노출시키는 첫 번째 방법은 서비스를 생성하고 유형을 노드포트로 설정하는 것입니다. 노드포트 서비스를 만들면 쿠버네티스는 모든 노드에 특정 포트를 할당하고(모든 노드에서 동일한 포트 번호가 사용됩니다.) 서비스를 구성하는 파드로 들어오는 연결(connection)을 전달(forwarding)합니다.

 

이것은 일반 서비스와 유사하지만 서비스의 내부 클러스터IP뿐만 아니라 모든 노드의 IP와 할당된 노드 포트로 서비스에 액세스할 수 있습니다. 이것은 노드포트 서비스와 상호작용할 때 더 큰 의미가 부여됩니다.

 

노드포트 서비스 YAML 생성

apiVersion: v1
kind: Service
metadata:
  name: kubia-nodeport
spec:
  type: NodePort                >  서비스 유형을 node port로 설정합니다.
  ports:
  - prot: 80                          >  서비스 내부 클러스터 IP의 포트
    targetPort: 8080             >  서비스 대상 파드의 포트
    nodePort: 30123            >  각 클러스터 노드의 포트 30123으로 서비스에 액세스할 수 있습니다.
  selector:
    app: kubi

유형으르 노드포트로 설정하고 이 서비스가 모든 클러스터 노드에 바인딩돼야 하는 노드 포트를 지정합니다. 노드 포트를 반드시 지정해야 하는것은 아니지만 이를 생략한다면 쿠버네티스가 임의의 포트를 선택할 것입니다. 예를들어 GKE에서 서비스를 만들 때, kubelet은 방화벽 규칙을 설정하라는 내용의 경고를 보여줄 것입니다. 이럴 때 다음과 같은 방법을 진행합니다.


노드포트 서비스 확인

위 YAML을 apply 혹은 create해서 생성했다면 kubectl get svc kubia-nodeport로 해당 서비스의 내용을 확인해볼 수 있습니다. 여기서 3번째 열에 'EXTERNAL-IP'부분을 확인할 수 있을 것입니다. 만약 위 YAML로 잘 생성이 되었다면 해당 부분에 <nodes>라고 표시돼 있고, 이는 클러스터 노드의 IP 주소로 서비스에 엑세스 할 수 있다는 것을 의미합니다. PORT(S) 열에는 클러스터 IP의 내부 포트 (80)과 노드 포트 (30123)이 모두 표시됩니다. 이 서비스는 다음 주소에서 액세스할 수 있습니다.

  • <클러스터 IP>:80
  • <첫 번째 노드 IP>:30123
  • <두 번쨰 노드 IP>:30123 등

다음 그림은 두 클러스터 노드의 포트 30123에 노출된 서비스를 보여줍니다.(GKE에서 이를 실행하면 적용됩니다. minikubve는 단일 노드만을 갖고 있지만 원칙은 동일학 적용됩니다.) 이런 포드에 대한 수신 연결은 임의로 선택된 파드로 전달되며, 연결 중인 노드에서 실행 중인 포트일 수도 있고 아닐 수도 있습니다.

 

첫 번째 노드의 포트 30123에서 수신된 연결은 첫 번째 노드에서 실행 중인 파드 또는 두 번째 노드에서 실행 중인 파드로 전달할 수 있습니다.

 

이제 외부 클라이언트가 노드포트 서비스에 액세스할 수 있도록 방화벽 규칙을 변경해줍니다. 앞서 언급했듯이 노드포트로 서비스에 액세스하려면 해당 노드포트에 대한 외부 연결을 허용하도록 구글 클라우드 플랫폼의 방화벽을 구성해야 합니다. 다음 명령어로 작업을 수행할 수 있습니다.

 

gcloud compute firewall-rules create kubia-svc-rule --allow=tcp:30123

 

노도 IP와 포트 30123으로 서비스를 액세스할 수 있게 되었습니다. 그러나 먼저 노드 IP를 알아야 합니다. 이 때 kubectl get nodes 명령어를 활용할 수 있습니다. 노드의 IP를 알고 나면 서비스에 curl 명령어를 이용해 서비스에 액세스할 수 있습니다.

 

이제 인터넷에서 어떤 노드든 포트 30123으로 파드에 액세스할 수 있습니다. 클라이언트가 요청을 보내는 노드는 중요하지 않습니다, 그러나 클라이언트가 첫 번째 노드에만 요청하면 해당 노드가 장애가 나면 클라이언트는 더 이상 서비스에 액세스할 수 없습니다. 그렇기 때문에 모든 노드에 요청을 분산시키고 해당 시점에 오프라인 상태인 노드로 요청을 보내지 않도록 노드 앞에 로드밸런서를 배치하는 것이 좋습니다.

 

쿠버네티스 클러스터가 이를 지원하는 경우 노드포트 서비스 대신 로드밸런서를 생성해 로드밸런서를 자동으로 프로비저닝 할 수 있습니다.


로드 밸런서
외부 로드 밸런서로 서비스 노출

클라우드 공급자에서 실행되는 쿠버네티스 클러스터는 일반적으로 클라우드 인프라에서 로드밸런서를 자동으로 프로비저닝하는 기능을 제공합니다. 노드 포트 대신 서비스 유형을 로드밸런서로 설정하기만 하면 됩니다. 로드 밸런서는 공개적으로 액세스 가능한 고유한 IP 주소를 가지며 모든 연결을 서비스로 전달합니다. 따라서 로드밸런서의 IP 주소로 서비스에 액세스할 수 있습니다.

 

쿠버네티스가 로드 밸런서 서비스를 지원하지 않는 환경에서 실행 중인 경우 로드밸런서는 프로비저닝되지 않지만 서비스는 여전히 노드포트 서비스처럼 작동합니다, 로드밸런서 서비스는 노드포트 서비스의 확장이기 때문입니다. 로드밸런서 서비스를 지원하는 구글 쿠버네티스 엔진(GKA)에서 이 예제를 실행할 수 있습니다.


로드밸런서 서비스 생성

앞에서 로드밸런서를 사용해 서비스를 생성하려면 다음 예제와 같이 YAML 매니페스트에 따라 서비스를 생성합니다.

apiVersion: v1
kind: Service
metadata:
  nema: kubia-loadbalancer
spec:
  type: LoadBalancer               >  이 유형의 서비스는 쿠버네티스 클러스터를 호스팅하는 인프라에서
  ports:                                         로드밸런서를 얻을 수 있습니다.
  - port: 80
    targetPort: 8080
  selector:
    app: kubia

이 예제에서 서비스 유형은 노드포트 대신 로드밸런서로 설정돼 있습니다. 특정 노드포트를 지정할 수 있지만 지정하지 않았으므로 쿠버네티스가 대신 포트를 자동으로 선택하게 됩니다.


로드밸런서를 통한 서비스 연결

서비스를 생성한 후 클라우드 인프라가 로드밸런서를 생성하고 IP 주소를 서비스 오브젝트에 쓰는 데 시간이 걸립니다. 그것이 완료되면 로드밸런서 IP 주소가 서비스의 external IP 주소로 표시됩니다. 이 서비스를 kubectl get svc 명령어로 확인할 수 있고, 출력되는 EXTERNAL - IP로 curl을 이용해 서비스에 엑세스할 수 있습니다. 이 방법을 통해 노드 포트와는 달리방화벽 설정 없이  연결할 수 있습니다.

 

파드에 HTTP 요청이 전달되는 방법을 확인하기 위해 다음 그림을 참고할 수 있습니다. 외부 클라이언트는 로드밸런서의 포트 80에 연결하고 노드에 암묵적으로 할당된 노드포트로 라우팅됩니다. 여기에서 연결은 파드 인스턴스로 전달됩니다.

 

로드 밸런서 유형 서비스는 '추가 인프라 제공 로드밸런서가 있는 노드포트 서비스'입니다. kubectl describe를 사용해 서비스에 대한 추가 정보를 보면 서비스에 노드포트가 선택됐음을 알 수 있습니다. 노드포트 부분에서 수행한 방식으로 노드포트 서비스에 대한 포트의 방화벽을 여는 경우 노드 IP로도 서비스에 액세스할 수 있습니다.


외부 연결 특성
  1. 불필요한 네트워크 홉
    외부 클라이언트가 노드포트로 서비스에 접속할 경우(로드밸런서를 먼저 통과하는 경우도 포함) 임의로 선택된 파드가 연결을 수신한 동일한 노드에서 실행 중일 수도 있고, 그렇지 않을 수 도 있습니다. 파드에 도달하려면 추가적인 네트워크 홉이 필요할 수 있으며 이것이 항상 바람직한것은 아닙니다.

    외부의 연결을 수신한 노드에서 실행 중인 파드로만 외부트래픽을 전달하도록 서비스를 구성해 이 추가 홉을 방지할 수 있습니다. 서비스의 스펙 섹션의 externalTrafficPolicy 필드를 생성하는 방법입니다. 위와 같은 YAML 파일 spec: 부분 밑으로 추가해주면 적용됩니다.

    서비스 정의에 이 설정이 포함되어 있고 서비스의 노드포트로 외부 연결이 열린 경우 서비스 프록시는 로컬에 실행 중인 파드를 선택합니다. 로컬 파드가 존재하지 않으면 연결이 중단됩니다.(어노테이션을 사용하지 않을 때 연결이 임의의 글로벌 파드로 전달되지 않습니다.) 따라서 로드밸런서는 그러한 파드가 하나 이상 있는 노드에만 연결을 전달하도록 해야 합니다.

    이 어노테이션을 사용하면 또 다른 단점이 있습니다. 일반적으로 연결은 모든 파드에 균등하게  분산되지만 이 어노테이션을 사용할 때는 더 이상 적용되지 않습니다.

    노드 두 개와 파드 세 개가 있다고 가정해봅시다. 노드 A 가 하나의 파드를 실행하고 노드 B 가 다른 두개를 실행한다고 가정하면 로드 밸런서가 두 노드 걸쳐 연결을 균등하게 분산하면 다음 그림에 표시된 것 처럼 노드 A의 파드는 모든 연결의 50%를 수신하지만 노드 B의 두 파드는 각각 25%만 수신합니다.

  2. 클라이언트 IP가 보존되지 않음
    일반적으로 클러스터 내의 클라이언트가 서비스로 연결할 때 서비스의 파드는 클라이언트의 IP 주소를 얻을 수 있습니다. 그러나 노드포트로 연결을 수신하면 패킷에서 소스 네트워크 주소 변환(SNAT)이 수행되므로 패킷의 소스 IP가 변경됩니다.

    파드는 실제 클라이언트의 IP를 볼 수 없습니다. 이는 클라이언트의 IP를 알아야 하는 일부 애플리케이션에서 문제가 될 수 있습니다. 예를 들어 웹 서버의 경우 액세스 로그에 브라우저의 IP를 표시하지 못한다는 것을 의미합니다.

    이전에 설명한 로컬 외브 트래픽 정책(Local External Traffic Policy)은 연결을 수신하는 노드와 대상 파드를 호스팅하는 노드 사이에 추가 홉이 없기 때문에 클라이언트 IP 보존에 영향을 미친다(SNAT가 수행되지 않습니다.).
반응형
반응형

목차

  1. 클러스터 외부에 있는 서비스 연결
  2. 서비스 Endpoint
  3. 셀렉터 없이 서비스와 Endpoint 리소스 생성
  4. 외부 서비스를 위한 별칭 생성
  5. Endpoint?

클러스터 외부에 있는 서비스 연결

이전 포스팅에서는 클러스터 내부에서 실행 중인 하나 이상의 파드와의 통신을 지원하는 서비스를 설명했습니다. 그러나 쿠버네티스 서비스 기능으로 내부에서만 통신을 하는 것이 아닌 외부 서비스를 노출하려는 경우가 있을 것입니다. 이 때 서비스가 클러스터 내에 있는 파드로 연결을 전달하는 게 아니라, 외부 IP와 포트로 연결을 전달하게 됩니다.

 

이 경우 서비스 로드밸런싱과 서비스 검색 모두 활용할 수 있습니다. 클러스터에서 실행 중인 클라이언트 파드는 내부 서비스에 연결하는 것처럼 외부 서비스에 연결할 수 있습니다,


서비스 Endpoint

들어가기에 앞서 서비스에 대해 좀 더 자세히 설명을 하도록 하겠습니다. 서비스는 파드에 직접 연결되지 않습니다. 대신 Endpoint 리소스가 그 사이에 있습니다. 서비스에서 kubectl describe 명령을 사용하면 엔드 포인트를 확인할 수 있습니다.

 

엔드 포인트 리소스는 서비스로 노출되는 파드의 IP 주소와 포트 목록입니다. 엔드 포인트 리소스는 다른 쿠버네티스 리소스와 유사하므로 kubectl get을 사용해 기본 정보를 표시할 수 있습니다. 파드 셀렉터는 서비스 스펙에 정의돼 있지만 들어오는 연결을 전달할 때 직접 사용하지는 않습니다. 대신 셀렉터는 IP와 포트 목록을 작성하는 데 사용되며 Endpoint 리소스에 저장됩니다. 클라이언트가 서비스에 연결하면 서비스 프록시는 이들 중 하나의 IP와 포트 쌍을 선택하고 들어온 연결을 대상 파드의 수신 대기 서버로 전달합니다.

서비스의 Endpoint를 서비스와 분리하면 Endpoint를 수동으로 구성하고 업데이트 할 수 있습니다. 파드 셀렉터 없이 서비스를 만들면 쿠버네티스는 Endpoint 리소스를 만들지 못합니다. 서비스를 위한 Endpoint 목록을 Endpoint 리소스로 설정하는 것은 독자에게 달렸습니다. 수동으로 관리되는 Endpoint를 사용해 서비스를 만들려면 서비스와 Endpoint 리소스를 모두 만들어야 합니다.


셀렉터 없이 서비스와 Endpoint 리소스 생성
apiVersion: v1
kind: Service
metadata:
  name: external-service    >  서비스 이름은 Endpoint 오브젝트 이름과 일치해야 합니다.
spec:
  ports:
  - port: 80

해당 YAML 매니페스트는 포트 80으로 들어오는 연결을 허용하는 external-service라는 서비스를 정의했습니다. 위에서 확인할 수 있듯이 서비스에 대한 파드 셀렉터는 따로 정의되어 있지 않습니다.

 

이러한 방식으로 서비스에 관한 Endpoint 리소스를 셀렉터 없이 생성할 수 있습니다. Endpoint는 별도의 리소스이며, 서비스 속성은 아니지만 셀렉터가 없는 서비스를 생성했기 때문에 Endpoint 리소스가 자동으로 생성되지 않습니다. 따라서 Endpoint 서비스를 생성해야 합니다. 다음 예제는 이러한 YAML 매니페스트를 보여줍니다.

apiVersion: v1
kind: Endpoints
metadate:
  name: external-service    >  위 예제에서 확인할 수 있듯이 서비스 이름과 
subsets:                                 Endpoint 오브젝트 이름과 일치 해야 합니다.
  - addresses:
    - ip: 123.123.123.123     >  서비스가 연결을 전달할 Endpoint의 IP
    - ip: 234.234.234.234
    ports:
    - port: 80                         >  Endpoint의 대상 포트

Endpoint 오브젝트는 서비스와 이름이 같아야 하고 서비스를 제공하는 대상 IP 주소와 포트 목록을 가져야 합니다. 서비스와 Endpoint 리소스가 모두 서버에 게시되면 파드 셀렉터가 있는 일반 서비스처럼 서비스를 사용할 수 있습니다. 서비스가 만들어진 후 만들어진 컨테이너에는 서비스의 환경변수가 포함되며 <IP:포트> 쌍에 대한 모든 연결은 서비스 엔드 포인트 간에 로드밸런싱합니다.

 

나중에 외부 서비스를 쿠버네티스 파드로 마이그레이션하기로 결정하면 수동 Endpoints 리소스를 사용하는 대신 서비스가 서비스를 실행하는 파드에 대한 연결을 자동으로 검색하고 관리할 수 있습니다. 이를 위해 셀렉터를 추가하여 서비스 구성을 업데이트합니다. 셀렉터를 추가하면 쿠버네티스가 Endpoing을 자동으로 관리하여 트래픽이 적절한 파드로 라우팅되도록 합니다. 


 하지만 서비스에서 셀렉터를 제거하는 경우 쿠버네티스는 Endpoint 리소스 업데이트를 중지합니다. 즉, 서비스 IP 주소와 포트는 동일하게 유지되지만 트래픽은 더 이상 Pod로 라우팅되지 않습니다. 이는 서비스의 실제 구현이 변경되는 동안에도 서비스 IP 주소가 일정하게 유지될 수 있음을 의미합니다.


외부 서비스를 위한 별칭 생성

서비스의 Endpoint를 수동으로 구성해 외부 서비스를 노출하는 대신 좀 더 간단한 방법으로 FQDN으로  외부 서비스를 참조할 수 있습니다.

 

외부 서비스의 별칭으로 사용되는 서비스를 만들려면 유형 필드를 ExternalNam으로 설정해 서비스 리소스를 만듭니다. api.somecompany.com에 공개 API가 있다고 가정해봅시다. 다음 예제에 표시된 대로 이를 가리키는 서비스를 정의할 수있습니다.

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  type: ExternalName
  externalName: someapi.somecompany.com
  ports:
  - port: 80

서비스가 생성되면 파드는 서비스의 FQDN을 사용하는 대신 external-service.default.svc.cluster.local 도메인 이름으로 외부 서비스에 연결할 수 있습니다.

 

이렇게하면 서비스를 사용하는 파드에서 실제 서비스 이름과 위치가 숨겨져 나중에 externalName 속성을 변경하거나 유형을 다시 ClusterIP로 변경하고 서비스 스펙을 만들어 서비스 스펙을 수정하면 나중에 다른 서비스를 가리킬 수 있습니다. 서비스를 위한 Endpoint 오브젝트를 수동 혹은 서비스에 레이블 셀렉터를 지정해 Endpoint가 자동으로 생성되도록 합니다.

 

ExternalName 서비스는 DNS 레벨에서만 구현됩니다. 서비스에 관한 간단한 CNAME DNS 레코드가 생성됩니다. 따라서 서비스에 연결하는 클라이언트는 서비스 프록시를 완전히 무시하고 외부 서비스에 직접 연결됩니다. 이러한 이유로 ExternalName 유형의 서비스는 ClusterIP를 얻지 못합니다.

 

* CNAME 레코드는 IP 주소 대신 FQDN을 가리킵니다.


Endpoint?

쿠버네티스에서 Endpoint는 서비스와 연결된 파드의 네트워크 정보(IP 주소 및 포트)를 저장하는 리소스입니다. 서비스에서 네트워크 트래픽을 올바른 파드로 라우팅하는 데 사용됩니다. Endpoint는 클라이언트와 쿠버네티스 클러스터 내에서 원하는 기능을 제공하는 파드 간의 통신을 가능하게 합니다.

서비스에 선택기가 정의되어 있으면 쿠버네티스는 해당 Endpoints 리소스를 자동으로 생성하고 관리합니다. 선택기는 서비스에서 대상으로 하는 파드의 레이블과 일치하는 키-값 쌍 집합입니다. 파드가 생성, 업데이트 또는 종료되면 쿠버네티스는 적절한 네트워크 정보로 Endpoint를 업데이트하여 서비스가 트래픽을 올바른 파드로 라우팅하도록 합니다.

이번 포스팅에서 다룬 것 처럼 외부 서비스(쿠버네티스 클러스터 외부)에 연결하거나 서비스와 연결된 파드를 수동으로 관리하려는 경우와 같이 셀렉터가 없는 서비스가 있을 수 있습니다. 이러한 시나리오에서는 Endpoints 리소스를 수동으로 만들고 관리해야 합니다. Endpoints 리소스에서 직접 대상 파드 또는 외부 서비스의 IP 주소와 포트를 지정하고 쿠버네티스는 이 정보를 사용하여 서비스에서 트래픽을 라우팅합니다.

반응형
반응형

목차

  • 서비스 검색
    1. 환경변수를 통한 서비스 검색
    2. DNS를 통한 서비스 검색
    3. FQDN을 통한 서비스 연결
    4. 파드의 컨테이너 내에서 셸 실행

서비스 검색

서비스를 만들면 파드에 액세스할 수 있는 안정적인 IP 주소와 포트가 생깁니다. 이 주소는 서비스각 유지되는 동안 변경되지 않습니다. 이 서비스의 파드는 생성되기도 하고 사라지기도 하고 파드 IP가 변경되거나 파드 수는 늘어나거나 줄어들 수 있지만, 항상 서비스의 IP주소로 액세스할 수 있어야 합니다.

 

그러나 클라이언트 파드는 서비스의 IP와 포트를 어떻게 알 수 있을까요? 다행히 쿠버네티스는 클라이언트 파드가 서비스의 IP와 포트를 검색할 수 있는 방법을 제공합니다. 이번 포스팅에서는 이 방법들에 대해 다뤄보도록 하겠습니다.

 

  1. 환경변수를 통한 서비스 검색

    파드가 시작되면 쿠버네티스는 해당 시점에 존재하는 각 서비스를 가리키는 환경변수 세트를 초기화합니다. 클라이언트 파드를 생성하기 전에 서비스를 생성하면 해당 파드의 프로세스는 환경변수를 검사해 서비스의 IP 주소와 포트를 얻을 수 있습니다.

    현재 실행 중인 파드 중 하나의 환경을 검사해 이러한 환경변수가 어떻게 보이는지 살펴봅시다. 파드를 만든 후에 서비스를 만들면 서비스에 대한 환경변수를 설정할 수 없습니다. 이것을 먼저 해결하는 것이 관건입니다.

    서비스에 대한 환경변수를 보려면 먼저 모든 파드를 삭제하고 레플리케이션컨트롤러에서 새로 파드를 만들어야 합니다. 다음과 같이 이름을 지정하지 않고 모든 파드를 삭제할 수 있습니다.

    kubectl delete pods --all

    이제 새 파드를 조회하고 파드를 kubectl exec 명령어의 대상으로 선택할 수 있습니다. 대상 파드를 선택하면 다음 예제와 같이 컨테이너 내부에서 env 명령어를 샐행해 환경변수를 조회할 수 있습니다.

    kubectl exec <파드이름> env

  2. DNS를 통한 서비스 검색

    kube-system 네임스페이스에 파드를 조회하면, 그 중에는 kube-dns라고 있습니다. kube-system 네임스페이스에는 동일한 이름의 해당 서비스가 있습니다.

    이름에서 알 수 있듯이 이 파드는 DNS 서버를 실행하며 클러스터에서 실행중인 다른 모든 파드는 자동으로 이를 사용하도록 구성됩니다. 쿠버네티스는 각 컨테이너의 /etc/resolv.conf 파일을 수정해 이를 수행합니다. 파드에서 실행 중인 프로세스에서 수행된 모든 DNS 쿼리는 시스템에서 싱행 중인 모든 서비스를 알고 있는 쿠버네티스의 자체 DNS 서버로 처리됩니다.

    이 때 파드가 내부 DNS를 사용할지 여부는  각 파드 스펙의 dnsPolicy 속성으로 구성할 수 있습니다. 각 서비스는 내부 DNS 서버에서 DNS 항목을 가져오고 서비스 이름을 알고 있는 클라이언트 파드는 환경변수 대신 FQDN으로 액세스할 수 있습니다.

    Kubernetes에서는 각 서비스에 대한 클러스터 내 DNS(Domain Name System)가 자동으로 구성됩니다. 이 DNS는 각 서비스의 FQDN을 노출시킵니다. 따라서 FQDN을 사용하여 각 서비스에 접근할 수 있습니다. 다음으로 FQDN에 대해 자세히 알아봅겠습니다.

  3. FQDN을 통한 서비스 연결

    FQDN은 일반적으로 "<service-name>.<namespace>.svc.cluster.local" 형식으로 구성됩니다. 예를 들어, "my-service"라는 이름의 서비스가 "my-namespace" 네임스페이스에 있다면 FQDN은 "my-service.my-namespace.svc.cluster.local"이 됩니다.

    Kubernetes에서 FQDN을 사용하면 서비스 간 통신이 간단해지며, 클러스터 내에서 애플리케이션을 배포하고 확장하는 것이 쉬워집니다. FQDN을 사용하여 클러스터 내에서 서비스를 참조할 수 있으므로, 서비스의 IP 주소나 포트 번호를 변경해도 클라이언트 측에서 변경할 필요가 없습니다.

    FQDN(Fully Qualified Domain Name)은 각 서비스에 대한 DNS 이름입니다. FQDN은 서비스의 이름과 네임스페이스(namespace)를 결합하여 생성됩니다. 프론트엔트-백엔드 예제를 다시 보면 프론트엔드 파드는 다음 FQDN으로 백엔드 데이터베이스 서비스에 연결할 수 있습니다.

    backend-database.default.svc.clister.local

    backend-database는 서비스 이름이고 default는 서비스가 정의된 네임스페이스를 나타내며 svc.cluster.local은 모든 클러스터의 로컬 서비스 이름에 사용되는 클러스터의 도메인 접미사입니다.

    서비스에 연결하는 것은 훨씬 간단합니다. 프론트엔드 파드가 데이터베이스 파드와 동일한 네임스페이스에 있는 경우 svc.cluster.local 접미사와 네임스페이스는 생략할 수 있습니다. 따라서 서비스를 단순히 backend-database라고 할 수 있습니다.

    이제 IP 대신 FQDN으로 kubia 서비스에 액세스할 수 있습니다. 이 동작은 기존 파드 내에서 수행해야 합니다. 이미 kubectl exec를 사용해 파드의 컨테이너에서 명령어를 실행하는 방법은 이미 다루었으니, 이번에는 curl 명령어를 직접 실행하는 대신 bash 셸을 실행해 컨테이너에서 여러 명령어를 실행할 수 있습니다. 이는 docker exec -it bash 명령어로 도커로 실행한 컨테이너에 들어갔던 것과 비슷합니다.

  4. 파드의 컨테이너 내에서 셸 실행

    kubecctl exec 명령어를 사용해 파드의 컨테이너 내에서 bash(혹은 다른 셸)를 실행할 수 있습니다. 이렇게 하면 실행하려는 모든 명령어를 kubectl exec로 수행할 필요 없이 원하는 만큼 컨테이너를 자유롭게 탐색할 수 있습니다. 하지만 이 작업을 수행하기 위해서는 컨테이너 이미지에서 셸 바이너리 실행파일이 사용 가능해야 합니다.

    셸을 올바르게 사용하려면 -it  옵션을 kubectl exec에 넣어야 합니다.

    kubectl exec -it <컨테이너 명> bash

    해당 명령어를 입력하면 컨테이너 안으로 들어갈 수 있습니다. 이제 curl 명령어를 이용해 다음 방법으로 kubia 서비스에 액세스할 수 있습니다. 컨테이너에 들어와 있다는 가정하에 다음 명령어를 통해 다음 명령어를 사용하면 상태가 출력될 것입니다.

    1) curl http://kubia.default.svc.cluster.local 
    2) curl http://kubia.default
    3) curl http://kubia

    요청 URL에서 호스트 이름으로 서비스 이름을 사용해 서비스에 액세스할 수 있습니다. 각 파드 컨테이너 내부의 DNS resolver가 구성돼 있기 때문에 네임스페이스와 ssvc.cluster.local 접미사를 생략할 수 있습니다. 컨테이너에서 /etc/resolv.conf 파일을 vi등을 이용해 열어 보면 원리를 이해할 수 있을 것입니다.

반응형
반응형

목차

  1. 서비스란
  2. 서비스 생성
  3. 서비스 조회 및 테스트
  4. kubectl exec
  5. 서비스 세션 어피니티 구성
  6. 한 서비스로 여러 포트 노출하기
  7. 이름이 지정된 포트 사용

서비스란

쿠버네티스 서비스는 웹 서버 또는 데이터베이스 실행과 같은 동일한 작업을 수행하는 Pod 집합을 함께 그룹화하는 추상화입니다. 서비스를 생성하면 클라이언트가 클러스터의 개별 위치에 관계없이 이러한 포드에 액세스할 수 있는 안정적이고 지속적인 진입점 또는 "터치포인트"를 생성할 수 있습니다.

 

서비스를 만들 때 Kubernetes는 서비스에 안정적인 IP 주소와 포트를 할당합니다. 이는 서비스 수명 동안 일정하게 유지되므로 클라이언트가 기본 Pod의 IP 주소 또는 포트 변경에 대해 걱정하지 않고 안정적으로 연결할 수 있습니다.

 

클라이언트는 안정적인 IP 주소 및 포트에 연결하여 서비스에 액세스합니다. 그런 다음 서비스는 클라이언트의 요청을 관리하는 포드 중 하나로 라우팅하며 실제로 원하는 기능을 제공합니다.

 

클라이언트는 서비스의 IP 주소 및 포트와만 상호 작용하므로 서비스를 제공하는 포드의 특정 위치를 알 필요가 없습니다. 이 추상화를 통해 Kubernetes는 서비스에 액세스하는 클라이언트의 기능에 영향을 주지 않고 Pod를 자유롭게 관리(예: 크기 조정, 업데이트 또는 재배치)할 수 있습니다. 포드의 이러한 동적 관리는 시스템의 전반적인 유연성, 확장성 및 안정성을 향상시킵니다.

 

프론트엔드 웹 서버와 백엔드 데이터베이스 서버가 있다고 가정해 봅시다. 프론트엔드 역할을 하는 파드는 여러 개 있을 수 있지만 백엔드 데이터베이스 파드는 하나만 있을 것입니다. 시스템이 기동하려면 두 가지 문제를 해결해야 합니다.

  • 웹 서버가 하나든 수백 개든 상관없이 외부 클라이언트는 프론트엔드 파드에 연결할 수 있습니다.
  • 프론트엔드 파드는 백엔드 데이터베이스에 연결해야 합니다. 데이터베이스는 파드 내에서 실행되므로 시간이 지남에 따라 클러스터 주위를 이동해 IP 주소가 변경될 수 있습니다. 백엔드 데이터베이스가 이동할 때마다 프론트엔드 파드를 재설정하는 것은 쉽지 않습니다.

프론트엔드 파드에 관한 서비스를 만들고 클러스터 외부에서 액세스할 수 있도록 구성하면 외부 클라이언트가 파드에 연결할 수 있는 하나의 고정 IP 주소가 노출됩니다. 마찬가지로 백엔드 파드에 관한 서비스를 생성해 안정적인 주소를 만듭니다. 파드의 IP 주소가 변경되더라도 서비스 IP주소는 변경되지 않습니다. 또한 서비스를 생성하면 프론트엔드 파드에서 환경변수 또는 DNS 이름으로 백엔드 서비스를 쉽게 찾을 수 있습니다.


서비스 생성

서비스를 지원하는 파드가 한 개 혹은 그 이상일 수 있습니다. 서비스 연결은 서비스 뒷단의 모든 파드로 로드밸런싱됩니다. 그러나 정확히 어떤 파드가 서비스의 일부분인지 아닌지를 어떻게 정의할까요?

 

레이블 셀렉터를 이전에 다룬적이 있습니다. 그리고 레플리케이션 컨틀로러와 기타 파드 컨트롤러에서 레이블 셀렉터를 사용해 동일 한 세트에 속하는 파드를 지정하는 방법 또한 다루었었습니다. 다음 그림에 보면 알 수 있듯이 동일한 메커니즘이 서비스에도 그래도 사용됩니다.

 

  1. kubectl expose로 서비스 생성
    서비스를 생성하는 가장 쉬운 방법은 kubectl expose를 사용하는 것입니다. 이전에 이를 사용하여 레플리케이션 컨트롤러를 노출하는데 사용했었습니다. expose 명령어는 레플리케이션 컨트롤러에서 사용된 것과 동일한 파드 셀렉터를 사용해 서비슷리소스를 생성하고 모든 파드를 단일 IP 주소와 포트로 노출합니다.

  2. YAML 디스크립터를 통한 서비스 생성
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - port: 80                    <  서비스가 사용할 포트
    targetPort: 8080       <  서비스가 포워드할 컨테이너 포트
  Selector:
    app: kubia

이 디스크립터에는 kubia라는 서비스를 정의했습니다. 이 서비스는 포트 80의 연결을 허용하고 각 연결을 app=kubia 레이블 셀렉터와 일치하는 파드의 포트 8080으로 라우팅합니다. 이 YMAL을 kubectl create 혹은 kubectl apply로 파일을 실행해 서비스를 생성할 수 있습니다.


서비스 조회 및 테스트

YAML을 게시한 후 네임스페이스의 모든 서비스 리소스를 조회하고 서비스에 내부 클러스터 IP가 할당됐는지 확인할 수 있습니다. 명령어 kubectl get svc(service)를 입력하면 확인할 수 있습니다.

 

이 명령어는 서비스에 할당된 IP 주소를 확인할 수 있고, 이 IP는 클러스터 IP이므로 클러스터 내부에서만 액세스할 수 있습니다. 서비스의 기본 목적은 파드 그룹을 클러스터의 다른 파드에 노출시키는 것이지만 대개 서비스를 하는 이유는외부로 노출하기 위함일 것입니다. 지금은 이 것보다 클러스터 내에서 서비스를 사용해보고 어떻게 동작하는지 살펴보겠습니다.

 

다음은 클러스터 내에서 서비스로 요청을 보낼 수 있는 방법을 조금 정리 했습니다.

  1. 서비스 내에서 클러스터 IP로 요청을 보내고 응답을 로그로 남기는 파드를 만듭니다. 그 후 파드의 로그를 검사해 서비스의 응답이 무엇인지 확인할 수 있습니다.
  2. 쿠버네티스 노드로 ssh 접속하고 curl 명령을 실행할 수 있습니다.
  3. kubectl exec 명령어로 기존 파드에서 curl 명령을 실행할 수 있습니다.

kubectl exec

kubectl exec 명령어를 사용하면 기존 파드의 컨테이너 내에서 원격으로 임의의 명령어를 실행할 수 있습니다. 컨테이너의 내용, 상태, 환경을 검사할 때 유용합니다. kubectl get pods 명령어로 파드를 조회하고 exec 명령어의 대상으로 하나를 선택합니다. 또한 서비스의 클러스터 IP를 알아야합니다. 다음 명령어를 실행할 때는 파드 이름과 서비스 IP를 상황에 맞게 변경해서 사용할 수 있습니다. 이 명령어에 대해서는 이전에 다룬적이 있어 사용하는 방법보다는 작동 방식을 알아보겠습니다. 사용 방법이 궁금하시다면 다음 페지에 간단히 정리되어 있습니다.

 

[Kubernetes] Kubectl 명령어 모음 [2] 수정, 실행, 대기

목차 수정 관련 명령어 kubectl scale kubectl patch kubectl edit kubectl label kubectl annotate 컨테이너에서 명령어 실행 kubectl exec 대기 명령어 kubectl wait 수정 관련 명령어 kubectl scale kubectl scale 명령어는 Kubernete

easyitwanner.tistory.com

 

exec 명령을 실행한 후 이벤트 순서는 다음과 같습니다.

  1. 파드의 컨테이너 내에서 curl 명령을 실행하도록 쿠버네티스에 지시합니다.
  2. curl은 HTTP 요청을 서비스 IP로 보냈습니다. 이 IP에는 파드들이 연결돼 있습니다.
  3. 쿠버네티스 서비스 프록시가 연결을 가로채서 파드들 중 임의의 파드로 요청을 전달합니다.
  4. 해당 파드 내에서 실행중인 Node.js는 요청을 처리하고 해당 파드의 이름을 포함하는 HTTP응답을 반환합니다.
  5. curl은 표준 출력으로 응답을 출력하고 이를 kubectl 이 있는 로컬 시스템의 표준 출력에 다시 표시합니다.

이 방법은 컨테이너 내의 주 프로세스가 서비스와 통신하는 것과 크게 다르지 않습니다.


서비스 세션 어피니티 구성

동일한 명령을 몇 번 더 실행하면 동일한 클라이언트에서 요청하더라도 서비스 프록시가 각 연결을 임의의 파드를 선택해 연결을 다시 전달(forward)하기 때문에 요청할 때마다 다른 파드가 선택됩니다.

 

반면 특정 클라이언트의 모든 요청을 매번 같은 파드로 리디렉션하려면 서비스의 세션어피니티 속성을 기본값 None 대신 ClienIP로 설정합니다.

apiVersion: v1
kind: Service
spec:
  sessionAffinity: ClientIP
...

이로써 서비스 프록시는 동일한 클라이언트 IP의 모든 요청을 동의한 파드로 전달합니다.

 

쿠버네티스는 None과 ClientIP라는 두 가지 유형의 서비스 세션 어피니티만 지원합니다. 쿠키 기반 세션 어피니티 옵션이 없지만 쿠버네티스 서비스가 HTTP 수준에서 작동하지 않는다는 것을 생각해 보면 크게 놀라운 일은 아닙니다. 서비스는 TCP와 UDP 패킷을 처리하고 그들이 가지고 있는 페이로드는 신경 쓰지 않습니다. 쿠키는 HTTP 프로토콜의 구성이기 때문에 서비스는 쿠키를 알지 못하며, 세션 어피니티를 쿠키 기반으로 할 수 없는 이유입니다.


한 서비스로 여러 포트 노출하기

서비스는 단일 포트만 노출하지만 여러 포트를 지원할 수도 있습니다. 예를 들어 파드가 두 개의 포트를 수신한다면 하나의 서비스를 사용해 포트 80과 443을 파드의 포트 8080과 8443으로 전달할 수 있습니다. 이 경우 굳이 두 개의 서비스를 만들 필요가 없습니다. 하나의 서비스를 사용해 멀티 포트 서비스를 사용하면 단일 클러스터 IP로 모든 서비스 포트가 노출됩니다. 하지만 여러 포트가 있는 서비스를 만든다면 각 포트의 이름을 지정해 줘야합니다.

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - name: http
    port: 80                          >  포트 80은 파드의 포트 8080과 매핑됩니다.
    targetPort: 8080
  - name: https
    port: 443                        >  포트 443은 파드의 포트 8443에 매핑됩니다.
    targetPort: 8443
  selector:                           >  레이블 셀렉터는 항상 모든 서비스에 적용됩니다.
    app: kubia

레이블 셀렉터는 서비스 전체에 적용되며 각 포트를 개별적으로 구성할 수는 없습니다. 다른 포트가 다른 파드 서브세트에 매핑되도록 하려면 서비스를 2개 만들어야 합니다.


이름이 지정된 포트 사용

각 파드의 포트에 이름을 지정하고 서비스 스펙에서 이름으로 참조할 수도 있습니다. 따라서 포트 번호가 잘 알려진 경우가 아니더라도 서비스 스펙을 좀 더 명확하게 합니다.

apiVersion: v1
kind: pod
spec:
  containers:
  - name: kubia
    ports:
      - name: http                      >  컨테이너 포트 8080은 http라고 정의 합니다.
        containerPort: 8080
      - name: https                    >  포트 8443은 https라고 정의합니다.
        containerPort: 8443

이렇게 파드를 정의해주면 서비스 스펙에서 이 이름으로 해당 포트를 참조할 수 있습니다.

apiVersion: v1
kind: Service
spec:
  ports:
  - name: http             >  포트 80은 http라는 컨테이너 포트에 매핑됩니다.
    port: 80
    targetPort: http
  - name: https            >  포트 443은 컨테이너 포트의 이름이 https인 것에 매핑됩니다.
    port: 443
    targetPort: https

이렇게 하면 나중에 서비스 스펙을 변경하지 않고도 포트 번호를 변경할 수있다는 큰 장점이 있습니다. 파드는 현재 http라는 이름에 포트 8080을 사용하고 있지만 나중에 포트 80으로 변경하기로 했다고 가정해봅시다. 포트가 번호로 설정되어 있었다면 파드마다 일일이 들어가서 수정해야할 것입니다. 하지만 이름을 지정했다면 이름이 있는 곳만 수정하면 됩니다.

반응형
반응형

목차

  1. 잡을 지정한 시간마다 작동시키기
  2. 크론잡 YAML 작성하기
  3. 스케줄 생성하기
  4. CronJob에 데드라인 지정하기

잡을 지정한 시간마다 작동시키기

잡 리소스를 생성하면 즉시 해당하는 파드를 실행합니다. 그러나 많은 배치 잡이 미래의 특정 시간 또는 지정된 간격으로 반복 실행해야 합니다. 예를들면 정기 업데이트 등이 있을 것 같습니다. 이러한 기능은 리눅스 명령어 중에서도 cron 명령어가 있었고 쿠버네티스에서도 이를 지원합니다.

 

쿠버네티스에서의 크론 작업은 크론잡 리소스를 만들어 구성합니다. 잡 실행을 위한 스케줄은 잘 알려진 크론 형식으로 지정하므로, 일반적인 크론 작업에 익숙하다면 금방 쿠버네티스의 크론잡을 이해할 수 있을 것입니다.

 

쿠버네티스는 설정된 시간에 잡 리소스가 생성되면 앞에서 배운 것처럼 하나 이상의 파드 복제본이 잡의 파드 템플릿에 따라 생성합니다. 잡 리소스가 생성되면 앞에서 배운 것처럼 하나 이상의 파드 복제본이 잡의 파드 템플릿에 따라 생성되고 시작합니다. 사실 그 이상은 특별한 것이 없습니다.


크론잡 YAML 작성하기

이 예제는 매 15분마다 배치 잡을 실행하는 것을 가정합니다.

apiVersion: batch/v1beta1                          >  CronJob의 api 버전입니다.
kind: CronJob
metadata:
  name: batch-job-every-fifteen-minutes
spec:
  schedule: "0,15,30,45 * * * *"                    >  매일, 매시간 0, 15, 30, 45분에 실행한다는 뜻입니다.
  jopTemplate:
    spec:
      template:
        metadata:
          labels:
            app: periodic-batch-jop
        spec:
          restartPolicy: OnFailure
          containers:
          - name: main
            image: luksa/batch-jop

스케줄 생성하기

크론의 스케줄 형식에 익숙하지 않을 수 있으니 간단히 소개하자면 스케줄은 왼쪽엣 오른쪽으로 다섯 개의 항목을 갖고 있습니다.

schedule: <분 / 시 / 일 / 월 / 요일>

위 예제를 예로들면 다음과 같습니다.

 schedule: "0,15,30,45 * * * *"

0,15,30,45 : 0, 15, 30, 45분 마다
*                : 매시(0~23)
*                : 매일(1~31)
*                : 매월(1~12)
*                : 모든 요일(0~7, 이 때 0과 7은 일요일입니다.)

CronJob에 데드라인 지정하기

잡 리소스는 대략 예정된 시간에 크론잡 리소스에서 생성됩니다. 그럼 잡은 파드를 생성하게 됩니다. 잡이나 파드가 상대적으로 늦게 생성되고 실행될 수 있습니다. 따라서 예정된 시간을 너무 초과해 시작돼서는 안 된다는 엄격한 요구 사항을 갖게 할 수 있습니다. 이런 경우 다음과 같이 startingDeadlineSeconds 필드를 지정해 데드라인을 설정할 수 있습니다.

apiVersion: batch/v1beta1
kind: CronJob
spec:
  schedule: "0,15,30,45 * * * *"
  startingDeadlineSeconds: 15
...

이 예제에서 매 15분 마다 실행하는데 어떠한 이유에서든 15초가 지나도 시작하지 않으면 잡이 실행되지 않고 실패로 표시됩니다.

 

일반적인 상황에서 크론잡은 스케줄에 설정한 각 실행에 항상 하나의 잡만 생성하지만 두개의 잡이 동시에 생성되거나 전혀 생성되지 않을 수 있습니다. 첫 번째 문제를 해결하려면 잡이 멱등성을 가져야 합니다. 두 번째 문제점의 경우 다음 번 잡 실행이 이전의 실행에서 완료했어야 하는 작업을 수행하는지 확인해야 합니다.

반응형
반응형

목차

  1. 잡(Job) 리소스
  2. 잡 리소스 YAML 파일 작성
  3. 잡에서 여러 파드 인스턴스 실행
  4. 잡 스케일링
  5. 잡 파드가 완료되는 데 걸리는 시간 제한하기

이전까지는 계속 지속되어 실행돼야 하는 파드에 대해 다루었습니다. 하지만 작업을 완료한 후에는 종료되는 테스크만 실행하려는 경우가 있을 것입니다. 레플리케이션 컨트롤러, 레플리카셋, 데몬셋은 완료되었다고 간주되지 않는 지속적인 테스크를 실행합니다. 그러나 완료 가능한 테스크에서는 프로세스가 종료된 후에 다시 시작되지 않습니다.

 

  • 리소스
    리소스(Resources)란 시스템에서 사용되는 하드웨어, 소프트웨어 및 데이터와 같은 자원들을 의미합니다. 컴퓨터와 관련된 여러 분야에서 사용되지만 쿠버네티스에서 리소스(Resource)는 컨테이너가 사용할 수 있는 CPU, 메모리, GPU 등의 하드웨어 자원을 의미하는 데 사용됩니다. 컨테이너에 리소스를 할당함으로써, 컨테이너 간에 리소스가 공정하게 분배되어 안정적인 서비스 운영이 가능해집니다.

잡(Job) 리소스

쿠버네티스는 잡(Job) 리소스로 이런 기능을 지원하며, 이전에서 논의된 다른 리소스와 유사하지만 잡은 파드의 컨테이너 내부에서 실행 중인 프로세스가 성공적으로 완료되면 컨테이너를 다시 시작하지 않는 파드를 실행할 수 있습니다. 일단 그렇게 되면 파드는 완료된 것으로 간주됩니다.

 

노드에 장애가 발생한 경우 해당 노드에 있던 잡이 관리하는 파드는 레플리카셋 파드와 같은 방식으로 다른 노드로 다시 스케줄링됩니다. 프로세스 자체에 장애가 발생한 경우, 잡에서 컨테이너를 다시 시작할 것인지 설정할 수 있습니다.

 

예를 들어 잡은 작업이 제대로 완료되는 것이 중요한 임시 작업에 유용합니다. 관리되지 않은 파드에서 작업을 실행하고 완료될 때까지 기다릴 수 있지만 작업이 수행되는 동안 노드에 장애가 발생하거나 파드가 노드에서 제거되는 경우 수동적으로 다시 생성해야 합니다. 특히 잡을 완료하는 데 몇 시간이 걸리는 경우 이 작업을 수동으로 수행한다는 것은 말이 되지 않는 일입니다. 이러한 잡은 데이터를 어딘가에 저장하고 있고, 이 데이터를 변환해서 어딘가로 전송해야 하는 경우를 예시로 들 수 있습니다. 


잡 리소스 YAML 파일 작성
apiVersion: batch/v1                              >  잡의 API 그룹입니다.
kind: Job
metadata:
  name: batch-jop
spec:                                                       >  파드 셀렉터를 따로 지정하지 않았지만 파드 템플릿의 레이블을 
  template:                                                  기반으로 만들어집니다.
    metadata:
      labels:
        app: batch-job
  spec:
    restartPolicy: OnFailure                      >  잡은 기본 재시작 정책(Always)를 사용할 수 없습니다.
      containers:
      - name: main
        image: luksa/batch-job

이 이미지는 정확히 120초 동안 실행된 후 종료되는 프로세스를 호출합니다.

 

파드 스펙에서는 컨테이너에서 실행 중인 프로세스가 종료될 때 쿠버네티스가 수행할 작업을 지정할 수 있습니다. 이 작업은 파드 스펙 속성인 restartPolicy로 수행되며 기본값은 Always입니다. 잡 파드는 무한정 실행하지 않으므로 기본 정책을 사용할 수 없습니다. 따라서 restartPolicy를 OnFailure나 Never로 명시적으로 설정해야 합니다. 이 설정은 컨테이너가 종료될 때 재시작되지 않도록 하는 것입니다. 이는 컨테이너가 종료 후에 재시작되지 않는 이유가 파드를 잡 리소스로 관리하기 때문이 아니라는 뜻이기도 합니다.


잡에서 여러 파드 인스턴스 실행

잡은 두 개 잇아의 파드 인스턴스를 실행해 병렬 또는 순차적으로 실행하도록 구성할 수 있습니다. 이는 잡 스펙에 completions와 parallelism 속성을 설정해 수행합니다.

 

  • 순차적으로 잡 파드 실행하기

잡을 두 번 이상 실행해야 하는 경우 잡의 파드를 몇 번 실행할지를 completions에 설정합니다.

apiVersion:  batch/v1
kind: Job
metadata:
  name: multi-completion-batch-job
spec:
  completions: 5                     > 이 숫자만큼 잡 파드를 순차적으로 생성합니다.
  template:
    <template is the same as in listing 4.11>

이 잡은 차례로 다섯 개의 파드를 실행합니다. 처음에는 파드를 하나 만들고, 파드의 컨테이너가 완료도면 두 번째 파드를 만들어 다섯 개의 파드가 성공적으로 완료될 때까지 위의 과정을 계속합니다. 파드 중 하나가 실패하면 잡이 새 파드를 생성하므로 잡이 전체적으로 다섯 개 이상의 파드를 생성할 수 있습니다.

 

  • 병렬로 잡 파드 실행하기

잡 파드를 하나씩 차례로 실행하는 대신 잡이 여러 파드를 병렬로 실행할 수도 있습니다. 다음 예제에서 볼 수 있는 것과 같이 잡 스펙의 parallelism 속성을 이용해 병렬로 실행할 파드 수를 지정합니다.

apiVersion:  batch/v1
kind: Job
metadata:
  name: multi-completion-batch-job
spec:
  completions: 5
  parallelism: 2    > 2개까지 병렬로 실행할 수 있도록 해줍니다.
  template:
    <template is the same as in listing 4.11>

잡 스케일링

잡이 실행되는 동안 잡의 parallelism 속성을 변경할 수도 있습니다. 이것은 레플리카셋이나 레플리케이션 컨트롤러를 스케일링하는 것과 유사하며, kubectl scale로 relicas 수를 변경할 수도 있습니다.

kubectl scale job multi-complietion-batch-job --replicas 3

잡 파드가 완료되는 데 걸리는 시간 제한하기

잡은 파드가 완료될 때까지 얼마나 기다려야 할까요? 파드가 특정 상태에 빠져서 도무지 완료할 수 없는 경우나 충분히 빠르게 완료할 수 없을 경우 어떻게 해야 할까요?

 

파드 스펙에 activeDeadlineSeconds 속성을 설정해 파드의 실행 시간을 제한할 수 있습니다. 파드가 이보다 오래 실행되면 시스템이 종료를 시도하고 잡을 실패한 것으로 표시합니다.

반응형