From d831aa23acc6070abac93a7cd1ef30535b137887 Mon Sep 17 00:00:00 2001 From: "Claudia J.Kang" Date: Tue, 23 Apr 2019 23:12:09 +0900 Subject: [PATCH] Second Korean l10n work for release-1.14 (#13970) * Update outdated files in dev-1.14-ko.2 partly (#13879) * Update outdated files in dev-1.14-ko.2 partly-concepts-contribute-ref (#13890) * ko: update dev-1.14-ko.2 for docs/tutorials/configuration/configure-redis-using-configmap.md #13738 (#13891) * Ko trans: translate contribute/participating.md in Korean (#13812) * Update outdated files of setup and tasks in dev-1.14-ko.2 partly (#13893) * ko-trans: Fix typo in install-minikube (#13894) * ko: Add tutorials/stateful-application/zookeeper #12452 (#13523) * ko: Add tutorials/services/source-ip #12457 (#13524) Co-authored-by: Yoon Co-authored-by: June Yi Co-authored-by: Seokho --- content/ko/_index.html | 2 +- content/ko/case-studies/ibm/index.html | 7 +- content/ko/case-studies/naic/index.html | 15 +- content/ko/case-studies/pinterest/index.html | 4 +- .../configuration/scheduler-perf-tuning.md | 52 +- content/ko/docs/concepts/containers/images.md | 26 +- .../ko/docs/concepts/overview/components.md | 2 + .../docs/concepts/overview/kubernetes-api.md | 6 +- .../object-management-kubectl/overview.md | 4 +- .../kubernetes-objects.md | 2 +- .../concepts/workloads/pods/pod-lifecycle.md | 2 +- .../concepts/workloads/pods/pod-overview.md | 2 +- content/ko/docs/contribute/_index.md | 2 +- content/ko/docs/contribute/participating.md | 263 ++++ content/ko/docs/reference/_index.md | 20 +- content/ko/docs/reference/glossary/taint.md | 4 +- .../ko/docs/reference/glossary/toleration.md | 6 +- .../ko/docs/reference/kubectl/cheatsheet.md | 20 +- content/ko/docs/setup/cri.md | 111 +- content/ko/docs/setup/multiple-zones.md | 6 +- content/ko/docs/setup/pick-right-solution.md | 34 +- .../ko/docs/tasks/tools/install-minikube.md | 2 +- .../configure-redis-using-configmap.md | 66 +- content/ko/docs/tutorials/services/_index.md | 5 + .../ko/docs/tutorials/services/source-ip.md | 342 +++++ .../stateful-application/zookeeper.md | 1095 +++++++++++++++++ .../application/zookeeper/zookeeper.yaml | 133 ++ 27 files changed, 2064 insertions(+), 169 deletions(-) create mode 100644 content/ko/docs/contribute/participating.md create mode 100644 content/ko/docs/tutorials/services/_index.md create mode 100644 content/ko/docs/tutorials/services/source-ip.md create mode 100644 content/ko/docs/tutorials/stateful-application/zookeeper.md create mode 100644 content/ko/examples/application/zookeeper/zookeeper.yaml diff --git a/content/ko/_index.html b/content/ko/_index.html index a73327cdf33a8..fbe20dc0dd938 100644 --- a/content/ko/_index.html +++ b/content/ko/_index.html @@ -8,7 +8,7 @@ {{< blocks/section id="oceanNodes" >}} {{% blocks/feature image="flower" %}} -### [쿠버네티스 (K8s)]({{< relref "/docs/concepts/overview/what-is-kubernetes" >}})는 컨테이너화된 애플리케이션을 자동으로 배포, 스케일링 및 관리해주는 오픈소스 시스템입니다. +### [쿠버네티스(K8s)]({{< relref "/docs/concepts/overview/what-is-kubernetes" >}})는 컨테이너화된 애플리케이션을 자동으로 배포, 스케일링 및 관리해주는 오픈소스 시스템입니다. 애플리케이션을 구성하는 컨테이너들의 쉬운 관리 및 발견을 위해서 컨테이너들을 논리적인 단위로 그룹화합니다. 쿠버네티스는 [Google에서 15년간 프로덕션 워크로드 운영한 경험](http://queue.acm.org/detail.cfm?id=2898444)을 토대로 구축되었으며, 커뮤니티에서 제공한 최상의 아이디어와 방법들이 결합되어 있습니다. {{% /blocks/feature %}} diff --git a/content/ko/case-studies/ibm/index.html b/content/ko/case-studies/ibm/index.html index 4f6927416c1a7..54e941c9cb3c8 100644 --- a/content/ko/case-studies/ibm/index.html +++ b/content/ko/case-studies/ibm/index.html @@ -6,10 +6,7 @@ cid: caseStudies css: /css/style_case_studies.css logo: ibm_featured_logo.svg -featured: true -weight: 2 -quote: > - We see CNCF as a safe haven for cloud native open source, providing stability, longevity, and expected maintenance for member projects—no matter the originating vendor or project. +featured: false ---
@@ -34,7 +31,7 @@

Solution

- +

Impact

IBM's intention in offering a managed Kubernetes container service and image registry is to provide a fully secure end-to-end platform for its enterprise customers. "Image signing is one key part of that offering, and our container registry team saw Notary as the de facto way to implement that capability in the current Docker and container ecosystem," Hough says. The company had not been offering image signing before, and Notary is the tool it used to implement that capability. "We had a multi-tenant Docker Registry with private image hosting," Hough says. "The Docker Registry uses hashes to ensure that image content is correct, and data is encrypted both in flight and at rest. But it does not provide any guarantees of who pushed an image. We used Notary to enable users to sign images in their private registry namespaces if they so choose." diff --git a/content/ko/case-studies/naic/index.html b/content/ko/case-studies/naic/index.html index daf4201e5dd65..d40dd19c774fa 100644 --- a/content/ko/case-studies/naic/index.html +++ b/content/ko/case-studies/naic/index.html @@ -6,10 +6,7 @@ cid: caseStudies css: /css/style_case_studies.css logo: naic_featured_logo.png -featured: true -weight: 3 -quote: > - Our culture and technology transition is a strategy embraced by our top leaders. It has already proven successful by allowing us to accelerate our value pipeline by more than double while decreasing our costs by more than half. +featured: false ---
@@ -36,7 +33,7 @@

Solution

- +

Impact

Leveraging Kubernetes, "our development teams can create rapid prototypes far faster than they used to," Barker said. Applications running on Kubernetes are more resilient than those running in other environments. The deployment of open source solutions is helping influence company culture, as NAIC becomes a more open and transparent organization. @@ -57,7 +54,7 @@

Impact

NAIC—which was created and overseen by the chief insurance regulators from the 50 states, the District of Columbia and five U.S. territories—provides a means through which state insurance regulators establish standards and best practices, conduct peer reviews, and coordinate their regulatory oversight. Their staff supports these efforts and represents the collective views of regulators in the United States and internationally. NAIC members, together with the organization’s central resources, form the national system of state-based insurance regulation in the United States.

The organization has been using the cloud for years, and wanted to find more ways to quickly deliver new services that provide more value for members and staff. They looked to Kubernetes for a solution. Within NAIC, several groups are leveraging Kubernetes, one being the Platform Engineering Team. "The team building out these tools are not only deploying and operating Kubernetes, but they’re also using them," Barker says. "In fact, we’re using GitLab to deploy Kubernetes with a pipeline using kops. This team was created from developers, operators, and quality engineers from across the company, so their jobs have changed quite a bit."

-In addition, NAIC is onboarding teams to the new platform, and those teams have seen a lot of change in how they work and what they can do. "They now have more power in creating their own infrastructure and deploying their own applications," Barker says. They also use pipelines to facilitate their currently manual processes. NAIC has consumers who are using GitLab heavily, and they’re starting to use Kubernetes to deploy simple applications that help their internal processes. +In addition, NAIC is onboarding teams to the new platform, and those teams have seen a lot of change in how they work and what they can do. "They now have more power in creating their own infrastructure and deploying their own applications," Barker says. They also use pipelines to facilitate their currently manual processes. NAIC has consumers who are using GitLab heavily, and they’re starting to use Kubernetes to deploy simple applications that help their internal processes.
@@ -71,7 +68,7 @@

Impact

"We needed greater agility to enable our own productivity internally," he says. "We decided it was right for us to move everything to the public cloud [Amazon Web Services] to help with that process and be able to access many of the native tools that allows us to move faster by not needing to build everything." The NAIC also wanted to be cloud-agnostic, "and Kubernetes helps with this for our compute layer," Barker says. "Compute is pretty standard across the clouds, and now we can take advantage of any of them while getting all of the other features Kubernetes offers."

-The NAIC currently hosts internal systems and development systems on Kubernetes, and has already seen how impactful it can be. "Our development teams can create rapid prototypes in minutes instead of weeks," Barker says. "This recently happened with an internal tool that had no measurable wait time on the infrastructure. It was solely development bound. There is now a central shared resource that lives in AWS, which means it can grow as needed." +The NAIC currently hosts internal systems and development systems on Kubernetes, and has already seen how impactful it can be. "Our development teams can create rapid prototypes in minutes instead of weeks," Barker says. "This recently happened with an internal tool that had no measurable wait time on the infrastructure. It was solely development bound. There is now a central shared resource that lives in AWS, which means it can grow as needed." The native integrations into Kubernetes at NAIC has made it easy to write code and have it running in minutes instead of weeks. Applications running on Kubernetes have also proven to be more resilient than those running in other environments. "We even have teams using this to create more internal tools to help with communication or automating some of their current tasks," Barker says.

"We knew that Kubernetes had become the de facto standard for container orchestration," he says. "Two major factors for selecting this were the three major cloud vendors hosting their own versions and having it hosted in a neutral party as fully open source." @@ -89,7 +86,7 @@

Impact

- + The open governance and broad industry participation in CNCF provided a comfort level with the technology, Barker says. "We also see it as helping to influence our own company culture," he says. "We’re moving to be a more open and transparent company, and we are encouraging our staff to get involved with the different working groups and codebases. We recently became CNCF members to help further our commitment to community contribution and transparency."

Factors such as vendor-neutrality and cross-industry investment were important in the selection. "In our experience, vendor lock-in and tooling that is highly specific results in less resilient technology with fewer minds working to solve problems and grow the community," Barker says.

NAIC is a largely Oracle shop, Barker says, and has been running mostly Java on JBoss. "However, we have years of history with other applications," he says. "Some of these have been migrated by completely rewriting the application, while others are just being modified slightly to fit into this new paradigm."

@@ -106,7 +103,7 @@

Impact

- + NAIC has seen a significant business impact from its efforts. "We have been able to move much faster at lower cost than we were able to in the past," Barker says. "We were able to complete one of our projects in a year, when the previous version took over two years. And the new project cost $500,000 while the original required $3 million, and with fewer defects. We are also able to push out new features much faster." He says the organization is moving toward continuous deployment "because the business case makes sense. The research is becoming very hard to argue with. We want to reduce our batch sizes and optimize on delivering value to customers and not feature count. This is requiring a larger cultural shift than just a technology shift." NAIC is "becoming more open and transparent, as well as more resilient to failure," Barker says. "Even our customers are wanting more and more of this and trying to figure out how they can work with us to accomplish our mutual goals faster. Members of the insurance industry have reached out so that we can better learn together and grow as an industry." diff --git a/content/ko/case-studies/pinterest/index.html b/content/ko/case-studies/pinterest/index.html index 86c81feab3746..0aa2381aa12d6 100644 --- a/content/ko/case-studies/pinterest/index.html +++ b/content/ko/case-studies/pinterest/index.html @@ -45,8 +45,8 @@

Impact

- -


+ +


"So far it’s been good, especially the elasticity around how we can configure our Jenkins workloads on that Kubernetes shared cluster. That is the win we were pushing for."

— Micheal Benedict, Product Manager for the Cloud and the Data Infrastructure Group at Pinterest
diff --git a/content/ko/docs/concepts/configuration/scheduler-perf-tuning.md b/content/ko/docs/concepts/configuration/scheduler-perf-tuning.md index d817a5ee0f6f4..0416b9b1c4878 100644 --- a/content/ko/docs/concepts/configuration/scheduler-perf-tuning.md +++ b/content/ko/docs/concepts/configuration/scheduler-perf-tuning.md @@ -6,7 +6,7 @@ weight: 70 {{% capture overview %}} -{{< feature-state for_k8s_version="1.12" >}} +{{< feature-state for_k8s_version="1.14" state="beta" >}} Kube-scheduler는 쿠버네티스의 기본 스케줄러이다. 그것은 클러스터의 노드에 파드를 배치하는 역할을 한다. 파드의 스케줄링 요건을 충족하는 @@ -24,16 +24,24 @@ API 서버에 해당 결정을 통지한다. 쿠버네티스 1.12 이전 버전에서, Kube-scheduler는 클러스터의 모든 노드에 대한 적합성(feasibility)을 확인한 후에 적합한 노드들에 대해서 점수를 측정했다. -쿠버네티스 1.12는 새로운 특징을 가지고 있으며, 이 특징은 스케줄러가 특정 +쿠버네티스 1.12는 새로운 특징을 추가했으며, 이 특징은 스케줄러가 특정 숫자의 적합한 노드를 찾은 이후에 추가적인 적합 노드를 찾는 것을 중단하게 한다. 이것은 대규모 클러스터에서 스케줄러 성능을 향상시킨다. 해당 숫자는 클러스터 -크기에 대한 비율로 지정되며 `percentageOfNodesToScore` 구성 옵션으로 -제어된다. 값의 범위는 1과 100 사이여야 한다. 그 이외의 값은 100%로 간주한다. -이 옵션의 기본 값은 50%이다. 클러스터 관리자는 이 값은 스케줄러 구성에 다른 -값을 지정함으로써 기본 값을 변경할 수 있다. 그러나, 이 값을 변경할 필요는 없을 것이다. +크기에 대한 비율로 지정된다. 그 비율은 `percentageOfNodesToScore` 구성 +옵션으로 제어될 수 있다. 값의 범위는 1과 100 사이여야 한다. 더 높은 값은 +100%로 간주된다. 0은 구성 옵션을 제공하지 않는 것과 동일하다. +점수를 측정할 노드의 비율이 구성 옵션에 명시되지 않은 경우를 대비하여, 쿠버네티스 1.14는 +클러스터의 크기에 기반하여 해당 비율 값을 찾는 로직을 가지고 있다. 이 로직은 +100-노드 클러스터에 대해 50%를 값으로 출력하는 선형 공식을 사용한다. 해당 공식은 5000-노드 +클러스터에 대해서는 10%를 값으로 출력한다. 자동으로 지정되는 값의 하한값은 5%이다. 다시 +말해, 사용자가 구성 옵션에 5 보다 낮은 값은 지정하지 않은 한, 스케줄러는 +클러스터의 크기와 무관하게 적어도 5%의 클러스터에 대해서는 항상 점수를 +측정한다. + +아래는 `percentageOfNodesToScore`를 50%로 설정하는 구성 예제이다. ```yaml -apiVersion: componentconfig/v1alpha1 +apiVersion: kubescheduler.config.k8s.io/v1alpha1 kind: KubeSchedulerConfiguration algorithmSource: provider: DefaultProvider @@ -43,40 +51,36 @@ algorithmSource: percentageOfNodesToScore: 50 ``` -{{< note >}} -클러스터에서 적합한 노드가 0 또는 50 미만인 경우, -스케줄러는 여전히 모든 노드를 확인한다. 이는 스케줄러가 탐색을 조기 -중단하기에는 적합한 노드의 수가 충분하지 않기 때문이다. -{{< /note >}} +{{< note >}} 클러스터에서 적합한 노드가 50 미만인 경우, 스케줄러는 여전히 +모든 노드를 확인한다. 그 이유는 스케줄러가 탐색을 조기 중단하기에는 적합한 +노드의 수가 충분하지 않기 때문이다. {{< /note >}} **이 특징을 비활성화하려면**, `percentageOfNodesToScore`를 100으로 지정한다. ### percentageOfNodesToScore 튜닝 `percentageOfNodesToScore`는 1과 100 사이의 값이어야하며 -기본 값은 50이다. 또한 50%의 노드로 하드 코딩된 최소 값이 내부적으로 -적용된다. 스케줄러는 `percentageOfNodesToScore` 값과 상관없이 -적어도 50%의 노드 탐색을 수행한다. 이는 수백 개 정도의 노드가 있는 +기본 값은 클러스터 크기에 따라 계산된다. 또한 50 노드로 하드 코딩된 +최소 값도 있다. 이는 수백 개 정도의 노드가 있는 클러스터에서는 해당 옵션을 더 낮은 값으로 변경하더라도 스케줄러가 -찾으려는 적합한 노드의 개수에는 크게 영향을 주지 않는다는 -뜻이다. 이것은 규모가 작은 클러스터에서는 이 옵션의 조정이 성능을 -눈에 띄게 향상시키지 않는 것을 감안하여 의도적으로 설계되었다. 1000 노드 -이상의 큰 규모의 클러스터에서는 이 값을 낮은 수로 설정하면 눈에 띄는 성능 -향상을 보일 수도 있다. +찾으려는 적합한 노드의 개수에는 크게 영향을 주지 않는다는 뜻이다. +이것은 규모가 작은 클러스터에서는 이 옵션의 조정이 성능을 눈에 띄게 향상시키지 않는 +것을 감안하여 의도적으로 설계되었다. 1000 노드 이상의 큰 규모의 클러스터에서는 이 값을 +낮은 수로 설정하면 눈에 띄는 성능 향상을 보일 수도 있다. 이 값을 세팅할 때 중요하게 고려해야 할 사항은, 클러스터에서 적은 수의 노드에 대해서만 적합성을 확인하면, 주어진 파드에 대해서 일부 노드의 점수는 측정이되지 않는다는 것이다. 결과적으로, 주어진 파드를 실행하는데 가장 높은 점수를 가질 가능성이 있는 노드가 점수 측정 단계로 조차 넘어가지 않을 수 있다. 이것은 파드의 이상적인 배치보다 낮은 결과를 초래할 것이다. -그 이유로, 이 값은 너무 낮은 비율로 설정되면 안 된다. 대략의 경험적 법칙은 30 이하의 +그 이유로, 이 값은 너무 낮은 비율로 설정되면 안 된다. 대략의 경험적 법칙은 10 이하의 값으로는 설정하지 않는 것이다. 더 낮은 값은 사용자의 애플리케이션에서 스케줄러의 처리량이 치명적이고 노드의 점수가 중요하지 않을 경우에만 사용해야 한다. 다시 말해서, 파드의 실행에 적합하기만 하다면 어느 노드가 선택되어도 사용자에게 상관없는 경우를 말한다. -만약 사용자의 클러스터가 단지 백여 개의 노드를 가지고 있는 경우 기본 값보다 낮은 값으로의 -변경을 추천하지 않는다. 그것은 스케줄러의 성능을 -크게 향상시키지 않을 것이다. +만약 사용자의 클러스터가 단지 백여 개 또는 더 적은 노드를 가지고 있는 경우, 이 구성 옵션의 +기본 값보다 낮은 값으로의 변경을 추천하지 않는다. 그것이 스케줄러의 성능을 크게 +향상시키지는 않을 것이다. ### 스케줄러가 노드 탐색을 반복(iterate)하는 방법 diff --git a/content/ko/docs/concepts/containers/images.md b/content/ko/docs/concepts/containers/images.md index a3be5743078a5..76bd64fe5ef9e 100644 --- a/content/ko/docs/concepts/containers/images.md +++ b/content/ko/docs/concepts/containers/images.md @@ -202,7 +202,7 @@ Docker는 프라이빗 레지스트리를 위한 키를 `$HOME/.dockercfg` 또 프라이빗 이미지를 사용하는 파드를 생성하여 검증한다. 예를 들면 다음과 같다. ```yaml -kubectl create -f - < ./kustomization.yaml +secretGenerator: +- name: myregistrykey + type: docker-registry + literals: + - docker-server=DOCKER_REGISTRY_SERVER + - docker-username=DOCKER_USER + - docker-password=DOCKER_PASSWORD + - docker-email=DOCKER_EMAIL +EOF + +kubectl apply -k . +secret/myregistrykey-66h7d4d986 created ``` 만약 Docer 자격 증명 파일이 이미 존재한다면, 위의 명령을 사용하지 않고, @@ -296,7 +307,8 @@ secret/myregistrykey created. 이제, `imagePullSecrets` 섹션을 파드의 정의에 추가함으로써 해당 시크릿을 참조하는 파드를 생성할 수 있다. -```yaml +```shell +cat < pod.yaml apiVersion: v1 kind: Pod metadata: @@ -308,6 +320,12 @@ spec: image: janedoe/awesomeapp:v1 imagePullSecrets: - name: myregistrykey +EOF + +cat <> ./kustomization.yaml +resources: +- pod.yaml +EOF ``` 이것은 프라이빗 레지스트리를 사용하는 각 파드에 대해서 수행될 필요가 있다. diff --git a/content/ko/docs/concepts/overview/components.md b/content/ko/docs/concepts/overview/components.md index e5aa4bc5c9e6e..44bc70e9edf05 100644 --- a/content/ko/docs/concepts/overview/components.md +++ b/content/ko/docs/concepts/overview/components.md @@ -107,3 +107,5 @@ cloud-controller-manager는 클라우드 밴더 코드와 쿠버네티스 코드 [클러스터-레벨 로깅](/docs/concepts/cluster-administration/logging/) 메커니즘은 검색/열람 인터페이스와 함께 중앙 로그 저장소에 컨테이너 로그를 저장하는 책임을 가진다. {{% /capture %}} + + diff --git a/content/ko/docs/concepts/overview/kubernetes-api.md b/content/ko/docs/concepts/overview/kubernetes-api.md index 1e0f8b1b41e74..0ed8643c862b9 100644 --- a/content/ko/docs/concepts/overview/kubernetes-api.md +++ b/content/ko/docs/concepts/overview/kubernetes-api.md @@ -32,7 +32,7 @@ API에 원격 접속하는 방법은 [Controlling API Access doc](/docs/referenc 경험에 따르면, 성공적인 시스템은 새로운 유스케이스의 등장과 기존의 유스케이스의 변경에 맞춰 성장하고 변경될 필요가 있다. 그래서, 쿠버네티스 API가 지속적으로 변경되고 성장하기를 바란다. 그러나, 일정 기간 동안은 현존하는 클라이언트와의 호환성을 깨지 않으려고 한다. 일반적으로, 새로운 API 리소스와 새로운 리소스 필드가 주기적으로 추가될 것이다. 리소스나 필드를 없애는 일은 다음의 [API deprecation policy](/docs/reference/using-api/deprecation-policy/)를 따른다. -호환되는 변경에 어떤 내용이 포함되는지, 어떻게 API를 변경하는지에 대한 자세한 내용은 [API change document](https://git.k8s.io/community/contributors/devel/api_changes.md)에 있다. +호환되는 변경에 어떤 내용이 포함되는지, 어떻게 API를 변경하는지에 대한 자세한 내용은 [API change document](https://git.k8s.io/community/contributors/devel/sig-architecture/api_changes.md)에 있다. ## OpenAPI 및 Swagger 정의 @@ -67,12 +67,12 @@ GET /swagger-2.0.0.pb-v1.gz | GET /openapi/v2 **Accept**: application/com.github 필드를 없애거나 리소스 표현을 재구성하기 쉽도록, 쿠버네티스는 `/api/v1`이나 `/apis/extensions/v1beta1`과 같이 각각 다른 API 경로에서 복수의 API 버전을 지원한다. -리소스나 필드 수준보다는 API 수준에서 버전을 선택했는데, API가 명료하고, 시스템 리소스와 행위 관점에서 일관성있으며, 더 이상 사용하지 않는 API나 실험적인 API에 접근을 제어할 수 있도록 하기 위함이다. 스키마 변경에 대해서 JSON과 Protobuf 직렬화 스키마 모두 동일한 가이드라인을 따른다. 다음에 이어지는 설명 모두는 이 두 가지 형식에 모두 해당한다. +리소스나 필드 수준보다는 API 수준에서 버전을 선택했는데, API가 명료하고, 시스템 리소스와 행위 관점에서 일관성있으며, 더 이상 사용되지 않는 API나 실험적인 API에 접근을 제어할 수 있도록 하기 위함이다. 스키마 변경에 대해서 JSON과 Protobuf 직렬화 스키마 모두 동일한 가이드라인을 따른다. 다음에 이어지는 설명 모두는 이 두 가지 형식에 모두 해당한다. API 버전 규칙과 소프트웨어 버전 규칙은 간접적으로 연관되어 있음을 알아두자. [API and release versioning proposal](https://git.k8s.io/community/contributors/design-proposals/release/versioning.md)에는 API 버전 규칙과 소프트웨어 버전 규칙 간의 관계가 기술되어 있다. -API 버전이 다른 경우는 안정성이나 기술 지원의 수준이 다르다는 것을 암시한다. 각각의 수준에 대한 조건은 [API Changes documentation](https://git.k8s.io/community/contributors/devel/api_changes.md#alpha-beta-and-stable-versions)에서 상세히 다룬다. 요약하자면 다음과 같다. +API 버전이 다른 경우는 안정성이나 기술 지원의 수준이 다르다는 것을 암시한다. 각각의 수준에 대한 조건은 [API Changes documentation](https://git.k8s.io/community/contributors/devel/sig-architecture/api_changes.md#alpha-beta-and-stable-versions)에서 상세히 다룬다. 요약하자면 다음과 같다. - 알파(Alpha) 수준: - 버전 이름에 `alpha`가 포함된다. (예: `v1alpha1`) diff --git a/content/ko/docs/concepts/overview/object-management-kubectl/overview.md b/content/ko/docs/concepts/overview/object-management-kubectl/overview.md index fb663e3fd9932..9fb93ffca3685 100644 --- a/content/ko/docs/concepts/overview/object-management-kubectl/overview.md +++ b/content/ko/docs/concepts/overview/object-management-kubectl/overview.md @@ -7,7 +7,8 @@ weight: 10 {{% capture overview %}} `kubectl` 커맨드라인 툴은 쿠버네티스 오브젝트를 생성하고 관리하기 위한 몇 가지 상이한 방법을 지원한다. 이 문서는 여러가지 접근법에 대한 개요을 -제공한다. +제공한다. Kubectl으로 오브젝트 관리하기에 대한 자세한 설명은 +[Kubectl 서적](https://kubectl.docs.kubernetes.io)에서 확인한다. {{% /capture %}} {{% capture body %}} @@ -179,6 +180,7 @@ kubectl apply -R -f configs/ - [오브젝트 구성을 이용한 쿠버네티스 오브젝트 관리하기 (명령형)](/docs/concepts/overview/object-management-kubectl/imperative-config/) - [오브젝트 구성을 이용한 쿠버네티스 오브젝트 관리하기 (선언형)](/docs/concepts/overview/object-management-kubectl/declarative-config/) - [Kubectl 명령어 참조](/docs/reference/generated/kubectl/kubectl-commands/) +- [Kubectl 서적](https://kubectl.docs.kubernetes.io) - [쿠버네티스 API 참조](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/) {{< comment >}} diff --git a/content/ko/docs/concepts/overview/working-with-objects/kubernetes-objects.md b/content/ko/docs/concepts/overview/working-with-objects/kubernetes-objects.md index 96c5d64f017d9..e9b0c3608b1e3 100644 --- a/content/ko/docs/concepts/overview/working-with-objects/kubernetes-objects.md +++ b/content/ko/docs/concepts/overview/working-with-objects/kubernetes-objects.md @@ -41,7 +41,7 @@ card: {{< codenew file="application/deployment.yaml" >}} -위 예시와 같이 .yaml 파일을 이용하여 디플로이먼트를 생성하기 위한 하나의 방식으로는 `kubectl` 커맨드-라인 인터페이스에 인자값으로 `.yaml` 파일를 건네 [`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands#apply) 명령을 이용하는 것이다. 다음 예시와 같다. +위 예시와 같이 .yaml 파일을 이용하여 디플로이먼트를 생성하기 위한 하나의 방식으로는 `kubectl` 커맨드-라인 인터페이스에 인자값으로 `.yaml` 파일를 건네 [`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands#apply) 커맨드를 이용하는 것이다. 다음 예시와 같다. ```shell diff --git a/content/ko/docs/concepts/workloads/pods/pod-lifecycle.md b/content/ko/docs/concepts/workloads/pods/pod-lifecycle.md index 2a57ec6c172f2..5a964d83b2922 100644 --- a/content/ko/docs/concepts/workloads/pods/pod-lifecycle.md +++ b/content/ko/docs/concepts/workloads/pods/pod-lifecycle.md @@ -183,7 +183,7 @@ kubelet은 실행 중인 컨테이너들에 대해서 선택적으로 두 가지 ## 파드의 준비성 게이트(readiness gate) -{{< feature-state for_k8s_version="v1.12" state="beta" >}} +{{< feature-state for_k8s_version="v1.14" state="stable" >}} 파드의 준비성에 대한 확장성을 추가하기 위해서 추가적인 피드백이나 신호를 `PodStatus`에 주입하는 방법인, diff --git a/content/ko/docs/concepts/workloads/pods/pod-overview.md b/content/ko/docs/concepts/workloads/pods/pod-overview.md index 416f3983eb831..bf0b2e47297c8 100644 --- a/content/ko/docs/concepts/workloads/pods/pod-overview.md +++ b/content/ko/docs/concepts/workloads/pods/pod-overview.md @@ -106,5 +106,5 @@ spec: {{% capture whatsnext %}} * 파드의 다른 동작들을 더 배워보자. * [파드 종료](/docs/concepts/workloads/pods/pod/#termination-of-pods) - * [파드 라이프사이클](../pod-lifecycle) + * [파드 라이프사이클](/ko/docs/concepts/workloads/pods/pod-lifecycle/) {{% /capture %}} diff --git a/content/ko/docs/contribute/_index.md b/content/ko/docs/contribute/_index.md index 858fce32b79c5..a6dac0bb95499 100644 --- a/content/ko/docs/contribute/_index.md +++ b/content/ko/docs/contribute/_index.md @@ -47,7 +47,7 @@ weight: 80 시작하는 데에 도움이 될 것이다. - [누구나](/docs/contribute/start/) - - 조치 가능한 버그 리포트 제출 + - 조치 가능한 이슈 열기 - [멤버](/docs/contribute/start/) - 기존 문서 개선 - [Slack](http://slack.k8s.io/) 또는 [SIG docs 메일링 리스트](https://groups.google.com/forum/#!forum/kubernetes-sig-docs)에서 개선을 위한 아이디어 제시 diff --git a/content/ko/docs/contribute/participating.md b/content/ko/docs/contribute/participating.md new file mode 100644 index 0000000000000..babb4ce67a1b6 --- /dev/null +++ b/content/ko/docs/contribute/participating.md @@ -0,0 +1,263 @@ +--- +title: SIG Docs에 참여하기 +content_template: templates/concept +card: + name: contribute + weight: 40 +--- + +{{% capture overview %}} + +SIG Docs는 쿠버네티스 프로젝트의 +[분과회(special interest group)](https://github.com/kubernetes/community/blob/master/sig-list.md) +중 하나로, 쿠버네티스 전반에 대한 문서를 작성하고, 업데이트하며 유지보수하는 일을 주로 수행한다. +분과회에 대한 보다 자세한 정보는 +[커뮤니티 GitHub 저장소 내 SIG Docs](https://github.com/kubernetes/community/tree/master/sig-docs) +를 참조한다. + +SIG Docs는 모든 컨트리뷰터의 콘텐츠와 리뷰를 환영한다. 누구나 풀 리퀘스트(PR)를 요청할 수 있고, +누구나 콘텐츠에 대해 이슈를 등록하거나 진행 중인 풀 리퀘스트에 코멘트를 등록할 수 있다. + +SIG Docs 내에서, [멤버](#멤버), [리뷰어](#리뷰어), 또는 [승인자](#승인자)가 될 수도 있다. +이런 역할은 변경을 승인하고 커밋할 수 있도록 보다 많은 접근 권한과 이에 상응하는 책임이 수반된다. +쿠버네티스 커뮤니티 내에서 멤버십이 운영되는 방식에 대한 보다 많은 정보를 확인하려면 +[커뮤니티 멤버십](https://github.com/kubernetes/community/blob/master/community-membership.md) +문서를 확인한다. 문서의 나머지에서는 대외적으로 쿠버네티스를 가장 잘 드러내는 수단 중 하나인 쿠버네티스 +웹사이트와 문서를 관리하는 책임을 가지는 SIG Docs에서, 이런 체계가 작동하는 특유의 방식에 대한 윤곽을 +잡아보겠다. + +{{% /capture %}} + +{{% capture body %}} + +## 역할과 책임 + +풀 리퀘스트가 콘텐츠를 게재하는데 사용되는 브랜치(현재는 `master`)에 머지되면, 해당 콘텐츠가 세상에 +발행되어 널리 읽힐 수 있게 된다. 발행된 콘텐츠가 높은 품질을 유지하도록, SIG Docs 승인자만 +풀 리퀘스트를 머지할 수 있도록 제한한다. 다음과 같이 진행된다. + +- 풀 리퀘스트에 `lgtm`과 `approve` 레이블이 부여되고 `hold` 레이블이 없는 경우에, 해당 + 풀 리퀘스트가 자동으로 머지된다. +- 쿠버네티스 조직 멤버와 SIG Docs 승인자는 코멘트를 추가해서(`/hold` 코멘트를 추가하거나 + `/lgtm` 코멘트를 달지 않아서) 주어진 풀 리퀘스트가 자동으로 머지되는 것을 막을 수 있다. +- 쿠버네티스 멤버 누구나 `/lgtm` 코멘트를 달아서 `lgtm` 레이블을 추가할 수 있다. +- `/approve` 코멘트를 달아서 풀 리퀘스트를 머지할 수 있는 SIG Docs 멤버는 승인자 뿐이다. + 일부 승인자는 추가로 [PR Wrangler](#pr-wrangler) 또는 + [SIG Docs chairperson](#sig-docs-chairperson) 같이 특화된 역할을 수행한다. + +쿠버네티스 조직 멤버와 SIG Docs 승인자 역할 사이의 기대와 차이에 대한 보다 많은 정보는 +[컨트리뷰터 유형](/docs/contribute#types-of-contributor) 문서를 참고한다. +다음 섹션에서는 이런 역할과 SIG Docs에서 이들이 작동하는 방식에 대해 보다 상세한 내용을 +다룬다. + +### 모든 사람 + +문서를 포함해서, 쿠버네티스의 모든 부분에 대해서 누구나 이슈를 제기할 수 있다. + +CLA에 서명한 누구나 풀 리퀘스트를 제출할 수 있다. CLA에 서명할 수 없다면, +쿠버네티스 프로젝트는 컨트리뷰션을 수용할 수 없다. + +### 멤버 + +[쿠버네티스 조직](https://github.com/kubernetes)의 모든 멤버가 풀 리퀘스트를 리뷰할 수 있고, +기술적 정확도를 기하기 위해 SIG Docs 팀 멤버가 다른 분과회 멤버의 리뷰를 요청하는 일도 자주 +발생한다. +SIG Docs는 쿠버네티스 조직의 멤버십 상태와 상관없이 보내주는 리뷰와 피드백 또한 환영한다. +풀 리퀘스트에 `/lgtm` 코멘트를 달아서 찬성 의사를 표시할 수 있다. 쿠버네티스 조직의 멤버가 아니라면, +`/lgtm` 코멘트는 자동화 시스템에 유효하지는 않다. + +쿠버네티스 조직의 모든 멤버는 `/hold` 코멘트를 달아서 풀 리퀘스트가 머지되는 것을 막을 수 있다. +또한 모든 멤버가 `/hold` 코멘트를 삭제해서 PR이 머지될 수 있도록 할 수도 있다. 해당 PR이 이미 +적임자로부터 `/lgtm`과 `/approve`를 받은 경우라면 말이다. + +#### 멤버 되기 + +최소 5개의 실질적인 풀 리퀘스트를 성공적으로 제출한 경우, 쿠버네티스 조직의 +[멤버십](https://github.com/kubernetes/community/blob/master/community-membership.md#member)을 +요청할 수 있다. 다음의 단계를 따른다. + +1. 멤버십을 [후원](/docs/contribute/advanced#sponsor-a-new-contributor)해 줄 두 명의 리뷰어 또는 승인자를 + 찾는다. + + [쿠버네티스 Slack 인스턴스의 #sig-docs 채널](https://kubernetes.slack.com) 또는 + [SIG Docs 메일링 리스트](https://groups.google.com/forum/#!forum/kubernetes-sig-docs)에서 + 후원을 요청한다. + + {{< note >}} + SIG Docs 멤버 개인에게 직접 email을 보내거나 Slack 다이렉트 메시지를 보내지 않는다. + {{< /note >}} + +2. `kubernetes/org` 리포지터리에 멤버십을 요청하는 GitHub 이슈를 등록한다. + [커뮤니티 멤버십](https://github.com/kubernetes/community/blob/master/community-membership.md) + 문서의 가이드라인을 따라서 양식을 채운다. + +3. 해당 GitHub 이슈에 후원자를 at-mentioning(`@`을 포함한 코멘트를 추가)하거나 + 링크를 직접 보내주어서 후원자가 해당 GitHub 이슈를 확인하고 `+1` 표를 줄 수 있도록 한다. + +4. 멤버십이 승인되면, 요청에 할당된 GitHub 관리자 팀 멤버가 승인되었음을 업데이트해주고 해당 GitHub 이슈를 종료한다. + 축하한다, 이제 멤버가 되었다! + +어떤 이유에서 멤버십 요청이 즉시 수용되지 않는 경우, 멤버십 위원회에서 재지원 전에 필요한 정보나 단계를 알려준다. + +### 리뷰어 + +리뷰어는 +[@kubernetes/sig-docs-pr-reviews](https://github.com/orgs/kubernetes/teams/sig-docs-pr-reviews) +GitHub 그룹의 멤버이다. [SIG Docs의 팀과 그룹](#teams-and-groups-within-sig-docs) 문서를 참고한다. + +리뷰어는 문서 풀 리퀘스트를 리뷰하고 제안받은 변경에 대한 피드백을 제공한다. + +자동화 시스템은 풀 리퀘스트에 대해 리뷰어를 할당하고, 컨트리뷰터는 해당 풀 리퀘스트에 +`/assign [@_github_handle]` 코멘트를 남겨서 특정 리뷰어에게 리뷰를 요청할 수 있다. +풀 리퀘스트가 기술적으로 정확하고 더 변경이 필요하지 않다는 의미로, 리뷰어는 `/lgtm` 코멘트를 +해당 풀 리퀘스트에 추가할 수 있다. + +할당된 리뷰어가 내용을 아직 리뷰하지 않은 경우, 다른 리뷰어가 나설 수 있다. 추가로, 기술 리뷰어를 +할당해서 그들이 `/lgtm`을 주기를 기다릴 수도 있다. + +사소한 변경이나 기술적 리뷰가 필요한 PR의 경우, SIG Docs [승인자](#승인자)가 `/lgtm`을 줄 +수도 있다. + +리뷰어의 `/approve` 코멘트는 자동화 시스템에서 무시된다. + +SIG Docs 리뷰어가 되는 방법과 수반되는 책임과 시간 할애에 대한 보다 많은 정보는 +[리뷰어나 승인자 되기](#리뷰어나-승인자-되기) 문서를 참조한다. + +#### 리뷰어 되기 + +[요건](https://github.com/kubernetes/community/blob/master/community-membership.md#reviewer)을 +충족하면, SIG Docs 리뷰어가 될 수 있다. 다른 SIG의 리뷰어는 SIG Docs의 리뷰어 자격에 +반드시 별도로 지원해야 한다. + +지원하려면, `kubernetes/website` 저장소의 +[최상위 OWNERS 파일](https://github.com/kubernetes/website/blob/master/OWNERS) +내 `reviewers` 섹션에 자신을 추가하는 풀 리퀘스트를 연다. PR을 한 명 이상의 현재 SIG Docs +승인자에게 할당한다. + +풀 리퀘스트가 승인되면, 이제 SIG Docs 리뷰어가 된다. +[K8s-ci-robot](https://github.com/kubernetes/test-infra/tree/master/prow#bots-home)이 +새로운 풀 리퀘스트에 대한 리뷰어로 당신을 추천하게 된다. + +일단 승인되면, 현재 SIG Docs 승인자가 +[@kubernetes/sig-docs-pr-reviews](https://github.com/orgs/kubernetes/teams/sig-docs-pr-reviews) +GitGub 그룹에 당신을 추가하기를 요청한다. `kubernetes-website-admins` GitHub 그룹의 +멤버만이 신규 멤버를 GitHub 그룹에 추가할 수 있다. + +### 승인자 + +승인자는 +[@kubernetes/sig-docs-maintainers](https://github.com/orgs/kubernetes/teams/sig-docs-maintainers) +GitHub 그룹의 멤버이다. [SIG Docs의 팀과 그룹](#teams-and-groups-within-sig-docs) 문서를 참조한다. + +승인자는 PR을 머지할 수 있으므로, 쿠버네티스 웹사이트에 콘텐츠를 게재할 수 있다. +PR을 승인하려면, 승인자는 `/approve` 코멘트를 해당 PR에 남긴다. 승인자가 아닌 누군가가 승인 +코멘트를 남기더라도, 자동화 시스템은 이를 무시한다. + +PR이 이미 `/lgtm`을 받았거나, 승인자가 `/lgtm`을 포함한 코멘트를 남긴 경우에는 +해당 PR이 자동으로 머지된다. SIG Docs 승인자는 추가적인 기술 리뷰가 필요하지 않은 변경에 대해서만 +`/lgtm`을 남겨야한다. + +SIG Docs 승인자가 되는 방법과 수반되는 책임과 시간 할애에 대한 보다 많은 정보는 +[리뷰어나 승인자 되기](#리뷰어나-승인자-되기) 문서를 참조한다. + +#### 승인자 되기 + +[요건](https://github.com/kubernetes/community/blob/master/community-membership.md#approver)을 +충족하면, SIG Docs 승인자가 될 수 있다. 다른 SIG의 승인자는 SIG Docs의 승인자 자격에 +반드시 별도로 지원해야 한다. + +지원하려면, `kubernetes/website` 저장소의 +[최상위 OWNERS 파일](https://github.com/kubernetes/website/blob/master/OWNERS) +내 `approvers` 섹션에 자신을 추가하는 풀 리퀘스트를 연다. PR을 한 명 이상의 현재 SIG Docs +승인자에게 할당한다. + +풀 리퀘스트가 승인되면, 이제 SIG Docs 승인자가 된다. +[K8s-ci-robot](https://github.com/kubernetes/test-infra/tree/master/prow#bots-home)이 +새로운 풀 리퀘스트에 대한 리뷰어로 당신을 추천하게 된다. + +일단 승인되면, 현재 SIG Docs 승인자가 +[@kubernetes/sig-docs-maintainers](https://github.com/orgs/kubernetes/teams/sig-docs-maintainers) +GitGub 그룹에 당신을 추가하기를 요청한다. `kubernetes-website-admins` GitHub 그룹의 +멤버만이 신규 멤버를 GitHub 그룹에 추가할 수 있다. + +#### 웹사이트 관리자 되기 + +`kubernetes-website-admins` GitHub 그룹의 멤버는 GitHub 그룹의 멤버십을 관리할 수 있고 +리포지터리를 세팅하거나 웹훅(webhook)을 추가, 삭제하고 트러블슈팅하는 것을 포함한 모든 관리 권한을 +가질 수 있다. 모든 SIG Docs 승인자가 이 수준의 액세스를 할 필요는 없다. + +만약 이 수준의 접근 권한이 필요하다면, 현재 웹사이트 관리자나 +[쿠버네티스 Slack](https://kubernetes.slack.com) #sig-docs 채널에서 말한다. + +#### PR Wrangler + +SIG Docs 승인자는 +[PR Wrangler 로테이션 스케줄러](https://github.com/kubernetes/website/wiki/PR-Wranglers)에 +올라서 주 단위로 돌아가며 역할을 수행한다. 모든 SIG Docs 승인자는 이 로테이션에 참여하게 된다. 보다 자세한 내용은 +[일주일 간 PR Wrangler 되기](/docs/contribute/advanced#be-the-pr-wrangler-for-a-week) +문서를 참고한다. + +#### SIG Docs chairperson + +SIG Docs를 포함한 각 SIG는, 한 명 이상의 SIG 멤버가 의장 역할을 하도록 선정한다. 이들은 SIG Docs와 +다른 쿠버네티스 조직 간 연락책(point of contact)이 된다. 이들은 쿠버네티스 프로젝트 전반의 조직과 +그 안에서 SIG Docs가 어떻게 운영되는지에 대한 폭넓은 지식을 갖추어야한다. 현재 의장의 목록을 확인하려면 +[리더십](https://github.com/kubernetes/community/tree/master/sig-docs#leadership) +문서를 참조한다. + +## SIG Docs 팀과 자동화 + +SIG Docs의 자동화는 다음의 두 가지 자동화 메커니즘에 의존한다. +GitHub 그룹과 OWNERS 파일이다. + +### GitHub 그룹 + +GitHub의 SIG Docs 그룹은 두 팀을 정의한다. + + - [@kubernetes/sig-docs-maintainers](https://github.com/orgs/kubernetes/teams/sig-docs-maintainers) + - [@kubernetes/sig-docs-pr-reviews](https://github.com/orgs/kubernetes/teams/sig-docs-pr-reviews) + +그룹의 전원과 의사소통하기 위해서 각각 GitHub 코멘트에서 그룹의 `@name`으로 참조할 수 있다. + +이 팀은 중복되지만, 정확히 일치하지는 않으며, 이 그룹은 자동화 툴에서 사용된다. +이슈, 풀 리퀘스트를 할당하고, PR 승인을 지원하기 위해서 자동화 시스템이 OWNERS 파일의 정보를 활용한다. + +### OWNERS 파일과 전문(front-matter) + +쿠버네티스 프로젝트는 GitHub 이슈와 풀 리퀘스트 자동화와 관련해서 prow라고 부르는 자동화 툴을 사용한다. +[쿠버네티스 웹사이트 리포지터리](https://github.com/kubernetes/website)는 다음의 두 +[prow 플러그인](https://github.com/kubernetes/test-infra/blob/master/prow/plugins.yaml#L210)을 +사용한다. + +- blunderbuss +- approve + +이 두 플러그인은 `kubernetes/website` GitHub 리포지터리 최상위 수준에 있는 +[OWNERS](https://github.com/kubernetes/website/blob/master/OWNERS)와 +[OWNERS_ALIASES](https://github.com/kubernetes/website/blob/master/OWNERS_ALIASES) +파일을 사용해서 해당 리포지터리에 대해 prow가 작동하는 방식을 제어한다. + +OWNERS 파일은 SIG Docs 리뷰어와 승인자의 목록을 포함한다. OWNERS 파일은 하위 디렉터리에 있을 수 +있고, 해당 하위 디렉터리와 그 이하의 파일에 대해 리뷰어와 승인자 역할을 수행할 사람을 새로 지정할 수 있다. +일반적인 OWNERS 파일에 대한 보다 많은 정보는 +[OWNERS](https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md) +문서를 참고한다. + +추가로, 개별 마크다운(Markdown) 파일 내 전문에 리뷰어와 승인자를 개별 GitHub 사용자 이름이나 GitHub +그룹으로 열거할 수 있다. + +OWNERS 파일과 마크다운 파일 내 전문의 조합은 자동화 시스템이 누구에게 기술적, 편집적 리뷰를 요청해야 할지를 +PR 소유자에게 조언하는데 활용된다. + +{{% /capture %}} + +{{% capture whatsnext %}} + +쿠버네티스 문서화에 기여하는 일에 대한 보다 많은 정보는 다음 문서를 참고한다. + +- [기여 시작하기](/docs/contribute/start/) +- [문서 스타일](/docs/contribute/style/) + +{{% /capture %}} + + diff --git a/content/ko/docs/reference/_index.md b/content/ko/docs/reference/_index.md index 98556ec6c963d..4ad5e4a59047c 100644 --- a/content/ko/docs/reference/_index.md +++ b/content/ko/docs/reference/_index.md @@ -16,29 +16,29 @@ content_template: templates/concept ## API 레퍼런스 -* [Kubernetes API Overview](/docs/reference/using-api/api-overview/) - 쿠버네티스 API에 대한 개요 +* [쿠버네티스 API 개요](/docs/reference/using-api/api-overview/) - 쿠버네티스 API에 대한 개요 * 쿠버네티스 API 버전 + * [1.14](/docs/reference/generated/kubernetes-api/v1.14/) * [1.13](/docs/reference/generated/kubernetes-api/v1.13/) * [1.12](/docs/reference/generated/kubernetes-api/v1.12/) * [1.11](/docs/reference/generated/kubernetes-api/v1.11/) - * [1.10](https://v1-10.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/) - * [1.9](https://v1-9.docs.kubernetes.io/docs/api-reference/v1.9/) + * [1.10](/docs/reference/generated/kubernetes-api/v1.10/) ## API 클라이언트 라이브러리 프로그래밍 언어에서 쿠버네티스 API를 호출하기 위해서, -[client libraries](/docs/reference/using-api/client-libraries/)를 사용할 수 있다. +[클라이언트 라이브러리](/docs/reference/using-api/client-libraries/)를 사용할 수 있다. 공식적으로 지원되는 클라이언트 라이브러리는 다음과 같다. -- [Kubernetes Go client library](https://github.com/kubernetes/client-go/) -- [Kubernetes Python client library](https://github.com/kubernetes-client/python) -- [Kubernetes Java client library](https://github.com/kubernetes-client/java) -- [Kubernetes JavaScript client library](https://github.com/kubernetes-client/javascript) +- [쿠버네티스 Go 클라이언트 라이브러리](https://github.com/kubernetes/client-go/) +- [쿠버네티스 Python 클라이언트 라이브러리](https://github.com/kubernetes-client/python) +- [쿠버네티스 Java 클라이언트 라이브러리](https://github.com/kubernetes-client/java) +- [쿠버네티스 JavaScript 클라이언트 라이브러리](https://github.com/kubernetes-client/javascript) ## CLI 레퍼런스 * [kubectl](/docs/user-guide/kubectl-overview) - 명령어를 실행하거나 쿠버네티스 클러스터를 관리하기 위해 사용하는 주된 CLI 도구. - * [JSONPath](/docs/user-guide/jsonpath/) - kubectl에서 [JSONPath expressions](http://goessner.net/articles/JsonPath/)을 사용하기 위한 문법 가이드. + * [JSONPath](/docs/user-guide/jsonpath/) - kubectl에서 [JSONPath 표현](http://goessner.net/articles/JsonPath/)을 사용하기 위한 문법 가이드. * [kubeadm](/docs/admin/kubeadm/) - 안정적인 쿠버네티스 클러스터를 쉽게 프로비전하기 위한 CLI 도구. * [kubefed](/docs/admin/kubefed/) - 연합된(federated) 클러스터 관리를 도와주는 CLI 도구. @@ -54,6 +54,6 @@ content_template: templates/concept ## 설계 문서 -쿠버네티스 기능에 대한 설계 문서의 아카이브. [Kubernetes Architecture](https://git.k8s.io/community/contributors/design-proposals/architecture/architecture.md)와 [Kubernetes Design Overview](https://git.k8s.io/community/contributors/design-proposals)가 좋은 출발점이다. +쿠버네티스 기능에 대한 설계 문서의 아카이브. [쿠버네티스 아키텍처](https://git.k8s.io/community/contributors/design-proposals/architecture/architecture.md)와 [쿠버네티스 디자인 개요](https://git.k8s.io/community/contributors/design-proposals)가 좋은 출발점이다. {{% /capture %}} diff --git a/content/ko/docs/reference/glossary/taint.md b/content/ko/docs/reference/glossary/taint.md index fe8119cea754f..66ec24d28de29 100644 --- a/content/ko/docs/reference/glossary/taint.md +++ b/content/ko/docs/reference/glossary/taint.md @@ -4,14 +4,14 @@ id: taint date: 2019-01-11 full_link: /docs/concepts/configuration/taint-and-toleration/ short_description: > - 파드가 노드나 노드 그룹에 스케줄링되는 것을 방지하는 키-값 쌍 및 효과이다. + 세 가지 필수 속성: 키(key), 값(value), 효과(effect)로 구성된 코어 오브젝트. 테인트는 파드가 노드나 노드 그룹에 스케줄링되는 것을 방지한다. aka: tags: - core-object - fundamental --- - 파드가 노드나 노드 그룹에 스케줄링되는 것을 방지하는 키-값 쌍 및 효과이다. + 세 가지 필수 속성: 키(key), 값(value), 효과(effect)로 구성된 코어 오브젝트. 테인트는 파드가 노드나 노드 그룹에 스케줄링되는 것을 방지한다. diff --git a/content/ko/docs/reference/glossary/toleration.md b/content/ko/docs/reference/glossary/toleration.md index 8778d28b1e398..469a86dd998ca 100644 --- a/content/ko/docs/reference/glossary/toleration.md +++ b/content/ko/docs/reference/glossary/toleration.md @@ -4,15 +4,15 @@ id: toleration date: 2019-01-11 full_link: /docs/concepts/configuration/taint-and-toleration/ short_description: > - 매칭되는 테인트(taint)를 가진 노드나 노드 그룹에 파드가 스케줄링되는 것을 활성화하는 키-값 쌍 및 효과이다. + 세 가지 필수 속성: 키(key), 값(value), 효과(effect)로 구성된 코어 오브젝트. 톨러레이션은 매칭되는 테인트(taint)를 가진 노드나 노드 그룹에 파드가 스케줄링되는 것을 활성화한다. aka: tags: - core-object - fundamental --- - 매칭되는 {{< glossary_tooltip text="테인트(taint)" term_id="taint" >}}를 가진 노드나 노드 그룹에 파드가 스케줄링되는 것을 활성화하는 키-값 쌍 및 효과이다. + 세 가지 필수 속성: 키(key), 값(value), 효과(effect)로 구성된 코어 오브젝트. 톨러레이션은 매칭되는 {{< glossary_tooltip text="테인트(taints)" term_id="taint" >}}를 가진 노드나 노드 그룹에 파드가 스케줄링되는 것을 활성화한다. -톨러레이션 및 {{< glossary_tooltip text="테인트" term_id="taint" >}}는 함께 작동하며, 파드가 적절하지 못한 노드에 스케줄되는 것을 방지한다. 하나 이상의 톨러레이션이 {{< glossary_tooltip text="파드" term_id="pod" >}}에 적용될 수 있으며, 이것은 매칭되는 {{< glossary_tooltip text="테인트" term_id="taint" >}}를 가진 노드나 노드 그룹에 파드가 스케줄링되는 것을 허용(그러나 필수는 아님)하도록 표시한다. +톨러레이션 및 {{< glossary_tooltip text="테인트" term_id="taint" >}}는 함께 작동하며, 파드가 적절하지 못한 노드에 스케줄되는 것을 방지한다. 하나 이상의 톨러레이션이 {{< glossary_tooltip text="파드" term_id="pod" >}}에 적용될 수 있다. 톨러레이션은 매칭되는 {{< glossary_tooltip text="테인트" term_id="taint" >}}를 가진 노드나 노드 그룹에 {{< glossary_tooltip text="파드" term_id="pod" >}}가 스케줄링되는 것을 허용(그러나 필수는 아님)하도록 표시한다. diff --git a/content/ko/docs/reference/kubectl/cheatsheet.md b/content/ko/docs/reference/kubectl/cheatsheet.md index e536b026a25a8..44750f19047a3 100644 --- a/content/ko/docs/reference/kubectl/cheatsheet.md +++ b/content/ko/docs/reference/kubectl/cheatsheet.md @@ -62,12 +62,16 @@ KUBECONFIG=~/.kube/config:~/.kube/kubconfig2 kubectl config view kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}' kubectl config view -o jsonpath='{.users[].name}' # 사용자 리스트 조회 -kubectl config current-context # 현재 컨텍스트 확인 +kubectl config get-contexts # 컨텍스트 리스트 출력 +kubectl config current-context # 현재 컨텍스트 출력 kubectl config use-context my-cluster-name # my-cluster-name를 기본 컨텍스트로 설정 # 기본 인증을 지원하는 새로운 클러스터를 kubeconf에 추가한다 kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword +# 해당 컨텍스트에서 모든 후속 kubectl 커맨드에 대한 네임스페이스를 영구적으로 저장한다 +kubectl config set-context --current --namespace=ggckad-s2 + # 특정 사용자와 네임스페이스를 사용하는 컨텍스트 설정 kubectl config set-context gce --user=cluster-admin --namespace=foo \ && kubectl config use-context gce @@ -84,15 +88,15 @@ kubectl config unset users.foo # foo 사용자 삭제 , `.yml`, `.json` 이 사용된다. ```bash -kubectl create -f ./my-manifest.yaml # 리소스(들) 생성 -kubectl create -f ./my1.yaml -f ./my2.yaml # 여러 파일로 부터 생성 -kubectl create -f ./dir # dir 내 모든 매니페스트 파일에서 리소스(들) 생성 -kubectl create -f https://git.io/vPieo # url로부터 리소스(들) 생성 +kubectl apply -f ./my-manifest.yaml # 리소스(들) 생성 +kubectl apply -f ./my1.yaml -f ./my2.yaml # 여러 파일로 부터 생성 +kubectl apply -f ./dir # dir 내 모든 매니페스트 파일에서 리소스(들) 생성 +kubectl apply -f https://git.io/vPieo # url로부터 리소스(들) 생성 kubectl create deployment nginx --image=nginx # nginx 단일 인스턴스를 시작 kubectl explain pods,svc # 파드와 서비스 매니페스트 문서를 조회 # stdin으로 다수의 YAML 오브젝트 생성 -cat <}} 컨테이너를 실행할 때 runc가 시스템 파일 디스크립터를 처리하는 방식에서 결함이 발견되었다. 악성 컨테이너는 이 결함을 사용하여 runc 바이너리의 내용을 덮어쓸 수 있으며 따라서 컨테이너 호스트 시스템에서 임의의 명령을 실행할 수 있다. -이 문제에 대한 자세한 내용은 +이 문제에 대한 자세한 내용은 [cve-2019-5736 : runc 취약점 ] (https://access.redhat.com/security/cve/cve-2019-5736) 참고하자. {{< /caution >}} @@ -25,7 +26,7 @@ weight: 100 {{< note >}} 이 문서는 Linux에 CRI를 설치하는 사용자를 위해 작성되었다. -다른 운영 체제의 경우, 해당 플랫폼과 관련된 문서를 찾아보자. +다른 운영 체제의 경우, 해당 플랫폼과 관련된 문서를 찾아보자. {{< /note >}} 이 가이드의 모든 명령은 `root`로 실행해야 한다. @@ -61,23 +62,20 @@ Control group은 프로세스에 할당된 리소스를 제한하는데 사용 {{< tabs name="tab-cri-docker-installation" >}} {{< tab name="Ubuntu 16.04" codelang="bash" >}} # Docker CE 설치 -## 저장소 설정 -### apt 패키지 인덱스 업데이트 - apt-get update - -### apt가 HTTPS 저장소를 사용할 수 있도록 해주는 패키지 설치 - apt-get update && apt-get install apt-transport-https ca-certificates curl software-properties-common +## 리포지터리 설정 +### apt가 HTTPS 리포지터리를 사용할 수 있도록 해주는 패키지 설치 +apt-get update && apt-get install apt-transport-https ca-certificates curl software-properties-common ### Docker의 공식 GPG 키 추가 - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - -### Docker apt 저장소 추가. - add-apt-repository \ - "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ - $(lsb_release -cs) \ - stable" +### Docker apt 리포지터리 추가. +add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" -## Docker ce 설치. +## Docker CE 설치. apt-get update && apt-get install docker-ce=18.06.2~ce~3-0~ubuntu # 데몬 설정. @@ -101,19 +99,19 @@ systemctl restart docker {{< tab name="CentOS/RHEL 7.4+" codelang="bash" >}} # Docker CE 설치 -## 저장소 설정 +## 리포지터리 설정 ### 필요한 패키지 설치. - yum install yum-utils device-mapper-persistent-data lvm2 +yum install yum-utils device-mapper-persistent-data lvm2 -### Docker 저장소 추가 +### Docker 리포지터리 추가 yum-config-manager \ - --add-repo \ - https://download.docker.com/linux/centos/docker-ce.repo + --add-repo \ + https://download.docker.com/linux/centos/docker-ce.repo -## Docker ce 설치. +## Docker CE 설치. yum update && yum install docker-ce-18.06.2.ce -## /etc/docker 디렉토리 생성. +## /etc/docker 디렉터리 생성. mkdir /etc/docker # 데몬 설정. @@ -220,36 +218,63 @@ EOF sysctl --system ``` +### containerd 설치 + {{< tabs name="tab-cri-containerd-installation" >}} -{{< tab name="Ubuntu 16.04+" codelang="bash" >}} -apt-get install -y libseccomp2 +{{< tab name="Ubuntu 16.04" codelang="bash" >}} +# containerd 설치 +## 리포지터리 설정 +### apt가 HTTPS로 리포지터리를 사용하는 것을 허용하기 위한 패키지 설치 +apt-get update && apt-get install -y apt-transport-https ca-certificates curl software-properties-common + +### Docker의 공식 GPG 키 추가 +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - + +### Docker apt 리포지터리 추가. +add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" + +## containerd 설치 +apt-get update && apt-get install -y containerd.io + +# containerd 설정 +mkdir -p /etc/containerd +containerd config default > /etc/containerd/config.toml + +# containerd 재시작 +systemctl restart containerd {{< /tab >}} {{< tab name="CentOS/RHEL 7.4+" codelang="bash" >}} -yum install -y libseccomp -{{< /tab >}} -{{< /tabs >}} - -### Containerd 설치 +# containerd 설치 +## 리포지터리 설정 +### 필요한 패키지 설치 +yum install yum-utils device-mapper-persistent-data lvm2 -[Containerd 릴리스](https://github.com/containerd/containerd/releases)는 주기적으로 출판된다. 아래의 값들은 작성 당시에 가용한 최신 버전을 기준으로 하드코드 되었다. 새로운 버전과 해시는 [여기](https://storage.googleapis.com/cri-containerd-release)에서 참고한다. +### Docker 리포지터리 추가리 +yum-config-manager \ + --add-repo \ + https://download.docker.com/linux/centos/docker-ce.repo -```shell -# 요구되는 환경 변수 export. -export CONTAINERD_VERSION="1.1.2" -export CONTAINERD_SHA256="d4ed54891e90a5d1a45e3e96464e2e8a4770cd380c21285ef5c9895c40549218" +## containerd 설치 +yum update && yum install containerd.io -# containerd tar 다운로드. -wget https://storage.googleapis.com/cri-containerd-release/cri-containerd-${CONTAINERD_VERSION}.linux-amd64.tar.gz +# containerd 설정 +mkdir -p /etc/containerd +containerd config default > /etc/containerd/config.toml -# 해시 확인. -echo "${CONTAINERD_SHA256} cri-containerd-${CONTAINERD_VERSION}.linux-amd64.tar.gz" | sha256sum --check - +# containerd 재시작 +systemctl restart containerd +{{< /tab >}} +{{< /tabs >}} -# 풀기. -tar --no-overwrite-dir -C / -xzf cri-containerd-${CONTAINERD_VERSION}.linux-amd64.tar.gz +### systemd -# containerd 시작. -systemctl start containerd -``` +`systemd` cgroup driver를 사용하려면, `/etc/containerd/config.toml`의 `plugins.cri.systemd_cgroup = true`을 설정한다. +kubeadm을 사용하는 경우에도 마찬가지로, 수동으로 +[cgroup driver for kubelet](/docs/setup/independent/install-kubeadm/#configure-cgroup-driver-used-by-kubelet-on-master-node)을 +설정해준다. ## 다른 CRI 런타임: frakti diff --git a/content/ko/docs/setup/multiple-zones.md b/content/ko/docs/setup/multiple-zones.md index 679a55f5efc94..1ba33c20ca108 100644 --- a/content/ko/docs/setup/multiple-zones.md +++ b/content/ko/docs/setup/multiple-zones.md @@ -185,7 +185,7 @@ kubernetes-minion-wf8i Ready 2m v1.13.0 Create a volume using the dynamic volume creation (only PersistentVolumes are supported for zone affinity): ```json -kubectl create -f - <}} -정적 바이너리를 내려받아서 리눅스에 Minikube를 설치할 수 있따. +정적 바이너리를 내려받아서 리눅스에 Minikube를 설치할 수 있다. ```shell curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \ diff --git a/content/ko/docs/tutorials/configuration/configure-redis-using-configmap.md b/content/ko/docs/tutorials/configuration/configure-redis-using-configmap.md index 1e630cd8d2089..66dfa237d6ac5 100644 --- a/content/ko/docs/tutorials/configuration/configure-redis-using-configmap.md +++ b/content/ko/docs/tutorials/configuration/configure-redis-using-configmap.md @@ -11,16 +11,18 @@ content_template: templates/tutorial {{% capture objectives %}} -* 컨피그 맵을 생성한다. -* 컨피그 맵을 사용해서 파드 명세를 생성한다. -* 파드를 생성한다. -* 설정이 올바르게 적용되었는지 검증한다. +* 다음을 포함하는 `kustomization.yaml` 파일을 생성한다. + * 컨피그 맵 생성자 + * 컨피그 맵을 사용하는 파드 리소스 +* `kubectl apply -k ./`를 실행하여 작업한 디렉토리를 적용한다. +* 구성이 잘 적용되었는지 확인한다. {{% /capture %}} {{% capture prerequisites %}} * {{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} +* 예시는 `kubectl` 1.14 이상 버전에서 동작한다. * [컨피그 맵을 사용해서 컨테이너 설정하기](/docs/tasks/configure-pod-container/configure-pod-configmap/)를 이해한다. {{% /capture %}} @@ -32,49 +34,48 @@ content_template: templates/tutorial 아래의 단계를 통해서 컨피그 맵에 저장된 데이터를 사용해서 Redis 캐시를 설정할 수 있다. -첫째, `redis-config` 파일에서 컨피그 맵을 생성한다. +첫째, `redis-config` 파일에서 컨피그 맵을 포함한 `kustomization.yaml`를 생성한다. {{< codenew file="pods/config/redis-config" >}} ```shell curl -OL https://k8s.io/examples/pods/config/redis-config -kubectl create configmap example-redis-config --from-file=redis-config -``` -```shell -configmap/example-redis-config created +cat <./kustomization.yaml +configMapGenerator: +- name: example-redis-config + files: + - redis-config +EOF ``` -생성된 컨피그 맵을 점검한다. +`kustomization.yaml`에 파드 리소스 구성을 추가한다. + +{{< codenew file="pods/config/redis-pod.yaml" >}} ```shell -kubectl get configmap example-redis-config -o yaml -``` +curl -OL https://k8s.io/examples/pods/config/redis-pod.yaml -```yaml -apiVersion: v1 -data: - redis-config: | - maxmemory 2mb - maxmemory-policy allkeys-lru -kind: ConfigMap -metadata: - creationTimestamp: 2016-03-30T18:14:41Z - name: example-redis-config - namespace: default - resourceVersion: "24686" - selfLink: /api/v1/namespaces/default/configmaps/example-redis-config - uid: 460a2b6e-f6a3-11e5-8ae5-42010af00002 +cat <>./kustomization.yaml +resources: +- redis-pod.yaml +EOF ``` -이제, 컨피그 맵에 저장된 설정 데이터를 사용하는 파드 명세를 생성한다. +컨피그 맵과 파드 개체를 생성하도록 kustomization 디렉토리를 적용한다. -{{< codenew file="pods/config/redis-pod.yaml" >}} - -파드를 생성한다. +```shell +kubectl apply -k . +``` +생성된 오브젝트를 확인한다. ```shell -kubectl create -f https://k8s.io/examples/pods/config/redis-pod.yaml +> kubectl get -k . +NAME DATA AGE +configmap/example-redis-config-dgh9dg555m 1 52s + +NAME READY STATUS RESTARTS AGE +pod/redis 1/1 Running 0 52s ``` 이 예제에서는 설정 볼륨이 `/redis-master`에 마운트되어 있다. @@ -82,7 +83,8 @@ kubectl create -f https://k8s.io/examples/pods/config/redis-pod.yaml 따라서, Redis 설정을 위한 파일 경로는 `/redis-master/redis.conf`이다. 이곳이 이미지가 Redis 마스터를 위한 설정 파일을 찾는 곳이다. -설정이 올바르게 적용되었는지 확인하기 위해서, `kubectl exec`를 사용해 파드 속에서 `redis-cli` 툴을 실행해 본다. +설정이 올바르게 적용되었는지 확인하기 위해서, +`kubectl exec`를 사용해 파드 속에서 `redis-cli` 툴을 실행해 본다. ```shell kubectl exec -it redis redis-cli diff --git a/content/ko/docs/tutorials/services/_index.md b/content/ko/docs/tutorials/services/_index.md new file mode 100644 index 0000000000000..071972c8d6442 --- /dev/null +++ b/content/ko/docs/tutorials/services/_index.md @@ -0,0 +1,5 @@ +--- +title: "서비스" +weight: 70 +--- + diff --git a/content/ko/docs/tutorials/services/source-ip.md b/content/ko/docs/tutorials/services/source-ip.md new file mode 100644 index 0000000000000..a6377c6c1a325 --- /dev/null +++ b/content/ko/docs/tutorials/services/source-ip.md @@ -0,0 +1,342 @@ +--- +title: 소스 IP 주소 이용하기 +content_template: templates/tutorial +--- + +{{% capture overview %}} + +쿠버네티스 클러스터에서 실행 중인 애플리케이션은 서로 간에 외부 세계와 +서비스 추상화를 통해 찾고 통신한다. 이 문서는 +다른 종류의 서비스로 보내진 패킷의 소스 IP 주소에 어떤 일이 벌어지는지와 +이 동작을 요구에 따라 토글할 수 있는지 설명한다. + +{{% /capture %}} + +{{% capture prerequisites %}} + +{{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} + +## 용어 + +이 문서는 다음 용어를 사용한다. + +* [NAT](https://en.wikipedia.org/wiki/Network_address_translation): 네트워크 주소 변환 +* [소스 NAT](https://en.wikipedia.org/wiki/Network_address_translation#SNAT): 패킷 상의 소스 IP 주소를 변경함, 보통 노드의 IP 주소 +* [대상 NAT](https://en.wikipedia.org/wiki/Network_address_translation#DNAT): 패킷 상의 대상 IP 주소를 변경함, 보통 파드의 IP 주소 +* [VIP](/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies): 가상 IP 주소, 모든 쿠버네티스 서비스에 할당된 것 같은 +* [Kube-proxy](/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies): 네트워크 데몬으로 모든 노드에서 서비스 VIP 관리를 관리한다. + + +## 전제 조건 + +이 문서의 예시를 실행하기 위해서 쿠버네티스 1.5 이상의 동작하는 클러스터가 필요하다. +이 예시는 HTTP 헤더로 수신한 요청의 소스 IP 주소를 회신하는 +작은 nginx 웹 서버를 이용한다. 다음과 같이 생성할 수 있다. + +```console +$ kubectl run source-ip-app --image=k8s.gcr.io/echoserver:1.4 +deployment.apps/source-ip-app created +``` + +{{% /capture %}} + +{{% capture objectives %}} + +* 간단한 애플리케이션을 다양한 서비스 종류로 노출하기 +* 각 서비스 유형에 따른 소스 IP NAT 의 동작 이해하기 +* 소스 IP 주소 보존에 관한 절충 사항 이해 + +{{% /capture %}} + + +{{% capture lessoncontent %}} + +## Type=ClusterIP인 서비스에서 소스 IP + +쿠버네티스 1.2부터 기본으로 제공하는 +[iptables 모드](/docs/concepts/services-networking/service/#proxy-mode-iptables)로 운영하는 경우 +클러스터 내에서 클러스터 IP로 패킷을 보내면 소스 NAT를 통과하지 않는다. +Kube-proxy는 이 모드를 `proxyMode` 엔드포인트를 통해 노출한다. + +```console +$ kubectl get nodes +NAME STATUS ROLES AGE VERSION +kubernetes-minion-group-6jst Ready 2h v1.13.0 +kubernetes-minion-group-cx31 Ready 2h v1.13.0 +kubernetes-minion-group-jj1t Ready 2h v1.13.0 + +kubernetes-minion-group-6jst $ curl localhost:10249/proxyMode +iptables +``` + +소스 IP 애플리케이션을 통해 서비스를 생성하여 소스 IP 주소 보존 여부를 테스트할 수 있다. + +```console +$ kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080 +service/clusterip exposed + +$ kubectl get svc clusterip +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +clusterip ClusterIP 10.0.170.92 80/TCP 51s +``` + +그리고 동일한 클러스터의 파드에서 `클러스터IP`를 치면: + +```console +$ kubectl run busybox -it --image=busybox --restart=Never --rm +Waiting for pod default/busybox to be running, status is Pending, pod ready: false +If you don't see a command prompt, try pressing enter. + +# ip addr +1: lo: mtu 65536 qdisc noqueue + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +3: eth0: mtu 1460 qdisc noqueue + link/ether 0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff + inet 10.244.3.8/24 scope global eth0 + valid_lft forever preferred_lft forever + inet6 fe80::188a:84ff:feb0:26a5/64 scope link + valid_lft forever preferred_lft forever + +# wget -qO - 10.0.170.92 +CLIENT VALUES: +client_address=10.244.3.8 +command=GET +... +``` +client_address는 클라이언트 파드와 서버 파드가 같은 노드 또는 다른 노드에 있는지 여부에 관계없이 항상 클라이언트 파드의 IP 주소이다. + +## Type=NodePort인 서비스에서 소스 IP + +쿠버네티스 1.5부터 [Type=NodePort](/docs/concepts/services-networking/service/#nodeport)인 서비스로 보내진 패킷은 +소스 NAT가 기본으로 적용된다. `NodePort` 서비스를 생성하여 이것을 테스트할 수 있다. + +```console +$ kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort +service/nodeport exposed + +$ NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport) +$ NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="ExternalIP")].address }') +``` + +클라우드 공급자 상에서 실행한다면, +위에 보고된 `nodes:nodeport`를 위한 방화벽 규칙을 열어주어야 한다. +이제 위에 노드 포트로 할당받은 포트를 통해 클러스터 외부에서 +서비스에 도달할 수 있다. + +```console +$ for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done +client_address=10.180.1.1 +client_address=10.240.0.5 +client_address=10.240.0.3 +``` + +명심할 것은 정확한 클라이언트 IP 주소가 아니고, 클러스터 내부 IP 주소이다. 왜 이런 일이 발생했는지 설명한다. + +* 클라이언트는 `node2:nodePort`로 패킷을 보낸다. +* `node2`는 소스 IP 주소(SNAT)를 패킷 상에서 자신의 IP 주소로 교체한다. +* `noee2`는 대상 IP를 패킷 상에서 파드의 IP로 교체한다. +* 패킷은 node 1로 라우팅 된 다음 엔드포인트로 라우팅 된다. +* 파드의 응답은 node2로 다시 라우팅된다. +* 파드의 응답은 클라이언트로 다시 전송된다. + +시각적으로 + +``` + client + \ ^ + \ \ + v \ + node 1 <--- node 2 + | ^ SNAT + | | ---> + v | + endpoint +``` + + +이를 피하기 위해 쿠버네티스는 클라이언트 소스 IP 주소를 보존하는 기능이 있다. +[(기능별 가용성은 여기에)](/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip). +`service.spec.externalTrafficPolicy`을 `Local`로 하면 +오직 로컬 엔드포인트로만 프록시 요청하고 다른 노드로 트래픽 전달하지 않으므로, +원본 소스 IP 주소를 보존한다. +만약 로컬 엔드 포인트가 없다면, 그 노드로 보내진 패킷은 버려지므로 +패킷 처리 규칙에서 정확한 소스 IP 임을 신뢰할 수 있으므로, +패킷을 엔드포인트까지 전달할 수 있다. + +다음과 같이 `service.spec.externalTrafficPolicy` 필드를 설정하자. + +```console +$ kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}' +service/nodeport patched +``` + +이제 다시 테스트를 실행해보자. + +```console +$ for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done +client_address=104.132.1.79 +``` + +엔드포인트 파드가 실행 중인 노드에서 *올바른* 클라이언트 IP 주소인 +딱 한 종류의 응답만 수신한다. + +어떻게 이렇게 되었는가: + +* 클라이언트는 패킷을 엔드포인트가 없는 `node2:nodePort` 보낸다. +* 패킷은 버려진다. +* 클라이언트는 패킷을 엔드포인트를 가진 `node1:nodePort` 보낸다. +* node1은 패킷을 올바른 소스 IP 주소로 엔드포인트로 라우팅 한다. + + +시각적으로 + +``` + client + ^ / \ + / / \ + / v X + node 1 node 2 + ^ | + | | + | v + endpoint +``` + + + +## Type=LoadBalancer인 서비스에서 소스 IP + +쿠버네티스 1.5 부터 [Type=LoadBalancer](/docs/concepts/services-networking/service/#loadbalancer)인 서비스로 +보낸 패킷은 소스 NAT를 기본으로 하는데, `Ready` 상태로 모든 스케줄된 모든 쿠버네티스 노드는 +로드 밸런싱 트래픽에 적합하다. 따라서 엔드포인트가 없는 노드에 +패킷이 도착하면 시스템은 엔드포인트를 *포함한* 노드에 프록시를 +수행하고 패킷 상에서 노드의 IP 주소로 소스 IP 주소를 변경한다 +(이전 섹션에서 기술한 것처럼). + +로드밸런서를 통해 source-ip-app을 노출하여 테스트할 수 있다. + +```console +$ kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer +service/loadbalancer exposed + +$ kubectl get svc loadbalancer +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +loadbalancer LoadBalancer 10.0.65.118 104.198.149.140 80/TCP 5m + +$ curl 104.198.149.140 +CLIENT VALUES: +client_address=10.240.0.5 +... +``` + +그러나 구글 클라우드 엔진/GCE 에서 실행 중이라면 동일한 `service.spec.externalTrafficPolicy` 필드를 `Local`로 설정하면 +서비스 엔드포인트가 *없는* 노드는 고의적으로 헬스 체크에 실패하여 +강제로 로드밸런싱 트래픽을 받을 수 있는 노드 목록에서 +자신을 스스로 제거한다. + +시각적으로: + +``` + client + | + lb VIP + / ^ + v / +health check ---> node 1 node 2 <--- health check + 200 <--- ^ | ---> 500 + | V + endpoint +``` + +이것은 어노테이션을 설정하여 테스트할 수 있다. + +```console +$ kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}' +``` + +쿠버네티스에 의해 `service.spec.healthCheckNodePort` 필드가 +즉각적으로 할당되는 것을 봐야 한다. + +```console +$ kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort + healthCheckNodePort: 32122 +``` + +`service.spec.healthCheckNodePort` 필드는 `/healthz`에서 헬스 체크를 제공하는 +모든 노드의 포트를 가르킨다. 이것을 테스트할 수 있다. + +``` +$ kubectl get pod -o wide -l run=source-ip-app +NAME READY STATUS RESTARTS AGE IP NODE +source-ip-app-826191075-qehz4 1/1 Running 0 20h 10.180.1.136 kubernetes-minion-group-6jst + +kubernetes-minion-group-6jst $ curl localhost:32122/healthz +1 Service Endpoints found + +kubernetes-minion-group-jj1t $ curl localhost:32122/healthz +No Service Endpoints Found +``` + +마스터에서 실행 중인 서비스 컨트롤러는 필요시에 클라우드 로드밸런서를 할당할 책임이 있다. +또한, 각 노드에 HTTP 헬스 체크를 이 포트와 경로로 할당한다. +헬스체크가 실패한 엔드포인트를 포함하지 않은 2개 노드에서 10초를 기다리고 +로드밸런서 IP 주소로 curl 하자. + +```console +$ curl 104.198.149.140 +CLIENT VALUES: +client_address=104.132.1.79 +... +``` + +__크로스 플랫폼 지원__ + +쿠버네티스 1.5부터 Type=LoadBalancer 서비스를 통한 +소스 IP 주소 보존을 지원하지만, +이는 클라우드 공급자(GCE, Azure)의 하위 집합으로 구현되어 있다. 실행중인 클라우드 공급자에서 +몇 가지 다른 방법으로 로드밸런서를 요청하자. + +1. 클라이언트 연결을 종료하고 새 연결을 여는 프록시를 이용한다. +이 경우 소스 IP 주소는 클라이언트 IP 주소가 아니고 +항상 클라우드 로드밸런서의 IP 주소이다. + +2. 로드밸런서의 VIP에 전달된 클라이언트가 보낸 요청을 +중간 프록시가 아닌 클라이언트 소스 IP 주소가 있는 노드로 +끝나는 패킷 전달자를 이용한다. + +첫 번째 범주의 로드밸런서는 진짜 클라이언트 IP를 통신하기 위해 +HTTP [X-FORWARDED-FOR](https://en.wikipedia.org/wiki/X-Forwarded-For) 헤더나 +[프록시 프로토콜](http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt)같이 로드밸런서와 +백엔드 간에 합의된 프로토콜을 사용해야 한다. +두 번째 범주의 로드밸런서는 서비스의 `service.spec.healthCheckNodePort` 필드의 저장된 포트를 가르키는 +간단한 HTTP 헬스 체크를 생성하여 +위에서 설명한 기능을 활용할 수 있다. + +{{% /capture %}} + +{{% capture cleanup %}} + +서비스를 삭제하자. + +```console +$ kubectl delete svc -l run=source-ip-app +``` + +디플로이먼트와 리플리카 셋과 파드를 삭제하자. + +```console +$ kubectl delete deployment source-ip-app +``` + +{{% /capture %}} + +{{% capture whatsnext %}} +* [서비스를 통한 애플리케이션 연결하기](/docs/concepts/services-networking/connect-applications-service/)에 대해 더 공부하기 +* [부하분산](/docs/user-guide/load-balancer)에 대해 더 공부하기 +{{% /capture %}} + + diff --git a/content/ko/docs/tutorials/stateful-application/zookeeper.md b/content/ko/docs/tutorials/stateful-application/zookeeper.md new file mode 100644 index 0000000000000..c724afe31cc6c --- /dev/null +++ b/content/ko/docs/tutorials/stateful-application/zookeeper.md @@ -0,0 +1,1095 @@ +--- +title: 분산 시스템 코디네이터 ZooKeeper 실행하기 +content_template: templates/tutorial +weight: 40 +--- + +{{% capture overview %}} +이 튜토리얼은 [아파치 ZooKeeper](https://zookeeper.apache.org) +쿠버네티스에서 [스테이트풀셋](/docs/concepts/workloads/controllers/statefulset/)과 +[파드디스룹선버짓(PodDisruptionBudget)](/docs/concepts/workloads/pods/disruptions/#specifying-a-poddisruptionbudget)과 +[파드안티어피피니티(PodAntiAffinity)](/docs/user-guide/node-selection/#inter-pod-affinity-and-anti-affinity-beta-feature)를 이용한 [Apache Zookeeper](https://zookeeper.apache.org) 실행을 설명한다. +{{% /capture %}} + +{{% capture prerequisites %}} + +이 튜토리얼을 시작하지 전에 +다음 쿠버네티스 개념에 친숙해야 한다. + +- [파드](/docs/user-guide/pods/single-container/) +- [클러스터 DNS](/docs/concepts/services-networking/dns-pod-service/) +- [헤드리스 서비스](/docs/concepts/services-networking/service/#headless-services) +- [퍼시스턴트볼륨](/docs/concepts/storage/volumes/) +- [퍼시스턴트볼륨 프로비저닝](https://github.com/kubernetes/examples/tree/{{< param "githubbranch" >}}/staging/persistent-volume-provisioning/) +- [스테이트풀셋](/docs/concepts/workloads/controllers/statefulset/) +- [파드디스룹션버짓](/docs/concepts/workloads/pods/disruptions/#specifying-a-poddisruptionbudget) +- [파드안티어피니티](/docs/user-guide/node-selection/#inter-pod-affinity-and-anti-affinity-beta-feature) +- [kubectl CLI](/docs/user-guide/kubectl/) + +최소한 4개의 노드가 있는 클러스터가 필요하며, 각 노드는 적어도 2 개의 CPU와 4 GiB 메모리가 필요하다. 이 튜토리얼에서 클러스터 노드를 통제(cordon)하고 비우게(drain) 할 것이다. **이것은 클러스터를 종료하여 노드의 모든 파드를 퇴출(evict)하는 것으로, 모든 파드는 임시적으로 언스케줄된다는 의미이다.** 이 튜토리얼을 위해 전용 클러스터를 이용하거나, 다른 테넌트에 간섭을 주는 혼란이 발생하지 않도록 해야합니다. + +이 튜토리얼은 클러스터가 동적으로 퍼시스턴트볼륨을 프로비저닝하도록 구성한다고 가정한다. +그렇게 설정되어 있지 않다면 +튜토리얼을 시작하기 전에 수동으로 3개의 20 GiB 볼륨을 +프로비저닝해야 한다. +{{% /capture %}} + +{{% capture objectives %}} +이 튜토리얼을 마치면 다음에 대해 알게 된다. + +- 어떻게 스테이트풀셋을 이용하여 ZooKeeper 앙상블을 배포하는가. +- 어떻게 지속적으로 컨피그맵을 이용해서 앙상블을 설정하는가. +- 어떻게 ZooKeeper 서버 디플로이먼트를 앙상블 안에서 퍼뜨리는가. +- 어떻게 파드디스룹션버짓을 이용하여 계획된 점검 기간 동안 서비스 가용성을 보장하는가. + {{% /capture %}} + +{{% capture lessoncontent %}} + +### ZooKeeper 기본 {#zookeeper-basics} + +[아파치 ZooKeeper](https://zookeeper.apache.org/doc/current/)는 +분산 애플리케이션을 위한 분산 오픈 소스 코디네이션 서비스이다. +ZooKeeper는 데이터를 읽고 쓰고 갱신을 지켜보도록 한다. 데이터는 +파일시스템처럼 계층적으로 관리되고 앙상블(ZooKeeper 서버의 집합) 내에 모든 ZooKeeper서버에 복제된다. +데이터에 모든 연산은 원자적이고 순처적으로 일관된다. ZooKeeper는 +[Zab](https://pdfs.semanticscholar.org/b02c/6b00bd5dbdbd951fddb00b906c82fa80f0b3.pdf) 합의 프로토콜을 +이용하여 앙상블 내에 모든 서버에 걸쳐 상태 머신을 복제하여 이를 보장한다. + +앙상블은 리더 선출을 위해 Zab 프로토콜을 사용하고, 리더 선출과 선거가 완료되기 전까지 앙상블은 데이터를 쓸 수 없다. 완료되면 앙상블은 Zab을 이용하여 확인하고 클라이언트에 보여지도록 모든 쓰기를 쿼럼(quorum)에 복제한다. 가중치있는 쿼럼과 관련없이, 쿼럼은 현재 리더를 포함하는 앙상블의 대다수 컴포넌트이다. 예를 들어 앙상블이 3개 서버인 경우, 리더와 다른 서버로 쿼럼을 구성한다. 앙상블이 쿼럼을 달성할 수 없다면, 앙상블은 데이터를 쓸 수 없다. + +ZooKeeper는 전체 상태 머신을 메모리에 보존하고 모든 돌연변이를 저장 미디어의 내구성 있는 WAL(Write Ahead Log)에 기록한다. 서버 장애시 WAL을 재생하여 이전 상태를 복원할 수 있다. WAL이 무제한으로 커지는 것을 방지하기 위해 ZooKeeper는 주기적으로 저장 미디어에 메모리 상태의 스냅샷을 저장한다. 이 스냅샷은 메모리에 직접 적재할 수 있고 스냅샷 이전의 모든 WAL 항목은 삭제될 수 있다. + +## ZooKeeper 앙상블 생성하기 + +아래 메니페스트에는 +[헤드리스 서비스](/docs/concepts/services-networking/service/#headless-services), +[서비스](/docs/concepts/services-networking/service/), +[파드디스룹션버짓](/docs/concepts/workloads/pods/disruptions//#specifying-a-poddisruptionbudget), +[스테이트풀셋](/docs/concepts/workloads/controllers/statefulset/)을 포함한다. + +{{< codenew file="application/zookeeper/zookeeper.yaml" >}} + +터미널을 열고 +[`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands/#apply) 명령어로 +메니페스트를 생성하자. + +```shell +kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml +``` + +이는 `zk-hs` 헤드리스 서비스, `zk-cs` 서비스, +`zk-pdb` PodDisruptionBudget과 `zk` 스테이트풀셋을 생성한다. + +```shell +service/zk-hs created +service/zk-cs created +poddisruptionbudget.policy/zk-pdb created +statefulset.apps/zk created +``` + +[`kubectl get`](/docs/reference/generated/kubectl/kubectl-commands/#get)을 사용하여 +스테이트풀셋 컨트롤러가 스테이트풀셋 파드를 생성하는지 확인한다. + +```shell +kubectl get pods -w -l app=zk +``` + +`zk-2` 파드가 Running and Ready 상태가 되면, `CTRL-C`를 눌러 kubectl을 종료하자. + +```shell +NAME READY STATUS RESTARTS AGE +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 19s +zk-0 1/1 Running 0 40s +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 0s +zk-1 0/1 ContainerCreating 0 0s +zk-1 0/1 Running 0 18s +zk-1 1/1 Running 0 40s +zk-2 0/1 Pending 0 0s +zk-2 0/1 Pending 0 0s +zk-2 0/1 ContainerCreating 0 0s +zk-2 0/1 Running 0 19s +zk-2 1/1 Running 0 40s +``` + +스테이트풀셋 컨트롤러는 3개의 파드를 생성하고, 각 파드는 +[ZooKeeper](http://www-us.apache.org/dist/zookeeper/stable/) 서버를 포함한 컨테이너를 가진다. + + +### 리더 선출 촉진 + +익명 네트워크에서 리더 선출을 위한 종료 알고리즘이 없기에, Zab은 리더 선출을 위해 명시적인 멤버 구성을 해야 한다. 앙상블의 각 서버는 고유 식별자를 가져야 하고, 모든 서버는 식별자 전역 집합을 알아야 하며, 각 식별자는 네트워크 주소에 연관되어야 한다. + +[`kubectl exec`](/docs/reference/generated/kubectl/kubectl-commands/#exec)를 이용하여 `zk` 스테이트풀셋의 파드의 +호스트네임을 알아내자. + +```shell +for i in 0 1 2; do kubectl exec zk-$i -- hostname; done +``` + +스테이트풀셋 컨트롤러는 각 순번 인덱스에 기초하여 각 파드에 고유한 호스트네임을 부여한다. 각 호스트네임은 `<스테이트풀셋 이름>-<순번 인덱스>` 형식을 취한다. `zk` 스테이트풀셋의 `replicas` 필드는 `3`으로 설정되었기 때문에, 그 스테이트풀셋 컨트롤러는 3개 파드의 호스트네임을 `zk-0`, `zk-1`, +`zk-2`로 정한다. + +```shell +zk-0 +zk-1 +zk-2 +``` + +ZooKeeper 앙상블에 서버들은 고유 식별자로서 자연수를 이용하고 서버 데이터 디렉터리에 `my` 라는 파일로 서버 식별자를 저장한다. + +각 서버에서 다음 명령어를 이용하여 `myid` 파일의 내용을 확인하자. + +```shell +for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done +``` + +식별자는 자연수이고, 순번 인덱스들도 음수가 아니므로, 순번에 1을 더하여 순번을 만들 수 있다. + +```shell +myid zk-0 +1 +myid zk-1 +2 +myid zk-2 +3 +``` + +`zk` 스테이트풀셋의 각 파드 Fully Qualified Domain Name (FQDN)을 얻기 위해 다음 명령어를 이용하자. + +```shell +for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done +``` + +`zk-hs` 서비스는 모든 파드를 위한 도메인인 +`zk-hs.default.svc.cluster.local`을 만든다. + +```shell +zk-0.zk-hs.default.svc.cluster.local +zk-1.zk-hs.default.svc.cluster.local +zk-2.zk-hs.default.svc.cluster.local +``` + +[쿠버네티스 DNS](/docs/concepts/services-networking/dns-pod-service/)의 A 레코드는 FQDN을 파드의 IP 주소로 풀어낸다. 쿠버네티스가 파드를 리스케줄하면, 파드의 새 IP 주소로 A 레코드를 갱신하지만, A 레코드의 이름은 바뀌지 않는다. + +ZooKeeper는 그것의 애플리케이션 환경설정을 `zoo.cfg` 파일에 저장한다. `kubectl exec`를 이용하여 `zk-0` 파드의 `zoo.cfg` 내용을 보자. + +```shell +kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg +``` + +아래 파일의 `server.1`, `server.2`, `server.3` 속성에서 +`1`, `2`, `3`은 ZooKeeper 서버의 `myid` 파일에 구분자와 +연관된다. +이들은 `zk` 스테이트풀셋의 파드의 FQDNS을 설정한다. + +```shell +clientPort=2181 +dataDir=/var/lib/zookeeper/data +dataLogDir=/var/lib/zookeeper/log +tickTime=2000 +initLimit=10 +syncLimit=2000 +maxClientCnxns=60 +minSessionTimeout= 4000 +maxSessionTimeout= 40000 +autopurge.snapRetainCount=3 +autopurge.purgeInterval=0 +server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888 +server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888 +server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888 +``` + +### 합의 달성 + +합의 프로토콜에서 각 참가자의 식별자는 유일해야 한다. Zab 프로토콜에서 동일한 고유 식별자를 요청하는 참가자는 없다. 이는 시스템 프로세스가 어떤 프로세스가 어떤 데이터를 커밋했는지 동의하게 하는데 필요하다. 2개 파드를 동일 순번으로 시작하였다면 두 대의 ZooKeeper 서버는 둘 다 스스로를 동일 서버로 식별한다. + +합의 프로토콜에서 각 참여자의 식별자는 고유해야 한다. Zab 프로토콜에 두 참여자가 동일한 고유 식별자로 요청해서는 안된다. 이는 시스템 프로세스가 어떤 프로세스가 어떤 데이터를 커밋했는지 동의하도록 하기 위해 필수적이다. 동일 순번으로 두 개의 파드가 실행했다면 두 ZooKeeper 서버는 모두 동일한 서버로 식별된다. +```shell +kubectl get pods -w -l app=zk + +NAME READY STATUS RESTARTS AGE +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 19s +zk-0 1/1 Running 0 40s +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 0s +zk-1 0/1 ContainerCreating 0 0s +zk-1 0/1 Running 0 18s +zk-1 1/1 Running 0 40s +zk-2 0/1 Pending 0 0s +zk-2 0/1 Pending 0 0s +zk-2 0/1 ContainerCreating 0 0s +zk-2 0/1 Running 0 19s +zk-2 1/1 Running 0 40s +``` + +각 파드의 A 레코드는 파드가 Ready 상태가 되면 입력된다. 따라서 +ZooKeeper 서버의 FQDN은 단일 엔드포인트로 확인되고 +해당 엔드포인트는 `myid` 파일에 구성된 식별자를 가진 +고유한 ZooKeeper 서버가 된다. + +```shell +zk-0.zk-hs.default.svc.cluster.local +zk-1.zk-hs.default.svc.cluster.local +zk-2.zk-hs.default.svc.cluster.local +``` + +이것은 ZooKeeper의 `zoo.cfg` 파일에 `servers` 속성이 정확히 구성된 앙상블로 나타나는 것을 보증한다. + +```shell +server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888 +server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888 +server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888 +``` + +서버가 Zab 프로토콜로 값을 커밋 시도하면, 합의를 이루어 값을 커밋하거나(리더 선출에 성공했고 나머지 두 개 파드도 Running과 Ready 상태라면) 실패한다(조건 중 하나라도 충족하지 않으면). 다른 서버를 대신하여 쓰기를 승인하는 상태는 발생하지 않는다. + +### 앙상블 무결성 테스트 + +가장 기본적인 테스트는 한 ZooKeeper 서버에 데이터를 쓰고 다른 ZooKeeper 서버에서 데이터를 읽는 것이다. + +아래 명령어는 앙상블 내에 `zk-0` 파드에서 `/hello` 경로로 `world`를 쓰는 스크립트인 `zkCli.sh`를 실행한다. + +```shell +kubectl exec zk-0 zkCli.sh create /hello world + +WATCHER:: + +WatchedEvent state:SyncConnected type:None path:null +Created /hello +``` + +`zk-1` 파드에서 데이터를 읽기 위해 다음 명령어를 이용하자. + +```shell +kubectl exec zk-1 zkCli.sh get /hello +``` + +`zk-0`에서 생성한 그 데이터는 앙상블 내에 모든 서버에서 +사용할 수 있다. + +```shell +WATCHER:: + +WatchedEvent state:SyncConnected type:None path:null +world +cZxid = 0x100000002 +ctime = Thu Dec 08 15:13:30 UTC 2016 +mZxid = 0x100000002 +mtime = Thu Dec 08 15:13:30 UTC 2016 +pZxid = 0x100000002 +cversion = 0 +dataVersion = 0 +aclVersion = 0 +ephemeralOwner = 0x0 +dataLength = 5 +numChildren = 0 +``` + +### 내구성있는 저장소 제공 + +[ZooKeeper 기본](#zookeeper-basics) 섹션에서 언급했듯이 +ZooKeeper는 모든 항목을 내구성있는 WAL에 커밋하고 메모리 상태의 스냅샷을 저장 미디에에 주기적으로 저장한다. +내구성을 제공하기 위해 WAL을 이용하는 것은 +복제된 상태 머신을 이루는 합의 프로토콜에서 +이용하는 일반적인 기법이다. + +[`kubectl delete`](/docs/reference/generated/kubectl/kubectl-commands/#delete) 명령을 이용하여 +`zk` 스테이트풀셋을 삭제하자. + +```shell +kubectl delete statefulset zk +statefulset.apps "zk" deleted +``` + +스테이트풀셋의 파드가 종료되는 것을 지켜보자. + +```shell +kubectl get pods -w -l app=zk +``` + +`zk-0`이 완전히 종료되면 `CTRL-C`를 이용해 kubectl을 종료하자. + +```shell +zk-2 1/1 Terminating 0 9m +zk-0 1/1 Terminating 0 11m +zk-1 1/1 Terminating 0 10m +zk-2 0/1 Terminating 0 9m +zk-2 0/1 Terminating 0 9m +zk-2 0/1 Terminating 0 9m +zk-1 0/1 Terminating 0 10m +zk-1 0/1 Terminating 0 10m +zk-1 0/1 Terminating 0 10m +zk-0 0/1 Terminating 0 11m +zk-0 0/1 Terminating 0 11m +zk-0 0/1 Terminating 0 11m +``` + +`zookeeper.yaml` 메니페스트를 다시 적용한다. + +```shell +kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml +``` + +`zk` 스테이트풀셋 오브젝트를 생성하지만, 매니페스트에 다른 API 오브젝트는 이미 존재하므로 수정되지 않는다. + +스테이트풀셋 컨트롤러가 스테트풀셋의 파드를 재생성하는 것을 확인한다. + +```shell +kubectl get pods -w -l app=zk +``` + +`zk-2` 파드가 Running과 Ready가 되면 `CTRL-C`를 이용하여 kubectl을 종료한다. + +```shell +NAME READY STATUS RESTARTS AGE +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 19s +zk-0 1/1 Running 0 40s +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 0s +zk-1 0/1 ContainerCreating 0 0s +zk-1 0/1 Running 0 18s +zk-1 1/1 Running 0 40s +zk-2 0/1 Pending 0 0s +zk-2 0/1 Pending 0 0s +zk-2 0/1 ContainerCreating 0 0s +zk-2 0/1 Running 0 19s +zk-2 1/1 Running 0 40s +``` + +아래 명령어로 [무결성 테스트](#sanity-testing-the-ensemble)에서 입력한 값을 +`zk-2` 파드에서 얻어온다. + +```shell +kubectl exec zk-2 zkCli.sh get /hello +``` + +`zk` 스테이트풀셋의 모든 파드를 종료하고 재생성했음에도, 앙상블은 여전히 원래 값을 돌려준다. + +```shell +WATCHER:: + +WatchedEvent state:SyncConnected type:None path:null +world +cZxid = 0x100000002 +ctime = Thu Dec 08 15:13:30 UTC 2016 +mZxid = 0x100000002 +mtime = Thu Dec 08 15:13:30 UTC 2016 +pZxid = 0x100000002 +cversion = 0 +dataVersion = 0 +aclVersion = 0 +ephemeralOwner = 0x0 +dataLength = 5 +numChildren = 0 +``` + +`zk` 스테이트풀셋의 `spec`에 `volumeClaimTemplates` 필드는 각 파드에 프로비전될 퍼시스턴트볼륨을 지정한다. + + +```yaml +volumeClaimTemplates: + - metadata: + name: datadir + annotations: + volume.alpha.kubernetes.io/storage-class: anything + spec: + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: 20Gi +``` + +`스테이트풀셋` 컨트롤러는 `스테이트풀셋`의 각 파드에 대한 +`퍼시스턴트볼륨클레임`을 생성한다. + +다음 명령어를 이용하여 `스테이트풀셋`의 `퍼시스턴트볼륨클레임`을 살펴보자. + +```shell +kubectl get pvc -l app=zk +``` + +`스테이트풀셋`의 파드를 재생성할 때에 파드의 퍼시스턴트볼륨도 다시 마운트한다. + +```shell +NAME STATUS VOLUME CAPACITY ACCESSMODES AGE +datadir-zk-0 Bound pvc-bed742cd-bcb1-11e6-994f-42010a800002 20Gi RWO 1h +datadir-zk-1 Bound pvc-bedd27d2-bcb1-11e6-994f-42010a800002 20Gi RWO 1h +datadir-zk-2 Bound pvc-bee0817e-bcb1-11e6-994f-42010a800002 20Gi RWO 1h +``` + +`스테이트풀셋`의 컨테이너 `template`의 `volumeMounts` 부분이 ZooKeeper 서버의 데이터 디렉터리에 퍼시스턴트볼륨 마운트하는 내용이다. + + +```shell +volumeMounts: + - name: datadir + mountPath: /var/lib/zookeeper +``` + +`zk` 스테이트풀셋이 (재)스케줄링될 때 항상 동일한 `퍼시스턴트볼륨`을 +ZooKeeper의 서버 디렉터리에 마운트한다. +파드를 재스케쥴할 때에도 ZooKeeper의 WAL을 통해 이뤄진 모든 쓰기와 +모든 그 스냅샷도 내구성을 유지한다. + +## 일관된 구성 보장하기 + +[리더 선출 촉진](#facilitating-leader-election)과 +[합의 달성](#achieving-consensus) 섹션에서 알렸듯이, +ZooKeeper 앙상블에 서버는 리더 선출과 쿼럼을 구성하기 위한 일관된 설정이 필요하다. +또한 Zab 프로토콜의 일관된 설정도 +네트워크에 걸쳐 올바르게 동작하기 위해서 +필요하다. 이 예시에서는 메니페스트에 구성을 직접 포함시켜서 일관된 구성을 +달성한다. + +`zk` 스테이트풀셋을 살펴보자. + +```shell +kubectl get sts zk -o yaml +… +command: + - sh + - -c + - "start-zookeeper \ + --servers=3 \ + --data_dir=/var/lib/zookeeper/data \ + --data_log_dir=/var/lib/zookeeper/data/log \ + --conf_dir=/opt/zookeeper/conf \ + --client_port=2181 \ + --election_port=3888 \ + --server_port=2888 \ + --tick_time=2000 \ + --init_limit=10 \ + --sync_limit=5 \ + --heap=512M \ + --max_client_cnxns=60 \ + --snap_retain_count=3 \ + --purge_interval=12 \ + --max_session_timeout=40000 \ + --min_session_timeout=4000 \ + --log_level=INFO" +… +``` + +ZooKeeper 서버를 시작하는데 사용한 명령어는 커맨드라인 파라미터로 환경 구성을 전달했다. 환경 변수를 이용하여서도 앙상블에 환경 구성을 전달할 수 있다. + +### 로깅 설정하기 + +`zkGenConfig.sh` 스크립트로 생성된 파일 중 하나는 ZooKeeper의 로깅을 제어한다. +ZooKeeper는 [Log4j](http://logging.apache.org/log4j/2.x/)를 이용하며 +기본 로깅 구성으로는 시간과 파일 크기 기준의 롤링 파일 어펜더를 사용한다. + + `zk` `스테이트풀셋`의 한 파드에서 로깅 설정을 살펴보는 아래 명령어를 이용하자. + +```shell +kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties +``` + +아래 로깅 구성은 ZooKeeper가 모든 로그를 +표준 출력 스트림으로 처리하게 한다. + +```shell +zookeeper.root.logger=CONSOLE +zookeeper.console.threshold=INFO +log4j.rootLogger=${zookeeper.root.logger} +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold} +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n +``` + +이는 컨테이너 내에서 안전하게 로깅하는 가장 단순한 방법이다. 표준 출력으로 애플리케이션 로그를 작성하면, 쿠버네티스는 로그 로테이션을 처리한다. 또한 쿠버네티스는 애플리케이션이 표준 출력과 표준 오류에 쓰여진 로그로 인하여 로컬 저장 미디어가 고갈되지 않도록 보장하는 정상적인 보존 정책을 구현한다. + +파드의 마지막 20줄의 로그를 가져오는 [`kubectl logs`](/docs/reference/generated/kubectl/kubectl-commands/#logs) 명령을 이용하자. + +```shell +kubectl logs zk-0 --tail 20 +``` + +`kubectl logs`를 이용하거나 쿠버네티스 대시보드에서 표준 출력과 표준 오류로 쓰여진 애플리케이션 로그를 볼 수 있다. + +```shell +2016-12-06 19:34:16,236 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52740 +2016-12-06 19:34:16,237 [myid:1] - INFO [Thread-1136:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52740 (no session established for client) +2016-12-06 19:34:26,155 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52749 +2016-12-06 19:34:26,155 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52749 +2016-12-06 19:34:26,156 [myid:1] - INFO [Thread-1137:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52749 (no session established for client) +2016-12-06 19:34:26,222 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52750 +2016-12-06 19:34:26,222 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52750 +2016-12-06 19:34:26,226 [myid:1] - INFO [Thread-1138:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52750 (no session established for client) +2016-12-06 19:34:36,151 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52760 +2016-12-06 19:34:36,152 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52760 +2016-12-06 19:34:36,152 [myid:1] - INFO [Thread-1139:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52760 (no session established for client) +2016-12-06 19:34:36,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52761 +2016-12-06 19:34:36,231 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52761 +2016-12-06 19:34:36,231 [myid:1] - INFO [Thread-1140:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52761 (no session established for client) +2016-12-06 19:34:46,149 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52767 +2016-12-06 19:34:46,149 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52767 +2016-12-06 19:34:46,149 [myid:1] - INFO [Thread-1141:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52767 (no session established for client) +2016-12-06 19:34:46,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52768 +2016-12-06 19:34:46,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52768 +2016-12-06 19:34:46,230 [myid:1] - INFO [Thread-1142:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52768 (no session established for client) +``` + +쿠버네티스는 더 강력하지만 조금 복잡한 로그 통합을 +[스택드라이버](/docs/tasks/debug-application-cluster/logging-stackdriver/)와 +[Elasticsearch와 Kibana](/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana/)를 지원한다. +클러스터 수준의 로그 적재(ship)와 통합을 위해서는 로그 순환과 적재를 위해 +[사이드카](https://kubernetes.io/blog/2015/06/the-distributed-system-toolkit-patterns) 컨테이너를 배포하는 것을 고려한다. + +### 권한없는 사용자를 위해 구성하기 + +컨테이너 내부의 권한있는 유저로 애플리케이션을 실행할 수 있도록 하는 +최상의 방법은 논쟁거리이다. +조직에서 애플리케이션을 권한없는 사용자가 실행한다면, +진입점을 실행할 사용자를 제어하기 위해 +[시큐리티컨텍스트](/docs/tasks/configure-pod-container/security-context/)를 이용할 수 있다. + +`zk` `스테이트풀셋`의 파드 `template`은 `SecurityContext`를 포함한다. + +```yaml +securityContext: + runAsUser: 1000 + fsGroup: 1000 +``` + +파드 컨테이너에서 UID 1000은 ZooKeeper 사용자이며, GID 1000은 +ZooKeeper의 그룹에 해당한다. + +`zk-0` 파드에서 프로세스 정보를 얻어오자. + +```shell +kubectl exec zk-0 -- ps -elf +``` + +`securityContext` 오브젝트의 `runAsUser` 필드 값이 1000 이므로 +루트 사용자로 실행하는 대신 ZooKeeper 프로세스는 ZooKeeper 사용자로 실행된다. +```shell +F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD +4 S zookeep+ 1 0 0 80 0 - 1127 - 20:46 ? 00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground +0 S zookeep+ 27 1 0 80 0 - 1155556 - 20:46 ? 00:00:19 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg +``` + +기본적으로 파드의 퍼시스턴트볼륨은 ZooKeeper 서버의 데이터 디렉터리에 마운트되고, 루트 사용자만이 접근 가능하다. 이 구성은 ZooKeeper 프로세스가 WAL에 기록하고 스냅샷을 저장하는 것을 방지한다. + +`zk-0` 파드의 ZooKeeper 데이터 디렉터리의 권한을 얻어오는 아래 명령어를 이용하자. + +```shell +kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data +``` + +`securityContext` 오브젝트의 `fsGroup` 필드 값이 1000 이므로, 파드의 퍼시스턴트 볼륨의 소유권은 ZooKeeper 그룹으로 지정되어 ZooKeeper 프로세스에서 읽고 쓸 수 있다. + +```shell +drwxr-sr-x 3 zookeeper zookeeper 4096 Dec 5 20:45 /var/lib/zookeeper/data +``` + +## ZooKeeper 프로세스 관리하기 + +[ZooKeeper 문서](https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_supervision)에서는 +"ZooKeeper의 서버 프로세스(JVM)을 관리할 +감독 프로세스를 필요할 것이다."라고 말한다. +와치독(감독 프로세스)를 활용하여 실패한 프로세스를 재시작하는 것은 분산시스템에서 +일반적인 방식이다. 쿠버네티스에서 애플리케이션을 배포할 때에는 +감독 프로세스로 외부 유틸리티를 사용하기보다 쿠버네티스를 애플리케이션의 +와치독으로서 사용해야 한다. + +### 앙상블 관리하기 + +`zk` `스테이트풀셋`은 `RollingUpdate` 업데이트 전략을 이용하도록 구성되었다. + +`kubectl patch`로 서버에 할당된 `cpu` 수를 갱신할 수 있다. + +```shell +kubectl patch sts zk --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]' + +statefulset.apps/zk patched +``` + +업데이트 상황을 지켜보기 위해 `kubectl rollout status` 이용하자. + +```shell +kubectl rollout status sts/zk + +waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664... +Waiting for 1 pods to be ready... +Waiting for 1 pods to be ready... +waiting for statefulset rolling update to complete 1 pods at revision zk-5db4499664... +Waiting for 1 pods to be ready... +Waiting for 1 pods to be ready... +waiting for statefulset rolling update to complete 2 pods at revision zk-5db4499664... +Waiting for 1 pods to be ready... +Waiting for 1 pods to be ready... +statefulset rolling update complete 3 pods at revision zk-5db4499664... +``` + +이것은 파드를 역순으로 한번에 하나씩 종료하고, 새로운 구성으로 재생성한다. 이는 롤링업데이트 동안에 쿼럼을 유지하도록 보장한다. + +이력과 이전 구성을 보기 위해 `kubectl rollout history` 명령을 이용하자. + +```shell +kubectl rollout history sts/zk + +statefulsets "zk" +REVISION +1 +2 +``` + +수정사항을 롤백하기 위해 `kubectl rollout undo` 명령을 이용하자. + +```shell +kubectl rollout undo sts/zk + +statefulset.apps/zk rolled back +``` + +### 프로세스 장애 관리하기 + +[재시작 정책](/docs/user-guide/pod-states/#restartpolicy)은 +쿠버네티스가 파드 내에 컨테이너의 진입점에서 프로세스 실패를 어떻게 다루는지 제어한다. +`스테이트풀셋`의 파드에서 오직 적절한 `재시작 정책`는 Always이며 +이것이 기본 값이다. 상태가 유지되는 애플리케이션을 위해 +기본 정책을 **절대로** 변경하지 말자. + +`zk-0` 파드에서 실행중인 ZooKeeper 서버에서 프로세스 트리를 살펴보기 위해 다음 명령어를 이용하자. + +```shell +kubectl exec zk-0 -- ps -ef +``` + +컨테이너의 엔트리 포인트로 PID 1 인 명령이 사용되엇으며 +ZooKeeper 프로세스는 엔트리 포인트의 자식 프로세스로 PID 27 이다. +```shell +UID PID PPID C STIME TTY TIME CMD +zookeep+ 1 0 0 15:03 ? 00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground +zookeep+ 27 1 0 15:03 ? 00:00:03 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg +``` + +다른 터미널에서 다음 명령어로 `zk` `스테이트풀셋`의 파드를 확인한다. + +```shell +kubectl get pod -w -l app=zk +``` + +또 다른 터미널에서 다음 명령어로 `zk-0` 파드의 ZooKeeper 프로세스를 종료시킨다. + +```shell +kubectl exec zk-0 -- pkill java +``` + +ZooKeeper 프로세스의 종료는 부모 프로세스의 종료를 일으킨다. 컨테이너 `재시작정책`이 Always이기 때문에 부모 프로세스를 재시작했다. + +```shell +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Running 0 21m +zk-1 1/1 Running 0 20m +zk-2 1/1 Running 0 19m +NAME READY STATUS RESTARTS AGE +zk-0 0/1 Error 0 29m +zk-0 0/1 Running 1 29m +zk-0 1/1 Running 1 29m +``` + +애플리케이션이 스크립트(`zkServer.sh` 같은)를 애플리케이션의 비지니스 로직을 구현한 프로세스를 시작하기 위해 이용한다면, +그 스크립트는 자식 프로세스와 함께 반드시 종료되어야 한다. +이는 쿠버네티스가 애플리케이션의 비지니스 로직을 구현한 프로세스가 실패할 때에 +애플리케이션 컨테이너를 재시작하는 것을 보증한다. + +### 활성도(Liveness) 테스트하기 + +실패한 애플리케이션을 재시작하도록 구성하는 것은 분산 시스템을 +건강하게 유지하는데 충분하지 않다. 시스템의 프로세스는 살아있지만 +응답이 없을 수 있고, 혹은 다른 건강하지 않은 경우의 시나리오가 있다. +애플리케이션 프로세스가 건강하지 않고 재시작해야만 한다는 것을 +쿠버네티스에게 알리도록 활성도 검사를 이용해야 한다. + +`zk` `스테이트풀셋`에 파드 `template`에 활성도 검사를 명시한다. +`` + +```yaml + livenessProbe: + exec: + command: + - sh + - -c + - "zookeeper-ready 2181" + initialDelaySeconds: 15 + timeoutSeconds: 5 +``` + +검사는 ZooKeeper의 `ruok` 4 글자 단어를 이용해서 서버의 건강을 테스트하는 +배쉬 스크립트를 호출한다. + +```bash +OK=$(echo ruok | nc 127.0.0.1 $1) +if [ "$OK" == "imok" ]; then + exit 0 +else + exit 1 +fi +``` + +한 터미널에서 `zk` 스테이트풀셋의 파드를 지켜보기 위해 다음 명령어를 이용하자. + +```shell +kubectl get pod -w -l app=zk +``` + +다른 창에서 `zk-0` 파드의 파일시스템에서 `zkOk.sh` 스크립트를 삭제하기 위해 다음 명령어를 이용하자. + +```shell +kubectl exec zk-0 -- rm /usr/bin/zookeeper-ready +``` + +ZooKeeper의 활성도 검사에 실패하면, +쿠버네티스는 자동으로 프로세스를 재시작하여 앙상블에 건강하지 않은 프로세스를 +재시작하는 것을 보증한다. + +```shell +kubectl get pod -w -l app=zk + +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Running 0 1h +zk-1 1/1 Running 0 1h +zk-2 1/1 Running 0 1h +NAME READY STATUS RESTARTS AGE +zk-0 0/1 Running 0 1h +zk-0 0/1 Running 1 1h +zk-0 1/1 Running 1 1h +``` + +### 준비도 테스트 + +준비도는 활성도와 동일하지 않다. 프로세스가 살아 있다면, 스케쥴링되고 건강하다. +프로세스가 준비되면 입력을 처리할 수 있다. 활성도는 필수적이나 준비도의 조건으로는 +충분하지 않다. 몇몇의 경우 +특별히 초기화와 종료 시에 프로세스는 살아있지만 +준비되지 않을 수 있다. + +준비도 검사를 지정하면, 쿠버네티스는 준비도가 통과할 때까지 +애플리케이션 프로세스가 네트워크 트래픽을 수신하지 않게 한다. + +ZooKeeper 서버에서는 준비도가 활성도를 내포한다. 그러므로 `zookeeper.yaml` 메니페스트에서 +준비도 검사는 활성도 검사와 동일하다. + +```yaml + readinessProbe: + exec: + command: + - sh + - -c + - "zookeeper-ready 2181" + initialDelaySeconds: 15 + timeoutSeconds: 5 +``` + +활성도와 준비도 검사가 동일함에도 둘 다 지정하는 것은 중요하다. +이는 ZooKeeper 앙상블에 건강한 서버만 아니라 +네트워크 트래픽을 수신하는 것을 보장한다. + +## 노드 실패 방지 + +ZooKeeper는 변조된 데이터를 성공적으로 커밋하기 위한 서버의 쿼럼이 필요하다. +3개의 서버 앙상블에서 성공적으로 저장하려면 2개 서버는 반드시 건강해야 한다. +쿼럼 기반 시스템에서, 멤버는 가용성을 보장하는 +실패 영역에 걸쳐 배포된다. +중단을 방지하기 위해 개별 시스템의 손실로 인해 모범 사례에서는 동일한 시스템에 +여러 인스턴스의 응용 프로그램을 함께 배치하는 것을 배제한다. + +기본적으로 쿠버네티스는 동일 노드상에 `스테이트풀셋`의 파드를 위치시킬 수 있다. 생성한 3개의 서버 앙상블에서 2개의 서버가 같은 노드에 있다면, 그 노드는 실패하고 ZooKeeper 서비스 클라이언트는 그 파드들의 최소 하나가 재스케쥴링될 때까지 작동 중단을 경험할 것이다. + +노드 실패하는 사건 중에도 중요 시스템의 프로세스가 재스케줄될 수 있게 +항상 추가적인 용량을 프로비전해야 한다. 그렇게 하면 쿠버네티스 스케줄러가 +ZooKeeper 서버 하나를 다시 스케줄하는 동안까지만 작동 중단될 것이다. +그러나 서비스에서 노드 실패로 인한 다운타임을 방지하려 한다면, +`파드안티어피니티`를 설정해야 한다. + +`zk` `스테이트풀셋`의 파드의 노드를 알아보기 위해 다음 명령어를 이용하자. + +```shell +for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done +``` + +`zk` `스테이트풀셋`에 모든 파드는 다른 노드에 배포된다. + +```shell +kubernetes-minion-group-cxpk +kubernetes-minion-group-a5aq +kubernetes-minion-group-2g2d +``` + +이는 `zk` `스테이트풀셋`의 파드에 `파드안티어피니티(PodAntiAffinity)`를 지정했기 때문이다. + +```yaml + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - zk + topologyKey: "kubernetes.io/hostname" +``` + +`requiredDuringSchedulingIgnoredDuringExecution` 필드는 +쿠버네티스 스케줄러에 `topologyKey`로 정의된 도메인에서 `app`이 `zk`라고 레이블링된 +두 개 파드가 위치하지 않도록 한다. +`topologyKey` `kubernetes.io/hostname`은 도메인이 개별 노드임을 나타낸다. +다른 규칙과 레이블, 셀렉터를 사용하여 +앙상블을 물리적인, 네트워크, 전원 장애 분야에 걸쳐 확산하도록 이 기법을 확장할 수 있다. + +## 생존 유지 + +**이 섹션에서는 노드를 통제(cordon)하고 비운다(drain). 공유된 클러스터에서 이 튜토리얼을 진행한다면, +다른 테넌트에 부정적인 영향을 비치지 않음을 보증해야 한다.** + +이전 섹션은 계획되지 않은 노드 실패에서 살아 남도록 +어떻게 파드를 확산할 것인가에 대해 알아보았다. +그러나 계획된 점검으로 인해 발생하는 일시적인 노드 실패에 대한 계획도 필요하다. + +클러스터에서 다음 명령으로 노드를 살펴보자. + +```shell +kubectl get nodes +``` + +[`kubectl cordon`](/docs/reference/generated/kubectl/kubectl-commands/#cordon)을 이용하여 +클러스터 내에 4개 노드를 제외하고 다른 모든 노드를 통제해보자. + +```shell +kubectl cordon <노드-이름> +``` + +`zk-pdb` `PodDisruptionBudget`을 살펴보고자 이 명령어를 이용하자. + +```shell +kubectl get pdb zk-pdb +``` + +`max-unavailable` 필드는 쿠버네티스가 `zk` `스테이트풀셋`에서 최대 1개의 파드는 +언제든지 가용하지 않을 수 있음을 나타낸다. + +```shell +NAME MIN-AVAILABLE MAX-UNAVAILABLE ALLOWED-DISRUPTIONS AGE +zk-pdb N/A 1 1 +``` + +한 터미널에서 `zk` `스테이트풀셋`의 파드를 지켜보는 이 명령어를 이용하자. + +```shell +kubectl get pods -w -l app=zk +``` + +다른 터미널에서 현재 스케쥴되는 파드의 노드를 살펴보자. + +```shell +for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done + +kubernetes-minion-group-pb41 +kubernetes-minion-group-ixsl +kubernetes-minion-group-i4c4 +``` + +`zk-0`파드가 스케쥴되는 노드를 통제하기 위해 +[`kubectl drain`](/docs/reference/generated/kubectl/kubectl-commands/#drain)를 이용하자. + +```shell +kubectl drain $(kubectl get pod zk-0 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data +node "kubernetes-minion-group-pb41" cordoned + +WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-minion-group-pb41, kube-proxy-kubernetes-minion-group-pb41; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-o5elz +pod "zk-0" deleted +node "kubernetes-minion-group-pb41" drained +``` + +클러스터에 4개 노드가 있기 때문에 `kubectl drain`이 성공하여 +`zk-0`을 다른 노드로 재스케쥴링 된다. + +```shell +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Running 2 1h +zk-1 1/1 Running 0 1h +zk-2 1/1 Running 0 1h +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 51s +zk-0 1/1 Running 0 1m +``` + +계속해서 `스테이트풀셋`의 파드를 첫 터미널에서 지켜보고 +`zk-1` 이 스케쥴된 노드를 비워보자. + +```shell +kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data "kubernetes-minion-group-ixsl" cordoned + +WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-minion-group-ixsl, kube-proxy-kubernetes-minion-group-ixsl; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-voc74 +pod "zk-1" deleted +node "kubernetes-minion-group-ixsl" drained +``` + +`zk-1` 파드는 스케쥴되지 않는데 이는 `zk` `스테이트풀셋`이 오직 2개 노드가 스케쥴되도록 파드를 위치시키는 것을 금하는 `파드안티어피니티` 규칙을 포함하였기 때문이고 그 파드는 Pending 상태로 남을 것이다. + +```shell +kubectl get pods -w -l app=zk + +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Running 2 1h +zk-1 1/1 Running 0 1h +zk-2 1/1 Running 0 1h +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 51s +zk-0 1/1 Running 0 1m +zk-1 1/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 0s +``` + +계속해서 스테이트풀셋의 파드를 지켜보고 +`zk-2`가 스케줄된 노드를 비워보자. + +```shell +kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data +node "kubernetes-minion-group-i4c4" cordoned + +WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-minion-group-i4c4, kube-proxy-kubernetes-minion-group-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog +WARNING: Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog; Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-minion-group-i4c4, kube-proxy-kubernetes-minion-group-i4c4 +There are pending pods when an error occurred: Cannot evict pod as it would violate the pod's disruption budget. +pod/zk-2 +``` + +kubectl 을 종료하기 위해 `CTRL-C`를 이용하자. + +`zk-2`를 추출하는 것은 `zk-budget`을 위반하기 때문에 셋째 노드를 비울 수 없다. 그러나 그 노드는 통제 상태로 남는다. + +`zk-0`에서 온전성 테스트 때에 입력한 값을 가져오는 `zkCli.sh`를 이용하자. + + +```shell +kubectl exec zk-0 zkCli.sh get /hello +``` + +`PodDisruptionBudget`이 존중되기 떄문에 서비스는 여전히 가용하다. + +```shell +WatchedEvent state:SyncConnected type:None path:null +world +cZxid = 0x200000002 +ctime = Wed Dec 07 00:08:59 UTC 2016 +mZxid = 0x200000002 +mtime = Wed Dec 07 00:08:59 UTC 2016 +pZxid = 0x200000002 +cversion = 0 +dataVersion = 0 +aclVersion = 0 +ephemeralOwner = 0x0 +dataLength = 5 +numChildren = 0 +``` + +[`kubectl uncordon`](/docs/reference/generated/kubectl/kubectl-commands/#uncordon) 이용하여 첫 노드의 통제를 풀자. + +```shell +kubectl uncordon kubernetes-minion-group-pb41 + +node "kubernetes-minion-group-pb41" uncordoned +``` + +`zk-1`은 이 노드에서 재스케쥴된다. `zk-1`이 Running과 Ready가 될 때까지 기다리자. + +```shell +kubectl get pods -w -l app=zk + +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Running 2 1h +zk-1 1/1 Running 0 1h +zk-2 1/1 Running 0 1h +NAME READY STATUS RESTARTS AGE +zk-0 1/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Terminating 2 2h +zk-0 0/1 Pending 0 0s +zk-0 0/1 Pending 0 0s +zk-0 0/1 ContainerCreating 0 0s +zk-0 0/1 Running 0 51s +zk-0 1/1 Running 0 1m +zk-1 1/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Terminating 0 2h +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 0s +zk-1 0/1 Pending 0 12m +zk-1 0/1 ContainerCreating 0 12m +zk-1 0/1 Running 0 13m +zk-1 1/1 Running 0 13m +``` + +`zk-2`가 스케쥴된 노드를 비워보자. + +```shell +kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-local-data +``` + +출력은 + +``` +node "kubernetes-minion-group-i4c4" already cordoned +WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-minion-group-i4c4, kube-proxy-kubernetes-minion-group-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog +pod "heapster-v1.2.0-2604621511-wht1r" deleted +pod "zk-2" deleted +node "kubernetes-minion-group-i4c4" drained +``` + +이번엔 `kubectl drain` 이 성공한다. + +`zk-2`가 재스케줄되도록 두번째 노드의 통제를 풀어보자. + +```shell +kubectl uncordon kubernetes-minion-group-ixsl +``` + +``` +node "kubernetes-minion-group-ixsl" uncordoned +``` + +`kubectl drain`을 `PodDisruptionBudget`과 결합하면 유지보수중에도 서비스를 가용하게 할 수 있다. drain으로 노드를 통제하고 유지보수를 위해 노드를 오프라인하기 전에 파드를 추출하기 위해 사용한다면 서비스는 혼란 예산을 표기한 서비스는 그 예산이 존중은 존중될 것이다. 파드가 즉각적으로 재스케줄 할 수 있도록 항상 중요 서비스를 위한 추가 용량을 할당해야 한다. +{{% /capture %}} + +{{% capture cleanup %}} + +- `kubectl uncordon`은 클러스터 내에 모든 노드를 통제 해제한다. +- 이 튜토리얼에서 사용한 퍼시스턴트 볼륨을 위한 + 퍼시스턴트 스토리지 미디어를 삭제하자. + 귀하의 환경과 스토리지 구성과 프로비저닝 방법에서 필요한 절차를 따라서 + 모든 스토리지가 재확보되도록 하자. +{{% /capture %}} + diff --git a/content/ko/examples/application/zookeeper/zookeeper.yaml b/content/ko/examples/application/zookeeper/zookeeper.yaml new file mode 100644 index 0000000000000..4afa806a5412b --- /dev/null +++ b/content/ko/examples/application/zookeeper/zookeeper.yaml @@ -0,0 +1,133 @@ +apiVersion: v1 +kind: Service +metadata: + name: zk-hs + labels: + app: zk +spec: + ports: + - port: 2888 + name: server + - port: 3888 + name: leader-election + clusterIP: None + selector: + app: zk +--- +apiVersion: v1 +kind: Service +metadata: + name: zk-cs + labels: + app: zk +spec: + ports: + - port: 2181 + name: client + selector: + app: zk +--- +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: zk-pdb +spec: + selector: + matchLabels: + app: zk + maxUnavailable: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: zk +spec: + selector: + matchLabels: + app: zk + serviceName: zk-hs + replicas: 3 + updateStrategy: + type: RollingUpdate + podManagementPolicy: Parallel + template: + metadata: + labels: + app: zk + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - zk + topologyKey: "kubernetes.io/hostname" + containers: + - name: kubernetes-zookeeper + imagePullPolicy: Always + image: "k8s.gcr.io/kubernetes-zookeeper:1.0-3.4.10" + resources: + requests: + memory: "1Gi" + cpu: "0.5" + ports: + - containerPort: 2181 + name: client + - containerPort: 2888 + name: server + - containerPort: 3888 + name: leader-election + command: + - sh + - -c + - "start-zookeeper \ + --servers=3 \ + --data_dir=/var/lib/zookeeper/data \ + --data_log_dir=/var/lib/zookeeper/data/log \ + --conf_dir=/opt/zookeeper/conf \ + --client_port=2181 \ + --election_port=3888 \ + --server_port=2888 \ + --tick_time=2000 \ + --init_limit=10 \ + --sync_limit=5 \ + --heap=512M \ + --max_client_cnxns=60 \ + --snap_retain_count=3 \ + --purge_interval=12 \ + --max_session_timeout=40000 \ + --min_session_timeout=4000 \ + --log_level=INFO" + readinessProbe: + exec: + command: + - sh + - -c + - "zookeeper-ready 2181" + initialDelaySeconds: 10 + timeoutSeconds: 5 + livenessProbe: + exec: + command: + - sh + - -c + - "zookeeper-ready 2181" + initialDelaySeconds: 10 + timeoutSeconds: 5 + volumeMounts: + - name: datadir + mountPath: /var/lib/zookeeper + securityContext: + runAsUser: 1000 + fsGroup: 1000 + volumeClaimTemplates: + - metadata: + name: datadir + spec: + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: 10Gi