Kubernetes 연구 노트 - 애플리케이션 개발을 위한 모범 사례(2) 20230604

3. 모든 클라이언트 요청이 제대로 처리되는지 확인

팟(Pod)이 시작될 때 모든 연결이 제대로 처리되는지 확인하는 방법

1. Pod 시작 시 클라이언트 연결 끊김 방지

팟(Pod)이 시작되면 레이블 선택기가 팟(Pod)의 레이블과 일치하는 서비스 엔드포인트로 모든 서비스를 노출합니다. 포드는 준비가 되었다는 신호를 kubernetes에 보내야 합니다. 포드가 준비되면 서비스 엔드포인트가 될 수 있습니다. 그렇지 않으면 클라이언트 연결 요청을 수락할 수 없습니다.

포드 사양에 준비 프로브가 지정되지 않은 경우 포드는 항상 준비된 것으로 간주됩니다. 첫 번째 kube-proxy가 노드에서 iptables 규칙을 업데이트하고 첫 번째 클라이언트 포드가 서비스에 연결하기 시작하면 기본적으로 준비된 것으로 간주되는 포드가 거의 즉시 요청 수락을 시작합니다. 애플리케이션이 현재 연결을 수락할 준비가 되지 않은 경우 클라이언트는 "연결 거부됨"과 같은 오류 메시지를 받게 됩니다.

메시지가 들어오는 요청을 처리할 준비가 된 경우에만 준비 프로브가 성공을 반환하도록 만들기만 하면 됩니다. 실제로 첫 번째 단계는 HTTP GET 요청에 대한 준비 프로브를 애플리케이션 루트 URL에 추가하는 것입니다.

2. 포드가 닫히면 클라이언트 연결이 끊어집니다.

포드가 삭제되고 포드의 컨테이너가 종료되면 포드의 컨테이너가 SIGTERM 신호를 수신할 때 어떻게 깔끔하게 종료됩니까? 모든 클라이언트 요청이 제대로 처리되도록 하려면 어떻게 해야 합니까?

포드가 삭제될 때 발생하는 일련의 이벤트 이해

1) api 서버는 포드 삭제 요청을 받으면 먼저 etcd의 상태를 수정하고 관찰자에게 삭제 이벤트를 알립니다. 옵저버 중 두 개는 kubelet과 엔드포인트 컨트롤러입니다.

kubelet은 포드 애플리케이션이 종료되었다는 알림을 받으면 pre-stop hook을 실행하고 SIGTERM 신호를 보내고 일정 시간 동안 기다린 후 컨테이너가 자체 종료되지 않으면 컨테이너를 강제로 종료합니다.

응용 프로그램이 SIGTERM 신호에 대한 응답으로 클라이언트 요청 수락을 중지하는 것을 이해하는 경우 응용 프로그램에 연결하려는 모든 시도는 연결 거부 오류를 수신하게 됩니다. 포드가 삭제된 시점부터 이 요청이 발생한 시점까지의 시간은 비교적 짧습니다. 이것은 api 서버와 kubelet 간의 직접적인 통신이기 때문입니다.

2) iptables 규칙에서 포드를 제거하기 전에 엔드포인트 컨트롤러는 포드가 삭제될 것이라는 알림을 받으면 포드가 위치한 모든 서비스에서 포드의 서비스 엔드포인트를 제거합니다. API 서버에 REST 요청을 보내 Endpoit API 개체를 수정합니다. 그런 다음 API 서버는 모든 클라이언트에게 이 Endpoint 개체에 주의를 기울이도록 알립니다. 이러한 감시자 중 일부는 작업자 노드에서 실행되는 kube-proxy 서비스입니다. 각 kube-proxy 서비스는 자체 노드에서 iptables 규칙을 업데이트하여 새 연결이 중지된 포드로 전달되지 않도록 합니다.

위의 두 가지 일련의 시간이 병렬적으로 발생하는데, 아마도 포드에서 애플리케이션 프로세스를 종료하는 데 걸리는 시간은 iptables 규칙의 업데이트를 완료하는 데 필요한 시간보다 약간 짧을 가능성이 높습니다. iptables 규칙의 업데이트는 상대적으로 깁니다. 이러한 이벤트는 먼저 엔드포인트 컨트롤러에 도달해야 하기 때문에 엔드포인트 컨트롤러는 API 서버에 새 요청을 보내고 API 서버는 kube-proxy를 제어해야 하며 마지막으로 kube-proxy는 iptables 규칙을 수정합니다. iptables 규칙이 모든 노드에 업데이트되기 전에 SIGTERM 신호가 전송될 가능성이 높습니다.

최종 결과는 Pod에 종료 신호를 보낸 후에도 Pod가 여전히 클라이언트 요청을 받을 수 있다는 것입니다. 응용 프로그램이 서버 소켓을 닫고 요청 수신을 중지하는 것을 이해하면 클라이언트가 "연결 거부" 유형 오류를 수신하게 됩니다.

문제를 풀다

포드에 준비 프로브를 추가하면 문제가 해결됩니다. 필요한 모든 것이 포드가 SIGTERM 신호를 수신할 때 준비 프로브가 실패하기 시작하는 것이라면 서비스 엔드포인트에서 포드가 제거됩니다. 그러나 이 제거 작업은 준비 프로브가 일정 시간 동안 계속 실패한 후에만 발생하며(준비 프로브의 사양에서 구성 가능) 이 제거 작업은 먼저 kube-proxy에 도달해야 하며 그런 다음 iptables 규칙이 적용됩니다. 이 포드를 제거했습니다.

당신이 할 수 있는 유일한 합당한 일은 모든 kube-proxy가 작업을 마칠 때까지 충분히 오래 기다리는 것입니다. 그러면 얼마나 걸립니까? 대부분의 시나리오에서 몇 초면 충분하지만 매번 충분하다는 보장은 없습니다. API 서버 또는 엔드포인트 컨트롤러가 과부하되면 알림이 kube-proxy에 도달하는 데 더 오래 걸립니다. 이 문제에 대한 완벽한 해결책이 없다는 것을 이해하는 것이 중요하지만 5초 또는 10초의 시간 지연을 추가하는 것도 사용자 경험을 크게 향상시킬 수 있습니다. 더 긴 지연 시간을 사용할 수 있지만 너무 길면 안 됩니다. 이렇게 하면 컨테이너가 정상적으로 종료되지 않고 포드가 오랫동안 삭제되고 여전히 목록에 표시되므로 원인이 됩니다. 포드를 삭제하는 사용자의 문제.

요약:

애플리케이션을 정상적으로 종료하려면 다음 단계를 따르세요.

  • 몇 초간 기다린 다음 새 연결 수락을 중지합니다.
  • 요청되지 않은 긴 연결을 모두 닫습니다.
  • 모든 요청이 완료될 때까지 기다리십시오.
  • 그런 다음 응용 프로그램을 완전히 닫습니다.

4. 애플리케이션을 쿠버네티스에서 편리하게 실행하고 관리하자

1. 관리 가능한 컨테이너 이미지 구축

애플리케이션을 이미지로 패키징할 때 애플리케이션의 바이너리 파일과 종속 라이브러리를 포함하거나 전체 운영 체제와 애플리케이션을 함께 패키징할 수 있습니다. 이미지의 운영 체제에 있는 모든 파일이 필요합니까? 아니요, 대부분의 파일은 사용되지 않으며 이미지를 필요 이상으로 크게 만듭니다. 포드가 노드에 처음으로 예약되면 시간이 오래 걸립니다. 최소 빌드 이미지는 디버그하기가 매우 어렵습니다. 컨테이너에서 ping, dig, curl 또는 기타 유사한 명령과 같은 일부 도구를 실행해야 하는 경우 컨테이너가 최소한 이러한 도구 세트를 포함하는 것이 얼마나 중요한지 깨닫게 될 것입니다.

이미지에 포함된 도구와 포함되지 않은 도구는 사용자의 필요에 따라 다릅니다.

2. 합리적으로 이미지에 레이블을 지정하고 ImagePullPolicypod를 올바르게 사용하십시오.

매니페스트에서 최신 버전을 사용하지 않는 것이 가장 좋습니다. 그렇지 않으면 지정된 버전으로 대체할 수 없습니다.

특정 버전을 지정할 수 있는 레이블을 사용해야 하며 변경 가능한 레이블을 사용하는 경우 pod spec에서 imagepullpolicy를 always로 설정해야 합니다. 그러나 프로덕션 환경에서 이 방법을 사용하는 경우 그의 추가 지침에 주의해야 합니다. 이미지의 풀 정책이 항상으로 설정된 경우 컨테이너 작업은 배포해야 하는 새 포드를 발견할 때 이미지 레지스트리에 연결합니다. 노드가 이미지가 수정되었는지 여부를 확인해야 하므로 포드 시작 속도가 느려집니다. 설상가상으로 이 전략은 미러 레지스트리에 도달할 수 없을 때 새 포드가 시작되지 않도록 합니다.

3. 단일 차원 레이블 대신 다차원 레이블 사용

레이블에는 다음이 포함될 수 있습니다.

  • 리소스가 속한 애플리케이션(또는 마이크로서비스)의 이름
  • 애플리케이션 수준(프론트엔드, 백엔드 등)
  • 런타임 환경(개발, 테스트, 스테이징, 프로덕션 등)
  • 버전 번호
  • 릴리스 유형
  • 릴리스 유형(안정적, 카나리아, 청록색 개발의 녹색 또는 파란색 등)
  • 테넌트(네임스페이스를 사용하는 대신 각 테넌트에서 다른 포드를 실행하는 경우)
  • 샤딩(샤딩이 있는 시스템)

태그 관리를 통해 리소스를 격리되지 않고 그룹으로 관리할 수 있으므로 리소스가 속한 위치를 쉽게 이해할 수 있습니다.

4. 주석을 통해 각 리소스 설명

주석을 사용하여 리소스에 추가 정보를 추가할 수 있습니다. 리소스는 적어도 리소스를 설명하는 주석과 리소스 소유자를 설명하는 주석을 포함해야 합니다.

마이크로서비스 프레임워크에서 포드는 포드가 의존하는 다른 서비스의 이름을 설명하는 주석을 포함해야 합니다. 이렇게 하면 포드 간의 종속성을 쉽게 표시할 수 있습니다. 다른 주석에는 빌드 및 버전 정보와 다른 도구 또는 GUI에서 사용하는 메타 정보(아이콘 이름 등)가 포함될 수 있습니다.

5. 프로세스 종료에 대한 추가 정보 제공

진단을 쉽게 하기 위해 포드 상태에 컨테이너 종료 이유를 표시하면 컨테이너의 프로세스가 컨테이너 시스템의 지정된 파일에 종료 메시지를 작성할 수 있습니다. 이 파일의 내용은 컨테이너가 종료된 후 kubelet에서 읽은 다음 kubectl describe pod에 표시됩니다. 이 프로세스가 종료 메시지를 작성해야 하는 파일의 기본 경로는 /dev/termination-log입니다. 이 경로는 Pod 사양의 컨테이너 정의 부분에서 terminationMessagePath 필드를 설정하여 사용자 지정할 수도 있습니다.

참고: 컨테이너가 어떤 파일에도 메시지를 쓰지 않는 경우 terminationMessagePolicy 필드는 FallbackToLogsOnError로만 설정할 수 있습니다. 이 경우 컨테이너 로그의 마지막 몇 줄은 종료 메시지로 처리됩니다(컨테이너가 성공적으로 종료되지 않은 경우에만).

6. 애플리케이션 로그 처리

애플리케이션은 파일 대신 표준 출력 인터럽트에 로그를 기록하며 애플리케이션 로그는 kubectl log 명령을 통해 쉽게 볼 수 있습니다.

팁: 컨테이너가 충돌하고 새 컨테이너로 교체되면 새 컨테이너의 로그가 표시됩니다. 이전 컨테이너의 로그를 보려면 kubectl logs 명령을 사용할 때 --provious 옵션을 추가하십시오.

애플리케이션이 표준 출력 터미널 대신 파일에 로그를 작성하는 경우 로그를 보는 다른 방법이 있습니다.

$kubectl exec <포드> 고양이 <로그 파일>        

이 명령은 컨테이너 내에서 cat 명령을 실행하고 로그 스트림을 kubectl로 반환하며 kubectl은 이를 터미널에 표시합니다.

컨테이너와 로그 또는 기타 파일 복사

파일을 로컬 컴퓨터로 전송합니다.

$kubectl cp foo-pod:/var/log/foo.log foo.log

로컬 머신에서 팟(Pod)으로 파일을 복사하려면 팟(Pod) 이름을 두 번째 인수로 지정할 수 있습니다.

$kubectl cp localfile foo-pod:/etc/remotefile

중앙 집중식 로깅 사용

kubectl 자체는 중앙 집중식 로깅을 제공하지 않으며 일반적으로 클러스터에서 일반 포드로 실행되는 다른 구성 요소를 통해 모든 컨테이너 로그의 중앙 집중식 저장 및 분석을 지원해야 합니다.

중앙 집중식 로깅 솔루션을 배포하는 것은 매우 간단합니다. 몇 가지 YAML/JSON 매니페스트 파일을 배포하기만 하면 됩니다.

Google의 kubernetes 엔진에서는 훨씬 더 간단합니다. 클러스터를 설정할 때 "Strackdriver 로깅 사용" 옵션을 선택하기만 하면 됩니다.

ElasticSearch, Logstash 및 Kibanna로 구성된 ELK 스택에 대해 들어보셨을 것입니다. Logstash가 FluentD로 대체된 EFK 스택의 약간 수정된 변형입니다.

EFK를 중앙 집중식 로깅으로 사용하는 경우 각 kubernetes 클러스터 노드는 FluentD 에이전트(DaemonSet를 포드로 사용하여 배포)를 실행합니다.이 에이전트는 컨테이너에서 로그를 수집하고 로그에 포드 관련 정보를 표시한 다음 이를 보내야 합니다. ElasticSearch 및 ElasticSearch는 이를 영구적으로 저장합니다. ElasticSearch는 또한 클러스터에서 포드로 배포됩니다. 이러한 로그는 종종 포드로 실행되고 서비스로 노출되는 ElasticSearch 데이터를 시각화하는 도구인 kubana를 통해 웹 브라우저에서 보고 분석할 수 있습니다. EFK의 세 가지 구성 요소는 아래 그림에 나와 있습니다.

여러 줄 로그 입력 처리

FluentD 에이전트는 로그 파일의 각 줄을 ElasticSearch 데이터 저장소에 항목으로 저장하며, 로그 출력이 java의 예외 스택과 같이 여러 줄에 걸쳐 있는 경우 중앙 로깅 시스템에 다른 항목으로 저장됩니다.

이 문제를 해결하기 위해 응용 프로그램 로그 출력 내용을 일반 텍스트 대신 JSON 형식으로 만들 수 있습니다. 이러한 방식으로 여러 줄의 로그 출력을 항목으로 저장할 수 있습니다. kibana에서 항목으로 표시될 수도 있지만 이 접근 방식은 kubectl log 명령을 사용하여 로그를 보기 어렵게 만듭니다.

해결 방법은 표준 출력 터미널에 대한 로그 출력은 여전히 ​​사용자 확장 로그이지만 FluentD가 처리할 로그 파일에 기록된 로그는 JSON 형식이라는 것입니다. 이렇게 하려면 노드 수준에서 FluentD 에이전트를 합리적으로 구성하거나 각 포드에 경량 로깅 컨테이너를 추가해야 합니다.

 

추천

출처blog.csdn.net/wwxsoft/article/details/131034972